Social Icons

twitterfacebookgoogle plusrss feedemail

5/21/2013

檔案傳送與效能分析(3)-斷線分析

延續上一篇文章檔案傳送與效能分析(1)-實驗過程與結果
這篇要開始講解當程式發生問題時封包的狀態為何?以及一些斷線分析的範例程式碼。

斷線 RST封包問題

當伺服器 close一個連接時,若client端接著發數據。根據TCP協定的規定,會收到一個RST響應,client再往這個服務器發送數據時,系統會發出一個SIGPIPE信號給進程,告訴進程這個連接已經斷開了,不要再寫了。
    根據信號的默認處理規則SIGPIPE信號的默認執行動作是terminate(終止、退出),所以client會退出。若不想客戶端退出可以把SIGPIPE設為SIG_IGN

    如:    signal(SIGPIPE,SIG_IGN);//    這時SIGPIPE交給了系統處理。


 註:服務器採用了fork的話,要收集垃圾進程,防止僵屍進程的產生,可以這樣處理:  signal(SIGCHLD,SIG_IGN); //交給系統init去回收。
    這裡子進程就不會產生僵屍程序了。
這邊拿unpv13e 裡面的範例來修改當例子



#include    "unp.h"

#define MAXBUF 40960
void processSignal(int signo)
{
    printf("Signal is %d\n", signo);
    signal(signo, processSignal);
}
void
str_cli(FILE *fp, int sockfd)
{
    char    sendline[MAXBUF], recvline[MAXBUF];

    while (1) {

        memset(sendline, 'a', sizeof(sendline));
        printf("Begin send %d data\n", MAXBUF);
        Writen(sockfd, sendline, sizeof(sendline));
        sleep(5);

    }
}

int
main(int argc, char **argv)
{
    int                    sockfd;
    struct sockaddr_in    servaddr;

    signal(SIGPIPE, SIG_IGN);
    //signal(SIGPIPE, processSignal);

    if (argc != 2)
        err_quit("usage: tcpcli [port]");

    sockfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(atoi(argv[1]));
    Inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);

    Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

    str_cli(stdin, sockfd);        /* do it all */

    exit(0);
}

為了方便觀察錯誤輸出,lib/writen.c也做了修改:
/* include writen */
#include    "unp.h"

ssize_t                        /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
    size_t        nleft;
    ssize_t        nwritten;
    const char    *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        printf("Begin Writen %d\n", nleft);
        if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR) {
                printf("intterupt\n");
                nwritten = 0;        /* and call write() again */
            }
            else
                return(-1);            /* error */
        }

        nleft -= nwritten;
        ptr += nwritten;
        printf("Already write %d, left %d, errno=%d\n", nwritten, nleft, errno);
    }
    return(n);
}
/* end writen */

void
Writen(int fd, void *ptr, size_t nbytes)
{
    if (writen(fd, ptr, nbytes) != nbytes)
        err_sys("writen error");
}

client.c放在tcpclieserv目錄下,修改Makefile,增加了client.c的編譯目標
client: client.c
                ${CC} ${CFLAGS} -o $@ $< ${LIBS}


測試1: 忽略SIGPIPE信號,在write之前,對方關閉程式

本機服務端

nc -l -p 30000

本機客戶端:
./client 30000
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
執行到上步停止服務端,client會繼續顯示:
Begin send 40960 data
Begin Writen 40960
writen error: Broken pipe(32)

結論:可見write之前,對方socket中斷,發送端write會返回-1,errno號為EPIPE(32)


測試2: catch SIGPIPE信號,writen之前,對方關閉接受進程

修改客戶端代碼,catch sigpipe信號
//signal(SIGPIPE, SIG_IGN);

        signal(SIGPIPE, processSignal);

本機服務端

nc -l -p 30000

本機客戶端:
make client
./client 30000
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
Begin send 40960 data
Begin Writen 40960
Already write 40960, left 0, errno=0
執行到上步停止服務端,client會繼續顯示:
Begin send 40960 data
Begin Writen 40960
Signal is 13
writen error: Broken pipe(32)

結論:可見write之前,對方socket中斷,發送端write時,會先調用SIGPIPE響應函數,然後write返回-1,errno號為EPIPE(32)

