TCP之 select模型(转)

前记:select模型主要用于解决tcp通信中,每次处理一个独立的客户都要单独的开线程,这样会导致客户连接数很大时,线程数也会很多。而使用select就会将线程缩减至2个,一个主线程用于接收Accept客户端的连接,另外一个子线程用于监听已经连接上来的客户端是否有信号状态,如果有信号则进行相应的消息处理。

服务端:

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>//必须放在windows.h前面
#include <Windows.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")

fd_set g_fdClientSock;
int clientNum = 0;

DWORD WINAPI ListenThreadProc(LPARAM lparam)
{

  fd_set fdRead;
  FD_ZERO(&fdRead);
  int nRet = 0;
  char *recvBuffer = (char*)malloc(1024);
  if (!recvBuffer)
  {
    return -1;
  }
  memset(recvBuffer, 0, 1024);
  while (TRUE)
  {
    fdRead = g_fdClientSock;
    timeval vt;

    vt.tv_sec = 0;
    vt.tv_usec = 0;
    nRet = select(0, &fdRead, 0,0,&vt);//会阻塞检查集合中所有socket是否有信号
    if (nRet != SOCKET_ERROR)
    {
      for (int i=0; i<g_fdClientSock.fd_count; i++)
      {
        if (FD_ISSET(g_fdClientSock.fd_array[i], &fdRead))
        {
          memset(recvBuffer, 0, 1024);
          nRet = recv(g_fdClientSock.fd_array[i], recvBuffer, 1024, 0);
          if (nRet >0)
          {
            //todo:
            printf("接收到数据:%s", recvBuffer);
            send(g_fdClientSock.fd_array[i], recvBuffer, strlen(recvBuffer), 0);
          }
          else//如果接收失败,则从集合中清除响应socket,并把客户端数量减1
          {
            closesocket(g_fdClientSock.fd_array[i]);
            clientNum--;
            FD_CLR(g_fdClientSock.fd_array[i], &g_fdClientSock);
          }
        }
      }
    }
  }

  if (recvBuffer)
  {
    free(recvBuffer);
    recvBuffer=nullptr;
  }
  return 0;
}

void main()
{
  int port = 5099;
  WSADATA wsaData;
  if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    printf("Failed to load Winsock");
    return;
  }

  //创建用于监听的套接字 AF_INET:IPV4版本
  SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
  if (sockSrv == INVALID_SOCKET)
  {
    return;
  }

  //地址绑定-告诉操作系统是在哪一个地址及端口
  SOCKADDR_IN addrSrv;
  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(port); //1024以上的端口号,htons本地转换为网络数据
  addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//电脑上所有的网络ip

  int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
  if(retVal == SOCKET_ERROR)
  {
    printf("绑定bind失败:%d\n", WSAGetLastError());
    return;
  }

  if(listen(sockSrv,5/*SOMAXCONN*/) ==SOCKET_ERROR)
  {
    printf("监听listen失败:%d", WSAGetLastError());
    return;
  }

  SOCKADDR_IN addrClient;//用于获取连接上来的人的地址信息
  int len = sizeof(SOCKADDR);

  CreateThread(NULL,NULL, (LPTHREAD_START_ROUTINE)ListenThreadProc, NULL, NULL, NULL);

  while(clientNum < FD_SETSIZE)
  {
    //等待客户请求到来
    SOCKET clientSock = accept(sockSrv, (SOCKADDR *) &addrClient, &len);
    if(clientSock == SOCKET_ERROR)
    {
      printf("接收Accept失败:%d", WSAGetLastError());
      break;
    }
    else
    {
      printf("接收Accept到客户端IP:[%s]\n", inet_ntoa(addrClient.sin_addr));
    }
    FD_SET(clientSock, &g_fdClientSock);//添加到集合中去
    clientNum++;//每次接收到一个人就+1,目前最多接收64个客户
  }

  closesocket(sockSrv);
  WSACleanup();
  system("pause");
}
客户端:

// Tcp_client.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

void main()
{
  //加载套接字
  WSADATA wsaData;
  char buff[1024];
  memset(buff, 0, sizeof(buff));

  if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    printf("Failed to load Winsock");
    return;
  }

  SOCKADDR_IN addrSrv;
  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(5099);//http默认端口
  addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

  //创建套接字
  SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
  if(SOCKET_ERROR == sockClient){
  printf("Socket() error:%d", WSAGetLastError());
  return;
  }

  //向服务器发出连接请求
  if(connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){
  printf("Connect failed:%d", WSAGetLastError());
  return;
  }

  int iRecvLen = 0;

  //发送数据
  char* buffSend = "hello, this is a Client....";
  iRecvLen = send(sockClient, buffSend, strlen(buffSend), 0);


  //接收数据
  iRecvLen = recv(sockClient, buff, sizeof(buff), 0);
  printf("%s\n", buff);

  //关闭套接字
  closesocket(sockClient);
  WSACleanup();
  system("pause");
}

————————————————
版权声明:本文为CSDN博主「昵称都是浮云」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qingzai_/article/details/80868202

posted on 2020-02-18 12:04  书和田  阅读(354)  评论(0)    收藏  举报