socket網絡編程快速上手(二)——細節問題(1)

3、細節問題一個也不能少

  Socket編程說簡單也簡單,程序很容易就能跑起來,說麻煩還真是麻煩,程序動不動就出問題。記得剛開始寫網絡代碼的時候,那真是使人抓狂的經歷,問題一個套一個,一會服務器起不來了,一會數據接收異常了,到最後本身都對那些系統調用都不放心了,怎麼會要考慮那麼多東西?起初,我是一萬個懷疑,是否是本身人品出問題了,怎麼別人沒遇到,全給本身遇上了。後來,拿着《UNIX網絡編程》隨便看看,那書怎麼會這麼瞭解個人?細節!細節!細節!那些問題都被別人明明寫出來了,本身又SX了。沒辦法,細節不注意,有苦說不出啊。編程

  不過也不能怪本身不愛學習啊,說實話那書實在太厚了,下面只記錄一些本身遇到的、知道緣由和解決方法的細節問題,還有不少後面慢慢學習吧!問題中使用的示例程序多少有點編造的意思,旨在說明問題。現實當中確定是會發生的,機率也不能說低,我也沒那麼多閒時去統計具體數據。服務器

 

  1. 端口複用

  此處描述的內容可能和端口複用的真實概念不符合,但我習慣用這種描述方法,理解下面內容便可。網絡

  在調試網絡程序的時候,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服務器代碼中是必不可少的。

相關文章
相關標籤/搜索