ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

scp代码逻辑

2021-07-19 19:03:04  阅读:328  来源: 互联网

标签:逻辑 代码 amt 源码 bp ssh scp size


https://blog.csdn.net/an_zhenwei/article/details/7951527

 

scp.c中main

 

基本流程

1、解析参数;

2、根据"最后的参数"看是本地copy,还是远程copy。

       分别指向toremote和tolocal函数。

3、toremote()分三种:(和下面分析的流程类似,请参考最下面的彩色字体描述)

       1)远端到远端,通过本地。(分解为两步:远端到本地,本地到远端)

       2)远端到远端,不通过本地

       3)本地到远端


4、tolocal()分为

       1)本地到本地

                      执行cp命令

       2)远端到本地

             此情况的详解如下:

“本地scp进程 <-----pipe----> 本地ssh子进程 < -------socket------>  server端sshd进程 <-------pipe--------> server端scp进程”

蓝色为本地操作;紫色为对端操作。

大量使用了下面这连个机制:

              使用“管道”作为“父子”进程之间的通信机制。

              使用dup2将“管道”和“标准输入输出口”绑定。


发送时,在scp进程中,子进程为ssh。

                      1、本地启用子进程,执行ssh命令,作为ssh client端,连接远端ssh server。(在ssh子进程中,使用dup2将“标准输入输出”和“管道”绑定;此时scp进程监听管道另一端)。

                      2、ssh client通过SSH_CMSG_EXEC_CMD消息将“scp -f -- 文件名”命令发送给ssh server。


接收时,在sshd子进程中,创建scp孙进程。

ssh server进程隐藏的流程

                     1)解析参数;

                      2)主进程作为守护进程daemon,一直在server_accept_loop中循环监听端口;

                      3)监听到一个ssh client连接,则创建一个sshd子进程,监听socket。

                      3、sshd子进程收到scp命令后,启用scp孙进程执行此命令。  (在scp孙进程中,使用dup2将“标准输入输出”和“管道”绑定;此时sshd子进程监听管道另一端)

                      4、scp孙进程发现-f参数,知道自己是源端,执行source()函数,直接写入“标准输出”,即管道,被sshd子进程接收到。

                             因为此sshd子进程和ssh client是一一对应的,sshd子进程收到数据后,缓存到stdout_buffer,然后组成SSH_SMSG_STDOUT_DATA报文,进而发送到ssh client端;

                      5、 ssh client收到后,也存入stdout_buffer,然后打印到“标准输出”。

                      6、 因为第1步的原因,本地scp进程收到内容。

 

##############################################################################################################

##############################################################################################################

 

https://blog.csdn.net/weixin_39671405/article/details/117104582

 

背景:

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

参考:

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

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

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

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

Struts2源码浅析-ConfigurationProvider

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

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

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

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

上篇文章介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...

Android 手势识别类 &lpar; 三 &rpar; GestureDetector 源码浅析

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

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

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

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

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

随机推荐

使用html2canvas实现批量生成条形码

/*前台代码*/

Mac OS下配置Eclipse C&plus;&plus;的方法

http://nonlz.blog.163.com/blog/static/128872032201262622921622/

maven常见问题问答

1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目完全迁移并应用maven ...

POJ3253Babelfish

http://poj.org/problem?id=3253 就是一个哈夫曼树,题目下边有提示,所以题意还是好理解的 #include #include

MemCacheManager

#region Fields private AreaRepository _areaRepository = new AreaRepository(); private ICacheManager ...

angular的post请求&comma;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的位置.代码大概是这种.

 

标签:逻辑,代码,amt,源码,bp,ssh,scp,size
来源: https://www.cnblogs.com/ztguang/p/15031747.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有