C++ TCP socket编程中的小陷阱(服务端accept 不阻塞 和 客户端connect 重连失败)

在编写一个使用C++ socket实现的TCP服务端与客户端小软件时接连碰上2个小陷阱,

终归是实践不足,基本功不扎实。

 

第1个问题: 服务端的accept函数没有阻塞

    程序运行到accept这里时直接就跳了过去,根本没停下来。

    怀疑过socket的配置是否有错误,经过各种调试,当把socket部分的代码从工程中截取出,单独放到一个空白工程中运行时,一切又都正常了。

    证实了socket的配置没有问题,只能是原工程中的哪个头文件包含后把socket的函数覆盖了。

    又经过了几次试验,最终定位到了罪魁祸首竟然是using namespace std

    网上查了一下才知道原来这也是个常见问题。因为std的命名空间里包含了太多常用的函数名,当全局使用std命名空间时,很容易发生函数运行没有得到预期的效果。

    在socket的这个问题中,重要的bind函数被std中的同名函数覆盖,使得服务端的socket没有绑定ip地址和商品,自然之后的accept函数也就不能正常阻塞。

     对策有2种,

     1是把bind函数改为全局调用方式  ::bind(), 摆脱std命名空间的影响。 

     2是放弃std命名空间在全局范围内的声明,改用指定使用函数的方式。 如 using std::cout 或using std::endl

 

第2个问题:客户端socket断开后重连失败

    这是在测试的时候发现的问题。这次的小软件设计是客户端在与服务端保持TCP连接的过程中可以随意向服务端发送命令。操作结束后可以暂时断开TCP连接,当有需求时再次连接。

    因此客户端的处理框架是循环等待用户输入,再根据内容来判断需要向服务端发起什么样的操作请求。

    

  WORD wVersionRequested;
  WSADATA wsaData;
  int err;
  SOCKET sockClient;

  wVersionRequested = MAKEWORD(1, 1);

  err = WSAStartup(wVersionRequested, &wsaData);
  if (err != 0) {
    return;
  }

  if (LOBYTE(wsaData.wVersion) != 1 ||
    HIBYTE(wsaData.wVersion) != 1) {
    WSACleanup();
    return;
  }
  sockClient = socket(AF_INET, SOCK_STREAM, 0);

  SOCKADDR_IN addrSrv;

  inet_pton(AF_INET, "127.0.0.1", &addrSrv.sin_addr.s_addr);

  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(6000);


  while (1)
  {
    char in_buf[40];
    string input_command;

    cin >> in_buf;

    input_command = in_buf;

    if (input_command.compare("TCP connect") == 0)
    {
      connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
      char recvBuf[500]; recv(sockClient, recvBuf, 500, 0);
      printf("%s\n", recvBuf);

    }
    else if (input_command.compare("operation") == 0)
    {
      char requst_buf[40] = "operation";
      send(sockClient, requst_buf, strlen(requst_buf) + 1, 0);

      char recvBuf[500]; recv(sockClient, recvBuf, 500, 0);
      printf("%s\n", recvBuf);
    }

    else if (input_command.compare("disconnect") == 0)
    {
      char requst_buf[40] = "disconnect";
      send(sockClient, requst_buf, strlen(requst_buf) + 1, 0);

      char recvBuf[500]; recv(sockClient, recvBuf, 500, 0);
      printf("%s\n", recvBuf);

      closesocket(sockClient);
    }
}

这样的客户端代码在启动后第一次连接服务端时一切都很正常,但在断开后做再次连接时发生了异常,服务端并没有收到再连接请求,一直停留在accept阻塞的位置。而客户端却能收到socket的信息反馈,但进一步确认后发现也只能收到返回消息,需要服务端处理的操作就无法得到服务端的响应。

这个现象就像是客户端在做二次连接时,连接请求并没有送到服务端,而只是把客户端可能会收到的返回消息作为缓存放在socket中,给客户端造成一种已经再次连接上的假象。

 

又经过一阵网络搜索,原来这也是一种常见现象。已经连接过的socket资源不能直接用于再次连接,要么对它做一次初始化,要么再次生成一个新的socket资源。作为修改,我选择了后者。

    if (input_command.compare("TCP connect") == 0)
    {
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD(1, 1);

      err = WSAStartup(wVersionRequested, &wsaData);
      if (err != 0) {
        return;
      }

      if (LOBYTE(wsaData.wVersion) != 1 ||
        HIBYTE(wsaData.wVersion) != 1) {
        WSACleanup();
        return;
      }

      SOCKET sockClient;
      sockClient = socket(AF_INET, SOCK_STREAM, 0);

      SOCKADDR_IN addrSrv;

      inet_pton(AF_INET, "127.0.0.1", &addrSrv.sin_addr.s_addr);

      addrSrv.sin_family = AF_INET;
      addrSrv.sin_port = htons(6000);

      connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
      char recvBuf[500]; recv(sockClient, recvBuf, 500, 0);
      printf("%s\n", recvBuf);

    }

把socket的建立代码移到发起TCP连接时才运行,保证每次发起连接请求用的都是新的socket,问题得到解决。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值