zoukankan      html  css  js  c++  java
  • 简易的命令行聊天室程序(Winsock,服务器&客户端)

           代码中使用WinSock2函数库,设计并实现了简单的聊天室功能。该程序为命令行程序。对于服务器和客户端,需要:

    1. 服务器:创建监听套接字,并按本地主机绑定;主线程监听并接受来自客户端的请求,并为该客户端创建单独线程;接收与发送消息的事务放在同各客户端的单独线程中处理。
    2. 客户端:创建套接字,并对服务器发起连接;主线程始终处于发送消息的状态;副线程用于不断从服务器接收来自其他客户端的消息。

    实验代码及部分说明如下:

    1. 服务器(Server.cpp)

    #include <WinSock2.h>

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include<vector>

     

    using namespace std;

     

    #define DEFAULT_PORT 7321

    #define BUF_SIZE 1024

     

    typedef struct{

          SOCKET info_socket_rev;

          sockaddr_in info_addr_rev;

    } info2thread;

     

    char szMessage[BUF_SIZE];

    std::vector<SOCKET>  vClientSockets;  //用于保存连接中的Client套接字

     

    DWORD WINAPI ThreadProc(PVOID pParam) ;

     

    int main(int argc,char* argv[]){

          WSADATA   wsd;

          SOCKET    sListen,sClient;

          sockaddr_in    local,client;

          int    iAddrSize;

          HANDLE    hThread;

        DWORD    dwThreadId;

          info2thread infoParam;

     

          //加载Winsock

          if(WSAStartup(MAKEWORD(2,2),&wsd) != 0){

               printf("Failed to load Winsock! ");

               exit(1);

          }

          //创建监听套接字及其地址信息结构体

          sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

          local.sin_addr.s_addr = htonl(INADDR_ANY);

          local.sin_port = htons(DEFAULT_PORT);

          local.sin_family = AF_INET;

          //j将套接字和地址信息绑定

          if(bind(sListen,(const sockaddr*) &local,sizeof(local)) == SOCKET_ERROR){

               printf("bind() failed:%d ",WSAGetLastError());

               return 1;

          }

          //监听

          listen(sListen,7);

          printf("Server is Ready, listening on Port:%d. > ",ntohs(local.sin_port));

         

     

          while(1){

               //处理连接请求

               iAddrSize = sizeof(client);

               sClient = accept(sListen,(sockaddr*)&client,&iAddrSize);

               if(sClient == INVALID_SOCKET){

                     printf("accept() failed:%d ",WSAGetLastError());

                     return 1;

               }

               printf("Accepted Client: %s:%d ",

                          inet_ntoa(client.sin_addr), ntohs(client.sin_port));

              

     

               infoParam.info_addr_rev = client;     //infoParam用于构建传给线程的结构体,主要用于IP地址和Port端口的输出

               infoParam.info_socket_rev = sClient;

               //在vector中保存连接中的客户端Socket

               vClientSockets.push_back(sClient);

               //创建进程接收数据

               hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&infoParam,0,&dwThreadId);

               if (hThread == NULL){

                printf("CreateThread() failed: %d ", GetLastError());

                break;

            }

               CloseHandle(hThread); //不再需要这个句柄,关掉它,但并非是关掉对应线程

          }

          //关闭

          closesocket(sListen);

     

          WSACleanup();

          return 0;

    }

     

    DWORD WINAPI ThreadProc (PVOID pParam) {

          int ret,errCode;

          info2thread *pInfo = (info2thread*) pParam;

          SOCKET sClient = pInfo->info_socket_rev,

                          sOther;

          sockaddr_in client = pInfo->info_addr_rev;

          char     szIPPort[BUF_SIZE],

                     szPort[7];

     

          //构造客户端 "IP:Port" 标识

          _itoa(ntohs(client.sin_port),szPort,10);  //itoa函数用于将整型数转化成对应的字符串,10表示十进制,末尾自动添''

          strcpy(szIPPort,inet_ntoa(client.sin_addr));  //拷贝IP

          strcpy(szIPPort+strlen(inet_ntoa(client.sin_addr)),":");

          strcpy(szIPPort+strlen(inet_ntoa(client.sin_addr))+1,szPort);  //拷贝Port

     

          while(1){

               //接收来自客户端的数据

               ret = recv(sClient,szMessage,BUF_SIZE,0);

               if (ret == SOCKET_ERROR){

                     errCode = WSAGetLastError();

                     switch(errCode){

                          case 10054: //10054:客户端主动退出时的错误代码

                                printf("%s:%d has exited. ",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

                                break;

                          default:

                                printf("recv() failed:%d from %s ", errCode,szIPPort);

                     }

     

                     break;  //跳出while(1)循环

               }

               szMessage[ret] = '';

               printf("Server%d REV %s: %s ",     GetCurrentProcessId(),

                                                                                  szIPPort,

                                                                                  szMessage);

              

               //向正在连接的所有其他客户端发送消息(聊天室)

               for(vector<SOCKET>::iterator it = vClientSockets.begin();it != vClientSockets.end(); ++it){

                     if(*it == sClient)      continue;

                    

                     sOther = *it;

                     //广播客户标识信息

                     ret = send(sOther,szIPPort,strlen(szIPPort),0);

                     if (ret == SOCKET_ERROR); //do nothing

                     //广播客户消息

                     ret = send(sOther,szMessage,strlen(szMessage),0);

                     if (ret == SOCKET_ERROR); //do nothing

               }

          }

     

          //关闭套接字,从vector中删除对应的元素

          for(vector<SOCKET>::iterator it = vClientSockets.begin(); it != vClientSockets.end();  ){

               if(*it == sClient){

                     it = vClientSockets.erase(it);

                     break;

               }

               else{

                     ++it;

               }

          }

          closesocket(sClient);

     

          return 0;

    }

    1. 客户端(Client.cpp)

    #include <WinSock2.h>

    #include <stdio.h>

     

    #define DEFAULT_PORT        7321

    #define BUF_SIZE 128

     

    char szMessage[BUF_SIZE],

               szRecieved[BUF_SIZE];

     

    DWORD WINAPI ThreadProc (PVOID);

     

    int main(int argc, char *argv[]){

          WSADATA wsd;

          SOCKET sClient;

          struct sockaddr_in server;

          HANDLE    hThread;

        DWORD    dwThreadId;

          int ret;

     

     

          // 加载Winsock

          if(WSAStartup(MAKEWORD(2,2), &wsd) != 0){

               printf("Failed to load Winsock library! ");

            return 1;

          }

          //创建套接字

          sClient = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

          if(sClient == INVALID_SOCKET){

               printf("socket() failed on %d ",WSAGetLastError());

               return 1;

          }

          //填写服务器地址信息结构体

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

          server.sin_port = htons(DEFAULT_PORT);

          server.sin_family = AF_INET;

          //查询服务器

     

          //连接

          if(connect(  sClient,(const sockaddr*)&server,

                                sizeof(server)) == SOCKET_ERROR){

               printf("connect() failed: %d ", WSAGetLastError());   

            return 1;

          }

          else

          {

               printf("This Client is Connected. > ");

          }

     

          //创建一个用于不停接收服务器数据的进程

          hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&sClient,0,&dwThreadId);

          if (hThread == NULL){

                printf("CreateThread() failed: %d, can't receive message from SERVER! ", GetLastError());

          }

          CloseHandle(hThread); //不再需要这个句柄,关掉它,但并非是关掉对应线程

     

          //发送数据

          do{

               gets_s(szMessage,BUF_SIZE-1);

               ret = send(sClient,szMessage,strlen(szMessage),0);

               if (ret == SOCKET_ERROR){

                printf("send() failed: %d ", WSAGetLastError());

                break;

            }

          }while(ret);

     

          //关闭套接字

          closesocket(sClient);

          WSACleanup();

     

          return 0;

    }

     

    DWORD WINAPI ThreadProc (PVOID pParam){

          SOCKET sRecv = *(SOCKET*)pParam;

          int ret;

     

          while(1){

     

               //接收标识

               ret = recv(sRecv,szRecieved,BUF_SIZE,0);

               if(ret == SOCKET_ERROR){

                     printf("[Unknown]"); //标识接收失败,则统一认其为Unknown用户

               }

               else{

                     szRecieved[ret] = '';

                     printf("[%s]",szRecieved);

               }

     

               //分割符

               printf(":");

     

               //接收消息

               ret = recv(sRecv,szRecieved,BUF_SIZE,0);

               if(ret == SOCKET_ERROR){

                     printf("recv() from server failed: %d ",WSAGetLastError());

                     continue;

               }

               szRecieved[ret] = '';

               printf("%s ",szRecieved);

          }

     

          return 0;

    }

          

           运行结果截图:

          

  • 相关阅读:
    [LeetCode] 14. Longest Common Prefix 最长共同前缀
    [LeetCode] 16. 3Sum Closest 最近三数之和
    [LeetCode] 15. 3Sum 三数之和
    [LeetCode] 75. Sort Colors 颜色排序
    [LeetCode] 13. Roman to Integer 罗马数字转为整数
    [LeetCode] 12. Integer to Roman 整数转为罗马数字
    [LeetCode] 8. String to Integer (atoi) 字符串转为整数
    [LeetCode] 9. Palindrome Number 验证回文数字
    [LeetCode] 388. Longest Absolute File Path 最长的绝对文件路径
    [LeetCode] 138. Copy List with Random Pointer 拷贝带随机指针的链表
  • 原文地址:https://www.cnblogs.com/instona/p/3467576.html
Copyright © 2011-2022 走看看