scp源碼淺析


背景:

  • 經常使用scp傳文件,發現它真的很給力,好奇心由來已久!
  • 恰好接到一個移植SSH服務到專有網絡(非IP網絡)的小任務,完成工作又能滿足好奇心,何樂而不為!
  • 我只從源碼淺淺的分析一下,后續有更多想法再補充

 

源碼賞析:

1、所有的故事都從main開始,也從main結束。(main也很無辜,它只是打開了計算機的一扇窗):

  作為一個命令行工具,命令行是必須要處理的,這里scp也是采用常見的getopt來處理命令行。

1 while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1)

    上面的字符串就是可以使用的命令行選項,帶冒號的表示有參數,比如 d 表示可以在shell輸入 scp -d ...,l: 表示可以在shell輸入 scp -l 1000 ... ,當然這樣重點要提到 -r, 加上它就可以遞歸傳輸子目錄,非常實用,其他參數我就不再詳解了。

    接下來會看到如下代碼:

1 /* Command to be executed on remote system using "ssh". */
2     (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
3         verbose_mode ? " -v" : "",
4         iamrecursive ? " -r" : "", pflag ? " -p" : "",
5         targetshouldbedirectory ? " -d" : "");

  可以看到,注釋里提到了,這是要通過ssh到遠程linux系統(你的目的電腦)去執行的一條命令,同樣也是scp喔,所以這是scp神奇又方便的原因之一!

  它通過ssh連接到目的機器,同樣執行了一個scp程序來實現數據通路,完成數據接收或者發送。

注意:上面隱含了2個條件:

(1)你本機或者遠程機,兩者之間必須有一個ssh服務端

(2)兩者都必須有scp這個工具

 

2、文件的發送和接收

  之所以來看scp的源碼,也就是對它的文件讀寫傳輸很感興趣,首先看的是如何選擇合適大小的塊來讀寫,如下函數:

1 BUF * allocbuf(BUF *bp, int fd, int blksize)

  這個函數就會根據文件大小來分配一個合適的文件塊,來分塊讀寫,並網絡傳輸。

  函數的返回值是一個結構,用來存放即將分配的內存地址和內存大小。

1 typedef struct {
2     size_t cnt;
3     char *buf;
4 } BUF;

  而這個函數最核心的邏輯就是獲取文件的塊大小(基於文件系統I/O),並按照預定的塊大小來補齊,就像常見的64字節對齊一樣,如果你不是64字節的倍數,那就給你補齊。這里scp的預定塊大小的補齊是按16384字節來補齊的。

1 if (fstat(fd, &stb) < 0) {
2         run_err("fstat: %s", strerror(errno));
3         return (0);
4     }
5     size = roundup(stb.st_blksize, blksize);
1 #ifndef roundup
2 # define roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
3 #endif

  這里,roundup就是按16384的倍數向上取整了,比如XFS的塊大小是512bytes到64KB,EXT3,EXT4的塊大小是4KB。

  然后就是分配size大小的內存了。

1 if (bp->cnt >= size)
2         return (bp);
3     if (bp->buf == NULL)
4         bp->buf = xmalloc(size);
5     else
6         bp->buf = xreallocarray(bp->buf, 1, size);
7     memset(bp->buf, 0, size);
8     bp->cnt = size;

  然后就是逐塊的發送文件了。

 1 set_nonblock(remout);
 2         for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
 3             amt = bp->cnt;
 4             if (i + (off_t)amt > stb.st_size)
 5                 amt = stb.st_size - i;
 6             if (!haderr) {
 7                 if ((nr = atomicio(read, fd,
 8                     bp->buf, amt)) != amt) {
 9                     haderr = errno;
10                     memset(bp->buf + nr, 0, amt - nr);
11                 }
12             }
13             /* Keep writing after error to retain sync */
14             if (haderr) {
15                 (void)atomicio(vwrite, remout, bp->buf, amt);
16                 memset(bp->buf, 0, amt);
17                 continue;
18             }
19             if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
20                 &statbytes) != amt)
21                 haderr = errno;
22         }
23         unset_nonblock(remout);
View Code

  當然,如果設置了-r選項,就會遞歸處理子目錄以及子目錄的文件

 文件接收方會收到發送方發過來的整個文件大小,然后整個過程就跟發送有點類似了:

 1 set_nonblock(remin);
 2         for (count = i = 0; i < size; i += bp->cnt) {
 3             amt = bp->cnt;
 4             if (i + amt > size)
 5                 amt = size - i;
 6             count += amt;
 7             do {
 8                 j = atomicio6(read, remin, cp, amt,
 9                     scpio, &statbytes);
10                 if (j == 0) {
11                     run_err("%s", j != EPIPE ?
12                         strerror(errno) :
13                         "dropped connection");
14                     exit(1);
15                 }
16                 amt -= j;
17                 cp += j;
18             } while (amt > 0);
19 
20             if (count == bp->cnt) {
21                 /* Keep reading so we stay sync'd up. */
22                 if (wrerr == NO) {
23                     if (atomicio(vwrite, ofd, bp->buf,
24                         count) != count) {
25                         wrerr = YES;
26                         wrerrno = errno;
27                     }
28                 }
29                 count = 0;
30                 cp = bp->buf;
31             }
32         }
33         unset_nonblock(remin);
View Code

 

參考:

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM