c语⾔编程实现scp功能,scp源码浅析
背景:
经常使⽤scp传⽂件,发现它真的很给⼒,好奇⼼由来已久!
恰好接到⼀个移植SSH服务到专有⽹络(⾮IP⽹络)的⼩任务,完成⼯作⼜能满⾜好奇⼼,何乐⽽不为!
我只从源码浅浅的分析⼀下,后续有更多想法再补充
源码赏析:
1、所有的故事都从main开始,也从main结束。(main也很⽆辜,它只是打开了计算机的⼀扇窗):
作为⼀个命令⾏⼯具,命令⾏是必须要处理的,这⾥scp也是采⽤常见的getopt来处理命令⾏。
while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -)
上⾯的字符串就是可以使⽤的命令⾏选项,带冒号的表⽰有参数,⽐如 d 表⽰可以在shell输⼊ scp -d ...,l: 表⽰可以在shell输⼊ scp -l 1000 ... ,当然这样重点要提到 -r, 加上它就可以递归传输⼦⽬录,⾮常实⽤,其他参数我就不再详解了。
接下来会看到如下代码:
/* Command to be executed on remote system using "ssh". */
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
verbose_mode ? " -v" : "",
iamrecursive ? " -r" : "", pflag ? " -p" : "",
targetshouldbedirectory ? " -d" : "");
可以看到,注释⾥提到了,这是要通过ssh到远程linux系统(你的⽬的电脑)去执⾏的⼀条命令,同样也是scp喔,所以这是scp神奇⼜⽅便的原因之⼀!
它通过ssh连接到⽬的机器,同样执⾏了⼀个scp程序来实现数据通路,完成数据接收或者发送。
注意:上⾯隐含了2个条件:
(1)你本机或者远程机,两者之间必须有⼀个ssh服务端
(2)两者都必须有scp这个⼯具
2、⽂件的发送和接收
之所以来看scp的源码,也就是对它的⽂件读写传输很感兴趣,⾸先看的是如何选择合适⼤⼩的块来读写,如下函数:
BUF * allocbuf(BUF *bp, int fd, int blksize)
这个函数就会根据⽂件⼤⼩来分配⼀个合适的⽂件块,来分块读写,并⽹络传输。
函数的返回值是⼀个结构,⽤来存放即将分配的内存地址和内存⼤⼩。
typedef struct {
size_t cnt;
char *buf;
} BUF;
⽽这个函数最核⼼的逻辑就是获取⽂件的块⼤⼩(基于⽂件系统I/O),并按照预定的块⼤⼩来补齐,就像常见的64字节对齐⼀样,如果你不是64字节的倍数,那就给你补齐。这⾥scp的预定块⼤⼩的补齐
是按16384字节来补齐的。
if (fstat(fd, &stb) < ) {
run_err("fstat: %s", strerror(errno));
return ();
}
size = roundup(stb.st_blksize, blksize);
#ifndef roundup
# define roundup(x, y) ((((x)+((y)-))/(y))*(y))
#endif
这⾥,roundup就是按16384的倍数向上取整了,⽐如XFS的块⼤⼩是512bytes到64KB,EXT3,EXT4的块⼤⼩是4KB。然后就是分配size⼤⼩的内存了。
if (bp->cnt >= size)
return (bp);
if (bp->buf == NULL)
bp->buf = xmalloc(size);
else
bp->buf = xreallocarray(bp->buf, , size);
memset(bp->buf, , size);
bp->cnt = size;
然后就是逐块的发送⽂件了。
set_nonblock(remout);
for (haderr = i = ; i < stb.st_size; i += bp->cnt) {
amt = bp->cnt;
if (i + (off_t)amt > stb.st_size)
amt = stb.st_size - i;
if (!haderr) {
if ((nr = atomicio(read, fd,
bp->buf, amt)) != amt) {
haderr = errno;
memset(bp->buf + nr, , amt - nr);
}
}
/* Keep writing after error to retain sync */
if (haderr) {
(void)atomicio(vwrite, remout, bp->buf, amt);jquery源码在线
memset(bp->buf, , amt);
continue;
}
if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
&statbytes) != amt)
haderr = errno;
}
unset_nonblock(remout);
当然,如果设置了-r选项,就会递归处理⼦⽬录以及⼦⽬录的⽂件
⽂件接收⽅会收到发送⽅发过来的整个⽂件⼤⼩,然后整个过程就跟发送有点类似了:set_nonblock(remin);
for (count = i = ; i < size; i += bp->cnt) {
amt = bp->cnt;
if (i + amt > size)
amt = size - i;
count += amt;
do {
j = atomicio6(read, remin, cp, amt,
scpio, &statbytes);
if (j == ) {
run_err("%s", j != EPIPE ?
strerror(errno) :
"dropped connection");
exit();
}
amt -= j;
cp += j;
} while (amt > );
if (count == bp->cnt) {
/* Keep reading so we stay sync'd up. */
if (wrerr == NO) {
if (atomicio(vwrite, ofd, bp->buf,
count) != count) {
wrerr = YES;
wrerrno = errno;
}
}
count = ;
cp = bp->buf;
}
}
unset_nonblock(remin);
参考:
【深⼊浅出jQuery】源码浅析--整体架构
最近⼀直在研读 jQuery 源码,初看源码⼀头雾⽔毫⽆头绪,真正静下⼼来细看写的真是精妙,让你感叹代码之美. 其结构明晰,⾼内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
【深⼊浅出jQuery】源码浅析2--奇技淫巧
最近⼀直在研读 jQuery 源码,初看源码⼀头雾⽔毫⽆头绪,真正静下⼼来细看写的真是精妙,让你感叹代码之美. 其结构明晰,⾼内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
Struts2源码浅析-ConfigurationProvider
ConfigurationProvider接⼝ 主要完成struts配置⽂件 加载 注册过程 ConfigurationProvider接⼝定义 public interface Configurat ...
(转)【深⼊浅出jQuery】源码浅析2--奇技淫巧
HashSet其实就那么⼀回事⼉之源码浅析
上篇⽂章介绍了hashMap,  本次将带⼤家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...
Android ⼿势识别类 ( 三 ) GestureDetector 源码浅析
前⾔:上 篇介绍了提供⼿势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的⼿势,是需要存储以及在必要的利⽤时加载取出⼿势.所 以,⽤户绘制出的⼀个完整的⼿势是需要⼀定的代码 ...
Android开发之Theme、Style探索及源码浅析
1 背景 前段时间⾥有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在⽹上随便⼀搜⼀⼤把模板,所以关于怎么⽤的问题我想这⾥也就不做太多的说明了,我们这⾥把重点放在理解 ...
【深⼊浅出jQuery】源码浅析2--使⽤技巧
最近⼀直在研读 jQuery 源码,初看源码⼀头雾⽔毫⽆头绪,真正静下⼼来细看写的真是精妙,让你感叹代码之美. 其结构明晰,⾼内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
随机推荐
使⽤html2canvas实现批量⽣成条形码
/*前台代码*/
Mac OS下配置Eclipse C+&plus;的⽅法
maven常见问题问答
1.前⾔ Maven,发⾳是[`meivin],"专家"的意思.它是⼀个很好的项⽬管理⼯具,很早就进⼊了我的必备⼯具⾏列,但是这次为了把project1项⽬完全迁移并应⽤maven ...
POJ3253Babelfish
MemCacheManager
#region Fields private AreaRepository _areaRepository = new AreaRepository(); private ICacheManager ...
angular的post请求,SpringMVC后台接收不到参数值的解决⽅案
这是我后台SpringMVC控制器接收isform参数的⽅法,只是简单的打出它的值: @RequestMapping(method = RequestMethod.POST) @ResponseBod ...
sizeof⽤法研究
⼀.基础研究 写⼀个c程序,打印int.long.double型变量所占的字节数.地址.各个字节的地址和内容.打印地址和内容⽐较好办,打印地址可以⽤取址符&,打印内容直接输出就⾏了,那么怎么打 ...
ef添加字段
先在实体类⾥添加字段 ,然后执⾏ Add-Migration updateNumberOfLikes Update-Database -Verbose
左右margin top问题百分⽐值
不想说今天⼼情有多差! 9点多開始发现⼀个"bug",需求是依据屏幕⾼度动态调整某个div的位置.代码⼤概是这种.