背景:

  • 经常使用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);
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);

参考:

https://en.wikipedia.org/wiki/XFS

https://en.wikipedia.org/wiki/Ext4

scp源码浅析的更多相关文章

  1. 【深入浅出jQuery】源码浅析--整体架构

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  2. 【深入浅出jQuery】源码浅析2--奇技淫巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  3. Struts2源码浅析-ConfigurationProvider

    ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...

  4. (转)【深入浅出jQuery】源码浅析2--奇技淫巧

    [深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html

  5. HashSet其实就那么一回事儿之源码浅析

    上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...

  6. Android 手势识别类 ( 三 ) GestureDetector 源码浅析

    前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...

  7. Android开发之Theme、Style探索及源码浅析

    1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...

  8. 【深入浅出jQuery】源码浅析2--使用技巧

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...

  9. Android手势源码浅析-----手势绘制(GestureOverlayView)

    Android手势源码浅析-----手势绘制(GestureOverlayView)

随机推荐

  1. Oracle单行函数用法

    单行函数分为五种类型:字符函数.数值函数.日期函数.转换函数.通用函数. 1.字符函数: 对于输入的字符转换为需要转为的字符或数值. upper()大写 --小写字母转为大写字母 --对于表指定的字符 ...

  2. Sass 基础(八)

    @import Sass 支持所有css 的@规则,以及一些Sass 专属的规则,也被称为“指令(directive)”.这些规则在Sass 中具有不同的 功效,详细解释如下. @import Sas ...

  3. TCP三次挥手四次握手

    三次握手: 客户端发起: 1.向服务器端发送报文SYN=1,ACK=0;客户端进入SYN-SEND状态. 2.服务端收到SYN=1,ACK=0的请求报文,向客户端返回确认报文SYN=1,ACK=1,服 ...

  4. 爬虫——json模块与jsonpath模块

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写.同时也方便了机器进行解析和生成.适用于进行数据交互的场景,比如网站前台与后 ...

  5. cefsharp作为采集工具(学习笔记)

    cefsharp(webkit内核)浏览器替代webbrowser用来采集页面数据. 需要在页面form加载完毕,用异步方式自动获取sourcecode. 由于国内cefsharp的资料相对比较少,在 ...

  6. hibernate的CRUD操作

    一对多关系映射的crud操作: 1.单项的保存操作 /** * 保存操作 * 正常的保存:创建一个联系人,需要关联客户 */ @Test public void test1(){ Session s= ...

  7. vertical-align垂直居中

    <div id="content"> <div id="weizi"> 锄禾日当午,<br> 汗滴禾下土.<br> ...

  8. Hadoop(5)-Hive

    在Hadoop的存储处理方面提供了两种不同的机制,一种是之前介绍过的Hbase,另外一种就是Hive,有关于Hbase,它是一种nosql数据库的一种,是一种数据库,基于分布式的列式存储,适合海量数据 ...

  9. 2.3 进程控制之exec函数族

    学习目标:学习使用exec函数族的重要的几个函数  一.引言 进程通过exec函数根据指定的文件名或目录名执行另一个可执行文件,当进程调用exec函数时,该进程的数据段.代码段和堆栈段完全被新程序替换 ...

  10. flask 中访问时后台错误 error: [Errno 32] Broken pipe

    解决办法:app.run(threaded=True) 个人理解:flask默认单线程,访问一个页面时会访问到很多页面,比如一些图片,加入参数使其为多线程