今天看linux C 編程實戰的my_server例子時,敲到這段代碼,對其父子進程關閉socket 進行close調用產生疑問
如圖中標注的三個close socket,思考子進程通信結束 關閉自己的通信socket:conn_fd可以理解,但將sock_fd,服務器的監聽socket也關閉就不知怎么回事了。而且父進程執行close(conn_fd)將子進程的通信socket關閉,這樣不久讓子進程無法和客戶端soket通信了嗎? 但是程序正常運行。重新查資料發現是自己對子進程資源的復制和close函數,還有文件描述符的理解不到位
1. 首先,socket是一種特殊的數據傳輸IO,也是一種文件描述符。
2. fork創建進程時,子進程共享父進程打開的文件描述符,但父進程對文件描述符的改變不會影響子進程中的文件描述符。
3. close 一個套接字的默認行為是把套接字標記為已關閉,然后立即返回到調用進程,該套接字描述符不能再由調用進程使用,也就是說它不能再作為read或write的第一個參數,然而TCP將嘗試發送已排隊等待發送到對端的任何數據,發送完畢后發生的是正常的TCP連接終止序列。
在多進程并發服務器中,父子進程共享著套接字,套接字描述符引用計數記錄著共享著的進程個數,當父進程或某一子進程close掉套接字時,描述符引用計數會相應的減一,當引用計數仍大于零時,這個close調用就不會引發TCP的四路握手斷連過程。
所以:父進程close(conn_fd)不會對子進程通信造成影響。相反,如果不這么做,在父進程中conn_fd占用著可用的文件描述符,會影響父進程accept鏈接的個數,因為conn_fd是有用戶級限制的(內核為了不讓某一個進程消耗掉所有的文件資源,其也會對單個進程最大打開文件數做默認值處理,默認值一般是1024,使用ulimit -n命令可以查看),再accept一個新的連接時,前一個conn_fd被覆蓋,就找不到了,但相應的描述符值還是被標記為“占用”,當達到用戶級限制時,不可再accept新連接,成為瓶頸。
在Web服務器中,通過更改系統默認值文件描述符的最大值來優化服務器是最常見的方式之一。具體優化方式請查看http://blog.csdn.net/kumu_linux/article/details/7877770其中介紹的很詳細
經本人親測,如果去除了父進程的close(conn_fd),可以accept的連接數大大減少。而且通過修改系統文件描述符最大值,可以大大提高accept連接數量。
初學Linux 對其中很多細節還不清楚,后續會針對修改!
文件描述符講解很好的博客:http://blog.csdn.net/cywosp/article/details/38965239
while(1) { conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len); if (conn_fd < 0) { my_err("accept", __LINE__); } PRintf("accept a new client, ip:%s/n", inet_ntoa(cli_addr.sin_addr)); if ((pid = fork()) == 0) { while(1) { if ((ret = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) { perror("recv"); exit(1); } recv_buf[ret-1] = '/0'; if (flag_recv == USERNAME) { name_num = find_name(recv_buf); switch(name_num) { case -1: send_data(conn_fd, "n/n"); break; case -2: exit(1); break; default: send_data(conn_fd, "y/n"); flag_recv = PASSWord; break; } } else if (flag_recv == PASSWORD) { if (strcmp(users[name_num].password, recv_buf) == 0) { send_data(conn_fd, "y/n"); send_data(conn_fd, "welcome login my tcp server/n"); printf("%s login/n", users[name_num].username); flag_recv = MESSAGE; } else { send_data(conn_fd, "n/n"); } } else if (flag_recv == MESSAGE) { if (strcmp("quit", recv_buf) == 0) { break; } printf("message from %s: %s/n", users[name_num].username, recv_buf); } } close(sock_fd); close(conn_fd); printf("child exit!/n"); exit(0); }else { close(conn_fd); }新聞熱點
疑難解答