Socket編程說簡單也簡單,程序很容易就能跑起來,說麻煩還真是麻煩,程序動不動就出問題。記得剛開始寫網絡代碼的時候,那真是使人抓狂的經歷,問題一個套一個,一會服務器起不來了,一會數據接收異常了,到最後本身都對那些系統調用都不放心了,怎麼會要考慮那麼多東西?起初,我是一萬個懷疑,是否是本身人品出問題了,怎麼別人沒遇到,全給本身遇上了。後來,拿着《UNIX網絡編程》隨便看看,那書怎麼會這麼瞭解個人?細節!細節!細節!那些問題都被別人明明寫出來了,本身又SX了。沒辦法,細節不注意,有苦說不出啊。編程
不過也不能怪本身不愛學習啊,說實話那書實在太厚了,下面只記錄一些本身遇到的、知道緣由和解決方法的細節問題,還有不少後面慢慢學習吧!問題中使用的示例程序多少有點編造的意思,旨在說明問題。現實當中確定是會發生的,機率也不能說低,我也沒那麼多閒時去統計具體數據。服務器
此處描述的內容可能和端口複用的真實概念不符合,但我習慣用這種描述方法,理解下面內容便可。網絡
在調試網絡程序的時候,TCP服務器常常起不來,老是在bind時出錯。經驗告訴我,此時換一個綁定端口每每就能起效,或者等個幾分鐘,服務器也能正常啓動了。由於當時跑的都是些小的測試代碼,換個端口仍是很方便的。可這種問題要是放到一個系統或者一個服務器上,怎能接受?socket
當時,本身摸索了幾天都沒什麼結果,最後仍是師傅威武,告訴我還有個端口複用的東西。如今整理,已經沒了當時的環境,本身構造了這一問題。ide
客戶端程序爲:學習
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <netdb.h> 9 10 #define PORT 1234 11 #define MAXDATASIZE 1000 12 13 int main(int argc, char *argv[]) 14 { 15 int sockfd, num; 16 char buf[MAXDATASIZE + 1] = {0}; 17 struct sockaddr_in server; 18 19 if (argc != 2) 20 { 21 printf("Usage:%s <IP Address>\n", argv[0]); 22 exit(1); 23 } 24 25 if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1) 26 { 27 printf("socket()error\n"); 28 exit(1); 29 } 30 bzero(&server, sizeof(server)); 31 server.sin_family = AF_INET; 32 server.sin_port = htons(PORT); 33 server.sin_addr.s_addr = inet_addr(argv[1]); 34 if (connect(sockfd, (struct sockaddr *)&server, \ 35 sizeof(server)) == -1) 36 { 37 printf("connect()error\n"); 38 exit(1); 39 } 40 41 while (1) 42 { 43 memset(buf, 0, sizeof(buf)); 44 if ((num = recv(sockfd, buf, MAXDATASIZE,0)) == -1) 45 { 46 printf("recv() error\n"); 47 exit(1); 48 } 49 buf[num - 1]='\0'; 50 printf("Server Message: %s\n",buf); 51 } 52 53 close(sockfd); 54 55 return 0; 56 }
服務器程序爲:測試
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include <signal.h> 10 11 #define PORT 1234 12 #define BACKLOG 5 13 #define MAXDATASIZE 1000 14 15 int main() 16 { 17 int listenfd, connectfd; 18 struct sockaddr_in server; 19 struct sockaddr_in client; 20 socklen_t addrlen; 21 char szbuf[MAXDATASIZE] = {0}; 22 int iCount = 0; 23 24 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 25 { 26 perror("Creating socket failed."); 27 exit(1); 28 } 29 30 int opt = SO_REUSEADDR; 31 // setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 32 33 bzero(&server, sizeof(server)); 34 server.sin_family = AF_INET; 35 server.sin_port = htons(PORT); 36 server.sin_addr.s_addr = htonl(INADDR_ANY); 37 if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1) 38 { 39 perror("Bind()error."); 40 exit(1); 41 } 42 if (listen(listenfd, BACKLOG) == -1) 43 { 44 perror("listen()error\n"); 45 exit(1); 46 } 47 48 addrlen = sizeof(client); 49 if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)) == -1) 50 { 51 perror("accept()error\n"); 52 exit(1); 53 } 54 printf("You got a connection from cient's ip is %s, prot is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port)); 55 56 memset(szbuf, 'a', sizeof(szbuf)); 57 while (iCount < 100) 58 { 59 send(connectfd, szbuf, sizeof(szbuf), 0); 60 iCount++; 61 } 62 63 printf("send over!\n"); 64 sleep(10); 65 66 close(connectfd); 67 close(listenfd); 68 69 return 0; 70 }
客戶端和服務器在兩臺電腦上運行,電腦經過交換機相連,當服務器將數據發送完成以後,sleep一段時間再關閉連接。根據程序,sleep的時候,手動斷電關閉交換機,sleep完成,服務器關閉鏈路,進程退出。而後再重啓服務器,服務器就起不來了,打印:spa
使用netstat –an查看端口使用狀況:調試
1234端口處於FIN_WAIT2狀態,端口處於佔用狀態,因此bind返回失敗了。解決辦法:code
代碼中增長下面語句便可
int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
上述語句在TCP服務器代碼中是必不可少的。