欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创][开源]通信服务器+客户机的四种模型

[原创][开源]通信服务器+客户机的四种模型

理论知识我也不说太多了,网络上一百度大把,但可能就是贴部分代码并没开源,所以你拿过去可能就用不了,而我这个就不同了开源分享
运行环境:VS2012  win10 X64
大致步骤为:
包含必要的头文件及库


  • 指定需要的SOCKET最高版本
  • 创建套接字
  • 绑定IP及端口
  • 监听
  • 连接
  • 接收数据
  • 发送数据
  • 关闭套接字

以下是核心代码
TCP通信
客户机:

int nErrCode=0;

  //初始化socket

  InitWinSock();

  //创建套接字

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

  sockaddr_in addr;

  addr.sin_family=AF_INET;

  addr.sin_port=htons(1234);

  addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

  //连接服务器

  nErrCode=connect(sock,

          (sockaddr*)&addr,//ip地址

          sizeof(sockaddr_in));//ip 地址的大小

  if (SOCKET_ERROR==nErrCode)

  {

    printf("查看是否已启动服务端!\n");

    closesocket(sock);

    WSACleanup();

    system("pause");

    return FALSE;

  }

  char InitName[20]={0};

  InitName[0]='@';

  printf("起个名字吧");

  scanf_s("%s",InitName+1,19);

  //发送消息

  nErrCode=send(sock,InitName,20,0);

  if (SOCKET_ERROR==nErrCode)

  {

    printf("发送消息失败!%d\n",GetLastError());

  }

  //双肩线程处理服务端发送回来的数据

  CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadProc,(LPVOID)sock,0,0);

  char buf[1024]={0};

  int nInputSize;

  while (true)

  {

    printf("请输入\n");

    scanf_s("%s",buf,1024);

    nInputSize=strlen(buf)+1;

    nErrCode=send(sock,buf,nInputSize,0);

    if (SOCKET_ERROR==nErrCode)

    {

      printf("发送消息失败\n");

    }

  }


服务器:

int nErrcode=0;//错误代码

  //创建套接字

  SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

  //设置套接字非阻塞模式

  unsigned long u1=1;  //设置套接字选项

  int nRet=ioctlsocket(sock,FIONBIO,&u1);

  //初始化ip地址  

  sockaddr_in addr;

  addr.sin_family=AF_INET;  //地址家族

  addr.sin_port=htons(1234);  //端口号  大端到小端

  addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//IP地址  本地:127.0.0.1

  //取得本地主机名称

  //char szHost[MAX_PATH]={0};

  //gethostbyname(szHost);

  //通过主机名取到地址信息

  //HOSTENT *pHost=gethostbuname(szHost);

  //绑定套接字

  nErrcode=bind(sock,  //套接字

    (sockaddr*)&addr,//  ip 地址

    sizeof(sockaddr_in));//ip 地址结构体大小 

  if (SOCKET_ERROR==nErrcode)

  {

    printf("ip地址绑定失败");

    closesocket(sock);  //关闭套接字

    WSACleanup();    //释放套接字

  }

  //监听

  nErrcode=listen(sock,

    SOMAXCONN);//能够同时连接的最大数目

  if (SOCKET_ERROR==nErrcode)

  {

    printf("监听失败");

    goto CloseSock;

  }

  int nAddrSize=sizeof(sockaddr_in);

  while (true)

  {

    sockaddr_in addrClient;

    memset(&addrClient,0,sizeof(sockaddr_in));

    addrClient.sin_family=AF_INET;

    //连接

    SOCKET ClientSocket=accept(sock,

      (sockaddr*)&addrClient,

      &nAddrSize);

    if (INVALID_SOCKET==ClientSocket)

    {

      continue;

    }

    NAME PlayName;

    PlayName.Sock=ClientSocket;

    g_player.push_back(PlayName);  //保存连接套接字

    //创建线程  结构客户端的消息

    CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadProc,(LPVOID)ClientSocket,0,0);

  }

  return TRUE;

CloseSock:

  closesocket(sock);

  WSACleanup();

  return FALSE;


