首页
社区
课程
招聘
[原创][开源]通信服务器+客户机的四种模型
发表于: 2015-11-14 13:30 11465

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

2015-11-14 13:30
11465
理论知识我也不说太多了,网络上一百度大把,但可能就是贴部分代码并没开源,所以你拿过去可能就用不了,而我这个就不同了开源分享

运行环境: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...
部分界面如图:

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (18)
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大于这个数, WSA_MAXIMUM_WAIT_EVENTS 的情况,楼主怎么解决的.
2015-11-14 14:02
0
雪    币: 163
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持开源
2015-11-14 14:11
0
雪    币: 768
活跃值: (515)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
4
mark,,,收藏一下,,谢谢楼主
2015-11-14 14:36
0
雪    币: 6400
活跃值: (4160)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
5
IOCP+线程池呢?
2015-11-14 14:37
0
雪    币: 156
活跃值: (97)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
DNS呢?
2015-11-14 14:40
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
IOCP和HTTP的有没有?
2015-11-14 15:30
0
雪    币: 1176
活跃值: (1234)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
8
想到过这点 但没做过
我估摸着添加线程吧  根据线程句柄来判断   
2015-11-14 15:47
0
雪    币: 1176
活跃值: (1234)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
9
时间 精力不够  没写  以后有时间再加上吧  
2015-11-14 15:49
0
雪    币: 1176
活跃值: (1234)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
10
没有写啊
2015-11-14 15:54
0
雪    币: 1361
活跃值: (1052)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
mark
2015-11-15 03:26
0
雪    币: 69
活跃值: (157)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
12
libev libuv
2015-11-15 09:42
0
雪    币: 86
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
很详细!
2015-11-16 22:21
0
雪    币: 54
活跃值: (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2015-11-16 22:38
0
雪    币: 325
活跃值: (650)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2015-11-19 01:01
0
雪    币: 263
活跃值: (64)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
感觉好多搞安全的人都比较偏科
2015-11-19 09:03
0
雪    币: 79
活跃值: (184)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
17
去看看ghost  就够了
2015-11-19 09:06
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
有没有IM那种的?
2015-11-23 11:56
0
雪    币: 5511
活跃值: (2072)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
感谢分享!
2015-12-1 11:57
0
游客
登录 | 注册 方可回帖
返回
//