在一個 CLIENT/SERVER模型的網絡應用中,客戶端的調用序列大致如下:
socket -> connect -> recv/send -> close
其中socket沒有什么可疑問的,主要是創建一個套接字用於與服務端交換數據,並且通常它會迅速返回,此時並沒有數據通過網卡發送出去,而緊隨其后的connect函數則會產生網絡數據的發送,TCP的三次握手也正是在此時開始,connect會先發送一個SYN包給服務端,並從最初始的CLOSED狀態進入到SYN_SENT狀態,在此狀態等待服務端的確認包,通常情況下這個確認包會很快到達,以致於我們根本無法使用netstat命令看到SYN_SENT狀態的存在,不過我們可以做一個極端情況的模擬,讓客戶端去連接一個隨意指定服務器(如IP地址為88.88.88.88),因為該服務器很明顯不會反饋給我們SYN包的確認包(SYN ACK),客戶端就會在一定時間內處於SYN_SENT狀態,並在預定的超時時間(比如3分鍾)之后從connect函數返回,connect調用一旦失敗(沒能到達ESTABLISHED狀態)這個套接字便不可用,若要再次調用connect函數則必須要重新使用socket函數創建新的套接字。
下面結合實例分析,客戶端代碼如下:
- /**
- * client.c
- *
- * TCP client program, it is a simple example only.
- * Writen By: Zhou Jianchun
- * Date: 2011.08.11
- *
- * Compiled With: gcc -o client client.c
- * Tested On: Ubuntu 11.04 LTS
- * gcc version: 4.5.2
- *
- */
- #include <stdio.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #define SERVER_PORT 20000
- void usage(char *name)
- {
- printf("usage: %s IP\n", name);
- }
- int main(int argc, char **argv)
- {
- int server_fd, client_fd, length = 0;
- struct sockaddr_in server_addr, client_addr;
- socklen_t socklen = sizeof(server_addr);
- if(argc < 2)
- {
- usage(argv[0]);
- exit(1);
- }
- if((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- {
- printf("create socket error, exit!\n");
- exit(1);
- }
- srand(time(NULL));
- bzero(&client_addr, sizeof(client_addr));
- client_addr.sin_family = AF_INET;
- client_addr.sin_addr.s_addr = htons(INADDR_ANY);
- bzero(&server_addr, sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- inet_aton(argv[1], &server_addr.sin_addr);
- server_addr.sin_port = htons(SERVER_PORT);
- if(connect(client_fd, (struct sockaddr*)&server_addr, socklen) < 0)
- {
- printf("can not connect to %s, exit!\n", argv[1]);
- printf("%s\n", strerror(errno));
- exit(1);
- }
- return 0;
- }
編譯完成之后執行:
- zhou@neptune:~/data/source$ ./client 88.88.88.88
此時程序會在connect函數中阻塞等待,約180秒之后輸出:
- can not connect to 88.88.88.88, exit!
- Connection timed out
此刻connect的返回值為ETIMEOUT。
在此過程中我們可以用netstat命令查詢連接狀態:
- zhou@neptune:~/data/source$ sudo netstat -natp |grep 20000
- tcp 0 1 192.168.0.4:44203 88.88.88.88:20000 SYN_SENT 5954/client
可以看到此時的TCP連接狀態為SYN_SENT,也就意味着發送了SYN包之后一直未得到服務端回饋SYN ACK包。
接下來我們使用這個客戶端程序來連接自己的機器,測試時我的IP地址是192.168.0.4,是一個無線局域網,結果如下:
- zhou@neptune:~/data/source$ ./client 192.168.0.4
- can not connect to 192.168.0.4, exit!
- Connection refused
因為我的機器上並沒有跑在指定端口(20000)上監聽的服務端程序,所以這個連接直接被協議棧拒絕(通過發送RST類型的TCP包),connect立刻返回,返回值為ECONNREFUSED。
再來看看去連接同一局域網中一台不存在的主機時的情形,比如這台想象的主機的IP地址為192.168.0.188:
- zhou@neptune:~/data/source$ ./client 192.168.0.188
- can not connect to 192.168.0.188, exit!
- No route to host
因為本地局域網中的該主機並不存在,ARP請求得不到回應,網關會回應主機不可達的ICMP報文,connect返回EHOSTUNREACH。
至此connect函數的分析就結束了,由於本人水平有限,博客中的不妥或錯誤之處在所難免,殷切希望讀者批評指正。同時也歡迎讀者共同探討相關的內容,如果樂意交流的話請留下您寶貴的意見,謝謝。
原博文地址:http://blog.csdn.net/polarbearboy/article/details/6678838