zoukankan      html  css  js  c++  java
  • 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服务器代码中是必不可少的。

  • 相关阅读:
    codevs 2632 非常好友
    codevs 1213 解的个数
    codevs 2751 军训分批
    codevs 1519 过路费
    codevs 1503 愚蠢的宠物
    codevs 2639 约会计划
    codevs 3369 膜拜
    codevs 3135 River Hopscotch
    数论模板
    JXOJ 9.7 NOIP 放松模拟赛 总结
  • 原文地址:https://www.cnblogs.com/wxyy/p/3308820.html
Copyright © 2011-2022 走看看