• socket网络编程快速上手(二)——细节问题(1)


    三、细节问题一个也不能少

      Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题。记得刚开始写网络代码的时候,那真是令人抓狂的经历,问题一个套一个,一会服务器起不来了,一会数据接收异常了,到最后自己都对那些系统调用都不放心了,怎么会要考虑那么多东西?起初,我是一万个怀疑,是不是自己人品出问题了,怎么别人没遇到,全给自己赶上了。后来,拿着《UNIX网络编程》随便看看,那书怎么会这么了解我的?细节!细节!细节!那些问题都被别人明明写出来了,自己又SX了。没办法,细节不注意,有苦说不出啊。

      不过也不能怪自己不爱学习啊,说实话那书实在太厚了,下面只记录一些自己遇到的、知道原因和解决方法的细节问题,还有很多后面慢慢学习吧!问题中使用的示例程序多少有点编造的意思,旨在说明问题。现实当中肯定是会发生的,概率也不能说低,我也没那么多闲时去统计具体数据。

    1. 端口复用

      此处描述的内容可能和端口复用的真实概念不符合,但我习惯用这种描述方法,理解下面内容即可。

      在调试网络程序的时候,TCP服务器经常起不来,总是在bind时出错。经验告诉我,此时换一个绑定端口往往就能起效,或者等个几分钟,服务器也能正常启动了。因为当时跑的都是些小的测试代码,换个端口还是很方便的。可这种问题要是放到一个系统或者一个服务器上,怎能接受?

      当时,自己摸索了几天都没什么结果,最后还是师傅威武,告诉我还有个端口复用的东西。现在整理,已经没了当时的环境,自己构造了这一问题。

      客户端程序为:

     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>
    ", argv[0]);
    22         exit(1);
    23     }
    24     
    25     if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
    26     {
    27         printf("socket()error
    ");
    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
    ");
    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
    ");
    47             exit(1);
    48         }
    49         buf[num - 1]='';
    50         printf("Server Message: %s
    ",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
    ");
    45         exit(1);
    46     }
    47     
    48     addrlen = sizeof(client);
    49     if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)) == -1) 
    50     {
    51         perror("accept()error
    ");
    52         exit(1);
    53     }
    54     printf("You got a connection from cient's ip is %s, prot is %d
    ", 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!
    ");
    64     sleep(10);
    65     
    66     close(connectfd);
    67     close(listenfd);
    68     
    69     return 0;
    70 }
    端口复用客户端

      客户端和服务器在两台电脑上运行,电脑通过交换机相连,当服务器将数据发送完成之后,sleep一段时间再关闭链接。根据程序,sleep的时候,手动断电关闭交换机,sleep完成,服务器关闭链路,进程退出。然后再重启服务器,服务器就起不来了,打印:

     

    使用netstat  –an查看端口使用情况:

     

    1234端口处于FIN_WAIT2状态,端口处于占用状态,所以bind返回失败了。解决办法:

    代码中增加下面语句即可

        int opt = SO_REUSEADDR;

        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    上述语句在TCP服务器代码中是必不可少的。

  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1092:求出e的值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1092:求出e的值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1091:求阶乘的和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1091:求阶乘的和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1090:含k个3的数
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1090:含k个3的数
    CodeForces 679B(Bear and Tower of Cubes)
    uva247 Calling Circles(传递闭包+DFS)
    uva673 Parentheses Balance(栈)
    uva439 Knight Moves(BFS求最短路)
  • 原文地址:https://www.cnblogs.com/wxyy/p/3308820.html
Copyright © 2020-2023  润新知