以下3种模型同TCP思路 大部分一样的稍微有些不同  当然了协议肯定不同啦
UDP通信
客户机:

  //1.初始化SOCKET

  InitWinSock();

  //2.创建套接字

  SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

  sockaddr_in addr,addr2;

  int n=sizeof(addr2);

  addr.sin_family = AF_INET;

  addr.sin_port = htons(1234);

  addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  //127.0.0.1

  char InitName[20] = { 0 };           //名字

  InitName[0] = '@';

  printf("%起个名字吧:");

  scanf_s("%s", InitName + 1, 19);

  //4.发送消息

  nErrCode = sendto(sock, InitName, sizeof(InitName), 0,(sockaddr*)&addr,n);

  //if (SOCKET_ERROR == nErrCode)

  //{

  //  printf("发送消息失败!%d\n",GetLastError());

  //}

  //创建线程处理服务端发送回来的数据

//  CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)sock, 0, 0);

  char buf[1024] = { 0 };

  int nInputSize;

  while (true)

  {

    printf("请输入:");

    scanf_s("%s", buf, 1024);

    nInputSize = strlen(buf) + 1;

    nErrCode = sendto(sock, buf, nInputSize, 0,(sockaddr*)&addr,sizeof(addr2));

    //if (SOCKET_ERROR == nErrCode)

    //{

    //  printf("发送消息失败\n");

    //}

  }


服务器:

//1.初始化OCKET

  InitWinSock();

  //2.建立Socket

  //初始化winSock的动态链接库后 需要在服务器端建立一个监听socket,

  //为此可以调用socket()函数来建立这个监听的socket,并定义通信协议

  SOCKET sSocket;

  sSocket = socket(AF_INET,       //PF_INET(AF_INET)

                 SOCK_DGRAM,    //Socket的类型 (SOCK_STREAM(TCP),SOCK_DGRAM(UDP))

                 IPPROTO_UDP);  

  //通信协议(如果使用者不指定则设为0)

  if (INVALID_SOCKET == sSocket)

  {

    printf("创建套接字失败! 错误码:%d", WSAGetLastError());

    WSACleanup();                  //释放服务

    return false;

  }

  

  //3.定义服务器地址

  sockaddr_in service;              //服务器套接字地址

  service.sin_family = AF_INET;

  ULONG ulAddr = inet_addr("127.0.0.1");

  service.sin_addr.s_addr = inet_addr("127.0.0.1");

  service.sin_port = htons((u_short)1234);

  

  //4.绑定端口

  /************************************************************************/

  /* 为服务器端口定义的监听socket指定一个地址及端口(Port),这术客户端才知道在连接哪一个地址的哪个端口

  /* 为此我们要调用bind()函数,该函数成功返回0,否则返回SOCKET_ERROR.

  /************************************************************************/

  if (SOCKET_ERROR == bind(sSocket,//Socket对象名

    (SOCKADDR*)&service,//Socket 的地址值,即所在机器的IP地址

    sizeof(service)))//name 的长度

  {

    closesocket(sSocket);       

    //关闭套件字

    WSACleanup();           //释放所占资源

    return false;

  }

  _beginthreadex(0, 0, RevcFromThreadProc, (LPVOID)sSocket, 0, 0);

  getchar();

  closesocket(sSocket);         //关闭套接字

  WSACleanup();               //释放套接字资源

  return true;


WSAAsyncSelectS模型(消息选择模型)

//1.初始化OCKET

  InitWinSock();

  //2.建立Socket

  //初始化winSock的动态链接库后 需要在服务器端建立一个监听socket,

  //为此可以调用socket()函数来建立这个监听的socket,并定义通信协议

  SOCKET sSocket;

  sSocket = socket(AF_INET,       //PF_INET(AF_INET)

                 SOCK_DGRAM,    //Socket的类型 (SOCK_STREAM(TCP),SOCK_DGRAM(UDP))

                 IPPROTO_UDP);  

  //通信协议(如果使用者不指定则设为0)

  if (INVALID_SOCKET == sSocket)

  {

    printf("创建套接字失败! 错误码:%d", WSAGetLastError());

    WSACleanup();                  //释放服务

    return false;

  }

  

  //3.定义服务器地址

  sockaddr_in service;              //服务器套接字地址

  service.sin_family = AF_INET;

  ULONG ulAddr = inet_addr("127.0.0.1");

  service.sin_addr.s_addr = inet_addr("127.0.0.1");

  service.sin_port = htons((u_short)1234);

  

  //4.绑定端口

  /************************************************************************/

  /* 为服务器端口定义的监听socket指定一个地址及端口(Port),这术客户端才知道在连接哪一个地址的哪个端口

  /* 为此我们要调用bind()函数,该函数成功返回0,否则返回SOCKET_ERROR.

  /************************************************************************/

  if (SOCKET_ERROR == bind(sSocket,//Socket对象名

    (SOCKADDR*)&service,//Socket 的地址值,即所在机器的IP地址

    sizeof(service)))//name 的长度

  {

    closesocket(sSocket);       

    //关闭套件字

    WSACleanup();           //释放所占资源

    return false;

  }

  _beginthreadex(0, 0, RevcFromThreadProc, (LPVOID)sSocket, 0, 0);

  getchar();

  closesocket(sSocket);         //关闭套接字

  WSACleanup();               //释放套接字资源

  return true;