測試3: writen過程中,對方關閉接受進程

為了方便操作,加大1次write的數據量,修改MAXBUF為4096000

本機服務端

nc -l -p 30000

本機客戶端:
make client
./client 30000
Begin send 4096000 data
Begin Writen 4096000
執行到上步停止服務端,client會繼續顯示:
Already write 589821, left 3506179, errno=0
Begin Writen 3506179
writen error: Connection reset by peer(104)

結論:可見socket write中,對方socket中斷,發送端write會先返回已經發送的字節數,再次write時返回-1,errno號為ECONNRESET(104)
為什麼以上測試,都是對方已經中斷socket後,發送端再次write,結果會有所不同呢。
從後來找到的書本中5.12,5.13能找到答案!!


The client's call to readline may happen before the server's RST is received by the client, or it may happen after. If the readline happens before the RST is received, as we've shown in our example, the result is an unexpected EOF in the client. But if the RST arrives first, the result is an ECONNRESET ("Connection reset by peer") error return from readline.
以上解釋了測試3的現象,write時,收到RST.


What happens if the client ignores the error return from readline and writes more data to the server? This can happen, for example, if the client needs to perform two writes to the server before reading anything back, with the first write eliciting the RST.
The rule that applies is: When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated.
If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE.


以上解釋了測試1,2的現象,write一個已經接受到RST的socket,系統內核會發送SIGPIPE給發送進程,如果進程catch/ignore這個信號,write都返回EPIPE錯誤.

因此,建議程式根據需要處理SIGPIPE信號做處理,盡量不要讓系統自動幫你把程式關掉,這樣你的應用就很難查處處理程式為什麼退出。



每一種信號都被OSKit給予了一個符號名,對於32位的i386平台而言,一個字32位,因而信號有32種。下面的表給出了常用的符號名、描述和它們的信號值。
更多的表格訊息請查看man page

符號名  信號值 描述                是否符合POSIX
SIGHUP  1   在控制終端上檢測到掛斷或控制線程死亡  是
SIGINT  2   交互注意信號              是
SIGQUIT  3   交互中止信號              是
SIGILL  4   檢測到非法硬件的指令          是
SIGTRAP  5   從陷阱中回朔              否
SIGABRT  6   異常終止信號              是
SIGEMT  7   EMT 指令                否
SIGFPE  8   不正確的算術操作信號          是
SIGKILL  9   終止信號                是
SIGBUS  10   總線錯誤                否
SIGSEGV  11   檢測到非法的內存調用          是
SIGSYS  12   系統call的錯誤參數           否
SIGPIPE  13   在無讀者的管道上寫           是
SIGALRM  14   報時信號                是
SIGTERM  15   終止信號                是
SIGURG  16   IO信道緊急信號             否
SIGSTOP  17   暫停信號                是
SIGTSTP  18   交互暫停信號              是
SIGCONT  19   如果暫停則繼續             是
SIGCHLD  20   子線程終止或暫停            是
SIGTTIN  21   後台線程組一成員試圖從控制終端上讀出  是
SIGTTOU  22   後台線程組的成員試圖寫到控制終端上   是
SIGIO   23   允許I/O信號               否
SIGXCPU  24   超出CPU時限               否
SIGXFSZ  25   超出文件大小限制            否
SIGVTALRM 26   虛時間警報器              否
SIGPROF  27   側面時間警報器             否
SIGWINCH 28   窗口大小的更改             否
SIGINFO  29   消息請求                否
SIGUSR1  30   保留作為用戶自定義的信號1        是
SIGUSR2  31   保留作為用戶自定義的信號        是


相關文章
檔案傳送與效能分析(1)-實驗過程與結果
檔案傳送與效能分析(2)-基本的檔案傳送
檔案傳送與效能分析(3)-斷線分析
檔案傳送與效能分析(4)-程式下載

沒有留言:

張貼留言

俗話說
凡走過必留下痕跡,凡住過必留下鄰居
凡爬過必留下樓梯,凡來過必留下IP
看過文章之後歡迎留下您寶貴的意見喔!

 
 
无觅相关文章插件,迅速提升网站流量