WSAEventSelect模型(事件选择模型)

//初始化套接字

  WSADATA stcData;

  int nResult=0;

  nResult=WSAStartup(MAKEWORD(2,2),&stcData);

  if (nResult==SOCKET_ERROR)

  {

    return FALSE;

  }

  //创建套接字

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

  //初始化地址

  sockaddr_in addr={0};

  addr.sin_family=AF_INET;

  addr.sin_port=htons(1234);

  addr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

  int nAddrLen=sizeof(sockaddr_in);//可以直接弄

  //////////////////////////////

  WSANETWORKEVENTS NetWorkEvent={0};//网络事件的记录和错误码

  SOCKET sClientSocket=0;//当前发送事件的客户端的sock句柄

  UINT uEventInfo=0;//事件对象的句柄数量

  CLIENTINFO ClientInfo={0};//当前发送事件的客户端

  EVENT_SOCKET_INFO EventSocketInfo={0};//保存事件和sock信息

  //绑定

  int nRet=0;

  nRet=bind(sock,(sockaddr*)&addr,nAddrLen);

  if (nRet==SOCKET_ERROR)

  {

    printf("绑定地址失败");

    goto CloseSocket;

  }

  //监听

  nRet=listen(sock,SOMAXCONN);

  if (nRet)

  {

    printf("监听失败");

    goto CloseSocket;

  }

  //创建一个事件对象

  WSAEVENT wsaEvent=WSACreateEvent();

  if (wsaEvent==WSA_INVALID_EVENT)

  {

    printf("创建事件对象失败");

    goto CloseSocket;

  }

  //注册网络事件对象

  if (WSAEventSelect(sock,    //当前服务端的sock句柄

            wsaEvent,  //事件对象句柄

            FD_ACCEPT|FD_CLOSE))//网络事件

  {

    printf("注册网络事件失败");

    goto CloseSocket;

  }

  //保存事件对象和套接字

  EventSocketInfo.EventArray[uEventInfo]=wsaEvent;

  EventSocketInfo.SocketArray[uEventInfo++]=sock;//从1开始 uEventInfo初始值为0

  while (true)

  {

    WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

    //等待网络事件发生

    DWORD dwIndex=WSAWaitForMultipleEvents(uEventInfo,  //事件句柄数量 

                  EventSocketInfo.EventArray, //指向事件对象句柄的指针

                        FALSE,      //等待事件句柄的全部数量

                      WSA_INFINITE,    //调用该函数的阻塞时间

                        FALSE);      //完成例程后不继续等待

    if (WSA_WAIT_FAILED==dwIndex)

    {

      continue;

    }

    //手动设置无信号

    WSAResetEvent(EventSocketInfo.EventArray);

    

    //找到有信号对象的标号

    //WSAWaitForMultipleEvents函数的返回值为   -WSA_WAIT_EVENT_0

    dwIndex=dwIndex-WSA_WAIT_EVENT_0;  //不减也可

    if (WSAEnumNetworkEvents(EventSocketInfo.SocketArray[dwIndex],

                EventSocketInfo.EventArray[dwIndex],

              //  EventSocketInfo.SocketArray[dwIndex],

                &NetWorkEvent)

      )

    {

      printf("调用“WSAEnumNetworkEvents”失败");

      goto CloseSocket;

    }

    //响应 网络事件 连接

    if ((NetWorkEvent.lNetworkEvents&FD_ACCEPT)&&(0==NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]))

    {

      sClientSocket=accept(sock,(sockaddr*)&addr,&nAddrLen);

      if (INVALID_SOCKET==sClientSocket)

      {

        printf("连接客户端失败");

        continue;

      }

      //为新客户创建网络事件

      EventSocketInfo.EventArray[uEventInfo]=WSACreateEvent();

      //保存刚连接进来的客户端套接字

      EventSocketInfo.SocketArray[uEventInfo]=sClientSocket;

      //为该客户端注册网络事件

      WSAEventSelect(sClientSocket,

              EventSocketInfo.EventArray[uEventInfo],

              FD_READ|FD_WRITE|FD_CLOSE);

      uEventInfo++;

      ClientInfo.ClientSock=sClientSocket;

      g_ClientInfo.push_back(ClientInfo);

      continue;

    }

    //接收消息

    if ((NetWorkEvent.lNetworkEvents&FD_READ)&&

      (0==NetWorkEvent.iErrorCode[FD_READ_BIT])

      )

    {

      //此处可接受数据  可封装   传SOCKET 套接字及下标

      char szBufTmp[1024]={0};

      int iRecv=recv(EventSocketInfo.SocketArray[dwIndex],szBufTmp,sizeof(szBufTmp),0);

      if (SOCKET_ERROR==iRecv||0==iRecv)

      {

        if (WSAEWOULDBLOCK==WSAGetLastError())

        {

          Sleep(20);

        }      

        break;

      }

      else

      {

        if (szBufTmp[0] == '@')

        {

          for (int i = 0; i < g_ClientInfo.size(); i++)

          {

            if (EventSocketInfo.SocketArray[dwIndex] == g_ClientInfo[i].ClientSock)

            {

              strcpy_s(g_ClientInfo[i].ClientName, 19, szBufTmp + 1);

              break;

            }

          }

        }

        else

        {

          int j;

          for (j = 0; j < g_ClientInfo.size(); j++)

          {

            if (EventSocketInfo.SocketArray[dwIndex] == g_ClientInfo[j].ClientSock)

            {

              break;

            }

          }

          for (int i = 0; i < g_ClientInfo.size(); i++)

          {

            char message[1044];

            sprintf_s(message, "%s说:%s", g_ClientInfo[j].ClientName, szBufTmp);

            if (i == j)

            {

              printf("%s\n", message);

              continue;

            }

            int nSize = strlen(message) + 1;

            int nErrCode = 0;

            //发送数据

            nErrCode = send(g_ClientInfo[i].ClientSock,   //套接字

              message,                 //发送数据缓冲区

              nSize,                 //发送数据长度

              0);                     //影响该函数的行为

            if (SOCKET_ERROR == nErrCode)

            {

              ::MessageBox(NULL, L"错误", L"发送数据失败", MB_OK);

            }

          }

        }

        //printf("%s",szBufTmp);

      }

      continue;

    }

    //关闭事件

    if ((NetWorkEvent.lNetworkEvents&FD_CLOSE)&&

      (0==NetWorkEvent.iErrorCode[FD_CLOSE_BIT])

      )

    {

      //关闭socket套接字和释放事件对象的占有资源

      closesocket(EventSocketInfo.SocketArray[dwIndex]);

      WSACloseEvent(EventSocketInfo.EventArray[dwIndex]);

      //将退出的事件客户端从事件数组中删除   并将之后的数据向前移动  

      //此步如果看过我上篇文章的源码 应该就很好理解了

      for (int i=dwIndex;i<uEventInfo;i++)

      {

        EventSocketInfo.EventArray[dwIndex]=EventSocketInfo.EventArray[dwIndex+1];

        EventSocketInfo.SocketArray[dwIndex]=EventSocketInfo.SocketArray[dwIndex+1];

      }

      uEventInfo--;

      continue;

    }

  }

  return TRUE;

CloseSocket:

  closesocket(sock);

  WSACleanup();

  return FALSE;


好了 就这些  稍后上传源码。。。
------------------------------------------------------------------------------
源码
Tcp通信服务器.rar
Tcp通信客户机.rar
UDP通信服务器.rar
UDP通信客户机.rar
WSAAsyncSelectS模型(消息选择模型).rar
WSAEventSelect模型(事件选择模型).rar
打包:
网络编程.rar
待会上传自己写的杀毒软件,依旧开源
界面仿腾讯电脑管家当然了肯定没那么牛逼啦  
详细见下个帖子  开始编辑ing...
部分界面如图:

上传的附件 Tcp通信服务器.rar
Tcp通信客户机.rar
UDP通信服务器.rar
UDP通信客户机.rar
WSAAsyncSelectS模型(消息选择模型).rar
WSAEventSelect模型(事件选择模型).rar
网络编程.rar
1.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功