上次实现了一个socket编程的C/S模型的通信程序,这次实现一个基于控制台的聊天程序。同样是socket技术的应用,同样是基于TCP协议的通信方式。这次的实现中遇到好多问题。废话不多说,直接进入主题。

聊天室程序,我是这么想的,需要一个服务端来控制登录用户的信息,然后可以登录很多不同的客户端。所以其实这个聊天程序事实上也是两个程序,一个服务端的程序,一个客户端的程序,服务端开一个,客户端可以开任意多个。从客户端连接到服务端,然后进行通信就可以了。

首先来设计客户端:

我认为客户端需要具备这几种功能:1、能够使用自定义的名字登录进聊天室;2、能够向登录的所有用户发送自己的消息;3、能够接受所有用户的消息;4、能够要求服务端返回当前在线用户列表;5、能够退出聊天室。

这样我的客户端程序就非常明晰了。首先需要一个登录函数login(),然后一个发送消息函数sendMessage(),继续是一个接受所有用户消息的函数RecvThread(),这里起这个名字,内行一眼就看出来是一个线程回调函数,没错,我把接受其他用户的信息设计了一个独立的线程进行处理,后话以后讲;接着是一个显示所有在线用户的函数viewList(),最后是一个退出聊天室的函数exitChatRoom()。

再来设计服务端:

服务端应该具备这些功能:1、接受用户登录并记录;2、显示所有在线用户;3、向所有用户发送系统消息;4、接受到任何一个用户的信息,把它转发到所有的客户端去;5、用户退出聊天室的处理;6、处理来自客户端显示所有在线用户的消息。

我们服务端的程序也差不多成型了。首先我们需要维护一个列表,这个列表需要记录所有在线用户,对表的操作包括用户登录时添加用户,用户退出时删除用户,addToOnlineQueue()函数用于向列表添加用户,logoutRoom()函数用户处理用户退出聊天室;showOnlineQueue()函数用于显示所有在线用户;recvMessage()函数用于接收所有用户的信息并转发;sendMsg()用于向所有客户端发送系统消息;sendListMsg()函数用于向所有有请求的客户端发送在线用户列表。

为了实现我设想的这一切,我还需要设计一些辅助的结构体来帮助我完成:

比如在客户端和服务端之间传递的报文需要自己设计,我设计的报文如下:

  1. typedef struct tagGRAMHEAD_t   
  2. {   
  3.     char    COMMAND;     //数据包命令   
  4.     short    PACKID;      //包ID   
  5.     char    COUNT;       //包总数   
  6.     char    NO;           //包分块序号   
  7.     short    LENGTH;      //包长度   
  8.     char    USERNO[20];   //用户名   
  9.     tagGRAMHEAD_t()   
  10.     {   
  11.         COMMAND =    0;   
  12.         PACKID    =    0;   
  13.         COUNT    =    0;   
  14.         NO        =    0;   
  15.         LENGTH  =    0;   
  16.         memset(USERNO, 0, sizeof(USERNO));   
  17.     }   
  18. }GramHead, *pGramHead;  

在服务端还需要维护一个在线用户列表:

  1. typedef struct tagUserOnline   
  2. {   
  3.     char userName[20];                //用户名   
  4.     sockaddr_in clientAddr;            //SOCKET地址   
  5.     tagUserOnline()   
  6.     {   
  7.         memset(userName, 0, 20);   
  8.         memset(&clientAddr, 0, sizeof(clientAddr));   
  9.     }   
  10. }UserOnline;  

然后为了区分不用的报文,以及报文的目的,我需要设计一个报文标志,定义如下:

  1. #define C_LOGIN          50             //客户登陆   
  2. #define C_LOGOUT         51             //客户离开   
  3. #define C_MESSAGE        52             //客户聊天信息   
  4. #define C_RECVMESSAGE    53             //客户接收到客户的聊天消息   
  5. #define C_USERLIST       54             //客户查看用户列表命令   
  6. #define S_LOGIN          70             //服务器返回登陆成功   
  7. #define S_LOGOUT         71             //服务器返回离开成功   
  8. #define S_MESSAGE        72             //系统消息   
  9. #define S_USERLIST       73             //服务器返回用户列表命令   
  10. #define S_USERLOGIN      74             //用户登陆,通知其他用户  

当然后来的设计中我把服务端的命令控制处理也独立用一个线程函数来处理了。程序员最喜欢看的就是代码了,我就不多废话了,直接看代码吧:

  1. chatSrv.h  
  2.  
  3. #ifndef _CHATSTRUCT_H   
  4. #define _CHATSTRUCT_H   
  5.    
  6. //在线用户的队列   
  7. #pragma pack(1)   
  8. typedef struct tagUserOnline   
  9. {   
  10.     char userName[20];              //用户名   
  11.     sockaddr_in clientAddr;         //SOCKET地址   
  12.     tagUserOnline()   
  13.     {   
  14.         memset(userName, 0, 20);   
  15.         memset(&clientAddr, 0, sizeof(clientAddr));   
  16.     }   
  17. }UserOnline;   
  18. #pragma pack()   
  19.    
  20.    
  21. //数据包头   
  22. #pragma pack(1)   
  23. typedef struct tagGRAMHEAD_t   
  24. {   
  25.     char    COMMAND;      //数据包命令   
  26.     short   PACKID;       //包ID   
  27.     char    COUNT;        //包总数   
  28.     char    NO;           //包分块序号   
  29.     short   LENGTH;       //包长度   
  30.     char    USERNO[20];   //用户名   
  31.     tagGRAMHEAD_t()   
  32.     {   
  33.         COMMAND =   0;   
  34.         PACKID  =   0;   
  35.         COUNT   =   0;   
  36.         NO      =   0;   
  37.         LENGTH  =   0;   
  38.         memset(USERNO, 0, sizeof(USERNO));   
  39.     }   
  40. }GramHead, *pGramHead;   
  41. #pragma pack()   
  42.    
  43. #define C_LOGIN          50                  //客户登陆   
  44. #define C_LOGOUT         51                  //客户离开   
  45. #define C_MESSAGE        52                  //客户聊天信息   
  46. #define C_RECVMESSAGE    53                  //客户接收到客户的聊天消息   
  47. #define C_USERLIST       54                  //客户查看用户列表命令   
  48.    
  49. #define S_LOGIN          70                  //服务器返回登陆成功   
  50. #define S_LOGOUT         71                  //服务器返回离开成功   
  51. #define S_MESSAGE        72                  //系统消息   
  52. #define S_USERLIST       73                  //服务器返回用户列表命令   
  53. #define S_USERLOGIN      74                  //用户登陆,通知其他用户   
  54.  
  55. #define PORT             7788  
  56. #define BUFFER_SIZE      1024  
  57.  
  58.  
  59. #endif   
  60.  
  61. //服务器命令线程      
  62. void serverCommandThread();     
  63. //添加用户到列表      
  64. void addToOnlineQueue(sockaddr_in from, char *name);     
  65. //显示所有在线用户      
  66. void showOnlineQueue();     
  67. //用户退出聊天室      
  68. void logoutRoom(sockaddr_in &from);     
  69. //接收到聊天消息的处理函数      
  70. void recvMessage(sockaddr_in &from, GramHead* pHead, char *buf);     
  71. //发送消息      
  72. void sendMsg(char *data, char command);     
  73. //找出在线用户列表存放到pList中      
  74. void viewList(char *pList);     
  75. //将数据发送到指定地址      
  76. void sendListMsg(char *data, char command, sockaddr_in &from);   
  1. chatSrv.cpp  
  2.  
  3. #include <winsock2.h>      
  4. #include <iostream.h>      
  5. #include <queue>      
  6. #include <list>      
  7. #include "chatSrv.h"      
  8.      
  9. #pragma comment(lib, "wsock32.lib")      
  10.      
  11. HANDLE thread_commandHandle;                 //定义接收线程句柄        
  12. std::list <UserOnline> userOnlineList;       //在线用户队列      
  13. SOCKET sock;                                 //socket      
  14.      
  15. void main()     
  16. {        
  17.     //1.启动SOCKET库,版本为2.0      
  18.     WORD wVersionRequested;     
  19.     WSADATA wsaData;     
  20.     int err;   
  21.       
  22.     wVersionRequested = MAKEWORD(2, 0);        
  23.     err = WSAStartup(wVersionRequested, &wsaData);     
  24.     if (err != 0)      
  25.     {     
  26.         cout << "Socket2.0初始化失败,Exit!";     
  27.         return;     
  28.     }        
  29.     if ((LOBYTE(wsaData.wVersion)!= 2) || (HIBYTE(wsaData.wVersion) != 0))      
  30.     {     
  31.         WSACleanup();     
  32.         return;      
  33.     }     
  34.          
  35.     //2.创建socket,      
  36.     sock = socket(AF_INET, SOCK_DGRAM, 0);     
  37.          
  38.     if (sock == INVALID_SOCKET)   
  39.     {     
  40.         cout << "Socket 创建失败,Exit!";     
  41.         return;     
  42.     }     
  43.          
  44.     //3.绑定      
  45.     sockaddr_in myaddr; //sockaddr_in相当于sockaddr结构      
  46.     memset(&myaddr, 0, sizeof(myaddr));     
  47.     myaddr.sin_family = AF_INET;     
  48.     myaddr.sin_addr.s_addr = ADDR_ANY;     
  49.     myaddr.sin_port = htons(PORT);         
  50.     bind(sock, (sockaddr*)&myaddr, sizeof(myaddr));     
  51.          
  52.     cout << "服务器开启成功!" << endl;     
  53.  
  54.     DWORD session_thread_id = 1;     
  55.     thread_commandHandle = CreateThread(0, 0,   
  56.         (LPTHREAD_START_ROUTINE)serverCommandThread,     
  57.         0, 0, &session_thread_id);     
  58.      
  59.     //接收到的SOCK地址      
  60.     sockaddr_in from;     
  61.     memset(&from, 0, sizeof(from));     
  62.          
  63.     GramHead head, backHead;     
  64.     int fromlength = sizeof(SOCKADDR), nnlen;    
  65.       
  66.     nnlen = fromlength;     
  67.     char *buf = new char[BUFFER_SIZE];     
  68.     memset(buf, 0, BUFFER_SIZE);     
  69.  
  70.     long number = 0;     
  71.     while (1)     
  72.     {     
  73.         number++;     
  74.      
  75.         recvfrom(sock, buf, BUFFER_SIZE, 0,   
  76.                  (struct sockaddr FAR *)&from,   
  77.                  (int FAR *)&fromlength);     
  78.  
  79.         memcpy(&head, buf, sizeof(GramHead));     
  80.         switch (head.COMMAND)     
  81.         {     
  82.         case C_LOGIN:                                   //登陆成功      
  83.             backHead.COMMAND = S_LOGIN;     
  84.             memcpy(buf, &backHead, sizeof(GramHead));     
  85.             if (sendto(sock, buf, sizeof(GramHead), 0, (sockaddr*)&from, nnlen)     
  86.                 == SOCKET_ERROR)     
  87.             {  
  88.                 cout << "main" << endl;  
  89.                 cout << WSAGetLastError() << endl;   
  90.             }  
  91.             cout << "用户" << (unsigned char *)head.USERNO << "登陆到聊天室!" << endl;     
  92.             addToOnlineQueue(from, head.USERNO);        //添加加用户到在线列表      
  93.             cout << "聊天室当前人数为:" << userOnlineList.size() << endl;     
  94. //          showOnlineQueue();                          //显示用户      
  95.             break;     
  96.         case C_LOGOUT:                                  //用户退出聊天室      
  97.             logoutRoom(from);     
  98.             break;     
  99.         case C_MESSAGE:                                 //用户聊天信息      
  100.             char data[BUFFER_SIZE-sizeof(GramHead)];     
  101.             memcpy(data, buf + sizeof(GramHead), BUFFER_SIZE - sizeof(GramHead));     
  102.             recvMessage(from, &head, data);     
  103.             break;     
  104.         case C_USERLIST:     
  105.             char plist[BUFFER_SIZE - sizeof(GramHead)];     
  106.             memcpy(plist, buf + sizeof(GramHead), BUFFER_SIZE - sizeof(GramHead));     
  107.             viewList(plist);     
  108.             sendListMsg(plist, S_USERLIST, from);     
  109.             break;     
  110.         }     
  111.         Sleep(500);     
  112.         memset(buf, 0, BUFFER_SIZE);     
  113.     }     
  114.     delete buf;      
  115.          
  116.     if (!closesocket(sock))      
  117.     {     
  118.         WSAGetLastError();     
  119.         return;     
  120.     }     
  121.     if (!WSACleanup())      
  122.     {     
  123.         WSAGetLastError();     
  124.         return;     
  125.     }     
  126. }     
  127.      
  128. void addToOnlineQueue(sockaddr_in from, char *name)     
  129. {     
  130.     sendMsg(name, S_USERLOGIN);   
  131.       
  132.     UserOnline online;     
  133.     online.clientAddr = from;     
  134.     strcpy(online.userName, name);     
  135.     userOnlineList.push_back(online);     
  136. }     
  137.      
  138. void showOnlineQueue()     
  139. {     
  140.     int size = userOnlineList.size();     
  141.     if (size == 0)     
  142.     {     
  143.         cout << "当前没有用户在线...." << endl;     
  144.         return;     
  145.     }     
  146.     else     
  147.     {     
  148.         cout << "---------------------------------" << endl;     
  149.         cout << "当前在线人数为:" << size << endl;     
  150.     }     
  151.  
  152.     std::list <UserOnline>::iterator theIterator;     
  153.     for (theIterator = userOnlineList.begin(); theIterator != userOnlineList.end(); theIterator++)     
  154.     {  
  155.         cout << "IP地址:" << inet_ntoa(theIterator->clientAddr.sin_addr)     
  156.         << "  端口号:" << theIterator->clientAddr.sin_port << " 用户名:"     
  157.         << theIterator->userName << endl;     
  158.     }  
  159.     cout << "---------------------------------" << endl;     
  160. }     
  161.      
  162. void logoutRoom(sockaddr_in &from)     
  163. {     
  164.     char msg[BUFFER_SIZE-sizeof(GramHead)];     
  165.     std::list <UserOnline>::iterator theIterator;     
  166.     for (theIterator = userOnlineList.begin(); theIterator != userOnlineList.end(); theIterator++)     
  167.     {     
  168.         if (inet_ntoa(theIterator->clientAddr.sin_addr) == inet_ntoa(from.sin_addr) &&   
  169.             theIterator->clientAddr.sin_port == from.sin_port)     
  170.         {     
  171.             memcpy(msg, theIterator->userName, sizeof(theIterator->userName));     
  172.             cout << msg << "离开了聊天室......" << endl;     
  173.             userOnlineList.erase(theIterator);     
  174.             sendMsg(msg, S_LOGOUT);     
  175.             break;     
  176.         }     
  177.     }     
  178.     cout << "当前在线人数为:" << userOnlineList.size() << endl;     
  179. }     
  180.      
  181.      
  182. void recvMessage(sockaddr_in &from, GramHead* pHead, char *buf)     
  183. {     
  184.     char dataBuf[BUFFER_SIZE];     
  185.     GramHead head;     
  186.     memcpy(head.USERNO, pHead->USERNO, sizeof(pHead->USERNO));   
  187.  
  188.     head.COMMAND = C_RECVMESSAGE;     
  189.     memcpy(dataBuf, &head, sizeof(GramHead));     
  190.     memcpy(dataBuf + sizeof(GramHead), buf, BUFFER_SIZE - sizeof(GramHead));     
  191.  
  192.     std::list <UserOnline>::iterator theIterator;     
  193.     for (theIterator = userOnlineList.begin(); theIterator != userOnlineList.end(); theIterator++)     
  194.     {        
  195.         if (sendto(sock, dataBuf, BUFFER_SIZE, 0, (sockaddr*)&theIterator->clientAddr, sizeof(SOCKADDR))     
  196.             == SOCKET_ERROR)     
  197.         {  
  198.             //cout << "recvMessage" << endl;  
  199.             cout << WSAGetLastError() << endl;     
  200.         }  
  201.     }     
  202. }     
  203.      
  204. void sendMsg(char *data, char command)     
  205. {     
  206.     char buf[BUFFER_SIZE];     
  207.     memset(buf, 0, BUFFER_SIZE);    
  208.       
  209.     GramHead head;     
  210.     head.COMMAND = command;     
  211.      
  212.     memcpy(buf, &head, sizeof(GramHead));     
  213.     memcpy(buf + sizeof(GramHead), data, BUFFER_SIZE - sizeof(GramHead));     
  214.          
  215.     std::list <UserOnline>::iterator theIterator;     
  216.     for (theIterator = userOnlineList.begin(); theIterator != userOnlineList.end(); theIterator++)     
  217.     {        
  218.         if (sendto(sock, buf, BUFFER_SIZE, 0, (sockaddr*)&theIterator->clientAddr, sizeof(SOCKADDR))     
  219.             == SOCKET_ERROR)     
  220.         {  
  221.             //cout << "sendMsg" << endl;  
  222.             cout << WSAGetLastError() << endl;     
  223.         }  
  224.     }     
  225. }     
  226.      
  227. void sendListMsg(char *data, char command, sockaddr_in &from)     
  228. {     
  229.     char buf[BUFFER_SIZE];     
  230.     memset(buf, 0, BUFFER_SIZE);    
  231.       
  232.     GramHead head;     
  233.     head.COMMAND = command;     
  234.          
  235.     memcpy(buf, &head, sizeof(GramHead));     
  236.     memcpy(buf + sizeof(GramHead), data, BUFFER_SIZE - sizeof(GramHead));     
  237.     if (sendto(sock, buf, BUFFER_SIZE, 0, (sockaddr*)&from, sizeof(SOCKADDR))     
  238.         == SOCKET_ERROR)    
  239.     {  
  240.         //cout << "sendListMsg" << endl;  
  241.         cout << WSAGetLastError() << endl;    
  242.     }  
  243. }     
  244.      
  245. //用户输入命令,完成操作      
  246. void serverCommandThread()     
  247. {     
  248.     char ch[BUFFER_SIZE-sizeof(GramHead)];     
  249.     while (1)     
  250.     {     
  251.         Sleep(1000);     
  252.         memset(ch, 0, BUFFER_SIZE - sizeof(GramHead));    
  253.           
  254.         //cin >> ch;     
  255.         gets(ch);  
  256.         if (!strcmp(ch, "list"))     
  257.         {     
  258.             showOnlineQueue();     
  259.         }     
  260.         else if (!strcmp(ch, "help"))     
  261.         {     
  262.             cout << "---------------------------------" << endl;     
  263.             cout << "[list] 命令查看在线用户列表" << endl;     
  264.             cout << "[send] 命令发送信息" << endl;     
  265.             cout << "---------------------------------" << endl;     
  266.         }     
  267.         else if (ch[0] == 's' && ch[1] == 'e' && ch[2] == 'n' && ch[3] == 'd')     
  268.         {     
  269.             sendMsg(ch + 4, S_MESSAGE);     
  270.         }     
  271.     }     
  272. }     
  273.      
  274. void viewList(char *pList)     
  275. {     
  276.     memset(pList, 0, BUFFER_SIZE);     
  277.     int start = 0, length = 0;     
  278.  
  279.     std::list <UserOnline>::iterator theIterator;     
  280.     for (theIterator = userOnlineList.begin(); theIterator != userOnlineList.end(); theIterator++)     
  281.     {     
  282.         length = strlen(theIterator->userName);     
  283.         memcpy(pList + start, theIterator->userName, length);     
  284.         pList[start+length] = '\n';     
  285.         start += length + 1;     
  286.     }     
  287. }   
  1. chatCli.h  
  2.  
  3. #ifndef _CHATSTRUCT_H   
  4. #define _CHATSTRUCT_H   
  5.    
  6. //数据包头   
  7. #pragma pack(1)   
  8. typedef struct tagGRAMHEAD_t   
  9. {   
  10.     char    COMMAND;      //数据包命令   
  11.     short   PACKID;       //包ID   
  12.     char    COUNT;        //包总数   
  13.     char    NO;           //包分块序号   
  14.     short   LENGTH;       //包长度   
  15.     char    USERNO[20];   //用户名   
  16.     tagGRAMHEAD_t()   
  17.     {   
  18.         COMMAND =   0;   
  19.         PACKID  =   0;   
  20.         COUNT   =   0;   
  21.         NO      =   0;   
  22.         LENGTH  =   0;   
  23.         memset(USERNO, 0, sizeof(USERNO));   
  24.     }   
  25. }GramHead, *pGramHead;   
  26. #pragma pack()   
  27.    
  28. #define C_LOGIN             50                  //客户登陆   
  29. #define C_LOGOUT            51                  //客户离开   
  30. #define C_MESSAGE           52                  //客户发送聊天信息   
  31. #define C_RECVMESSAGE       53                  //客户接收到客户的聊天消息   
  32. #define C_USERLIST          54                  //客户查看用户列表命令   
  33.    
  34. #define S_LOGIN             70                  //服务器返回登陆成功   
  35. #define S_LOGOUT            71                  //服务器返回离开成功   
  36. #define S_MESSAGE           72                  //系统消息   
  37. #define S_USERLIST          73                  //服务器返回用户列表命令   
  38. #define S_USERLOGIN         74                  //用户登陆,通知其他用户   
  39.    
  40. #define Srv_IP_ADDR         "127.0.0.1"  
  41. #define PORT                7788  
  42. #define BUFFER_SIZE         1024  
  43. #define LOGIN_NAME_LENGTH   20  
  44.  
  45. #endif   
  46.  
  47. bool login();                               //登陆      
  48. void sendMessage(char *buf, int length);    //发送聊天消息      
  49. void viewList();                            //查看用户列表      
  50. bool exitChatRoom();                        //退出聊天室    
  1. chatCli.cpp  
  2.  
  3. #include <winsock2.h>      
  4. #include <iostream.h>    
  5. #include <stdio.h>    
  6. #include "chatCli.h"      
  7. #pragma comment(lib, "wsock32.lib")      
  8.      
  9. SOCKET sock;          //socket      
  10. sockaddr_in addrto;   //发往的地址      
  11.     
  12. char loginName[LOGIN_NAME_LENGTH];  //登录用户名  
  13. char sendMsg[BUFFER_SIZE];          //发送缓冲区      
  14.      
  15. HANDLE thread_recvHandle;   //定义接收线程句柄      
  16. void RecvThread();          //接收线程      
  17.      
  18. void main()     
  19. {        
  20.     //1.启动SOCKET库,版本为2.0      
  21.     WORD wVersionRequested;     
  22.     WSADATA wsaData;     
  23.     int err;         
  24.  
  25.     wVersionRequested = MAKEWORD(2, 0);        
  26.     err = WSAStartup(wVersionRequested, &wsaData);     
  27.     if (err != 0)      
  28.     {     
  29.         cout << "Socket2.0初始化失败,Exit!";     
  30.         return;  
  31.     }        
  32.     if ((LOBYTE(wsaData.wVersion) != 2) || (HIBYTE(wsaData.wVersion) != 0))    
  33.     {     
  34.         WSACleanup();     
  35.         return;      
  36.     }     
  37.          
  38.     //2.创建socket,      
  39.     sock = socket(AF_INET, SOCK_DGRAM, 0);     
  40.          
  41.     if (sock == INVALID_SOCKET )   
  42.     {     
  43.         cout << "Socket 创建失败,Exit!";     
  44.         return;     
  45.     }     
  46.          
  47.     //3.设置发往的地址      
  48.     memset(&addrto, 0, sizeof(addrto));     
  49.     addrto.sin_family = AF_INET;     
  50.     addrto.sin_addr.s_addr = inet_addr(Srv_IP_ADDR); //此处应填服务器IP         
  51.     addrto.sin_port = htons(PORT); //端口号必须和服务器绑定的端口号一致      
  52.          
  53.     int nlen = sizeof(addrto);     
  54.     unsigned int uIndex = 1;     
  55.          
  56.     while (!login())     
  57.     {     
  58.         continue;     
  59.     }     
  60.      
  61.     //创建会话线程  
  62.     DWORD session_thread_id = 1;     
  63.     thread_recvHandle = CreateThread(0, 0,   
  64.         (LPTHREAD_START_ROUTINE)RecvThread,     
  65.         0, 0, &session_thread_id);     
  66.  
  67.     while (true)     
  68.     {     
  69.         memset(sendMsg, 0, BUFFER_SIZE);     
  70.         //cin >> sendMsg;   
  71.         gets(sendMsg);  
  72.         if (!strcmp(sendMsg, "list"))     
  73.         {     
  74.             viewList();     
  75.         }     
  76.         else if (!strcmp(sendMsg, "exit"))     
  77.         {     
  78.             if (exitChatRoom())     
  79.             {     
  80.                 break;     
  81.             }     
  82.             else     
  83.             {     
  84.                 cout << "非安全退出..........." << endl;     
  85.             }     
  86.         }     
  87.         else if (!strcmp(sendMsg, "help"))     
  88.         {     
  89.             cout << "*********************************" << endl;     
  90.             cout << "[list] 命令查看在线用户列表" << endl;     
  91.             cout << "[help] 命令查看帮助" << endl;     
  92.             cout << "[exit] 命令退出聊天室" << endl;     
  93.             cout << "*********************************" << endl;     
  94.         }     
  95.         else     
  96.         {     
  97.             sendMessage(sendMsg, strlen(sendMsg));     
  98.         }     
  99.          
  100.     }     
  101.     Sleep(2500);     
  102.      
  103.     if (!closesocket(sock))      
  104.     {     
  105.         WSAGetLastError();     
  106.         return;     
  107.     }     
  108.     if (!WSACleanup())      
  109.     {     
  110.         WSAGetLastError();     
  111.         return;     
  112.     }        
  113. }     
  114.      
  115. bool login()     
  116. {     
  117.     GramHead loginHead;     
  118.     unsigned char name[LOGIN_NAME_LENGTH];     
  119.  
  120.     memset(name, 0, LOGIN_NAME_LENGTH);     
  121.  
  122.     cout << "输入你登录聊天室的用户名:" << endl;     
  123.     cin >> loginHead.USERNO;     
  124.     strcpy(loginName, loginHead.USERNO);     
  125.  
  126.     loginHead.COMMAND = C_LOGIN;     
  127.     loginHead.LENGTH = BUFFER_SIZE;     
  128.     loginHead.COUNT = 5;     
  129.     loginHead.NO = 1;     
  130.     loginHead.PACKID = 20;     
  131.  
  132.     unsigned char *szMsg = new unsigned char[BUFFER_SIZE];     
  133.     memset(szMsg, 0, BUFFER_SIZE);    
  134.       
  135.     int nlen = sizeof(addrto);     
  136.     memcpy(szMsg, &loginHead, sizeof(GramHead));    
  137.         
  138.     if (sendto(sock, (const char *)szMsg, sizeof(GramHead), 0, (sockaddr*)&addrto, nlen)     
  139.         == SOCKET_ERROR)     
  140.     {  
  141.         cout << "login" << endl;  
  142.         cout << WSAGetLastError() << endl;     
  143.     }  
  144.     else     
  145.     {  
  146.         cout << "正在登陆,请稍后......" << endl;     
  147.     }  
  148.     delete szMsg;     
  149.      
  150.     //接收到的SOCK地址      
  151.     sockaddr_in from;     
  152.     memset(&from, 0, sizeof(from));     
  153.          
  154.     int fromlength = sizeof(SOCKADDR);     
  155.     char *buf = new char[BUFFER_SIZE];   
  156.       
  157.     Sleep(1000);     
  158.  
  159.     recvfrom(sock, buf, BUFFER_SIZE, 0,   
  160.              (struct sockaddr FAR *)&from,   
  161.              (int FAR *)&fromlength);    
  162.       
  163.     GramHead loginBack; //接收到的登陆返回包头      
  164.     memcpy(&loginBack, buf, sizeof(GramHead));     
  165.  
  166.     if (loginBack.COMMAND == S_LOGIN)     
  167.     {     
  168.         cout << "登陆成功,欢迎你来到rangercyh的聊天室!" << endl;     
  169.         delete buf;     
  170.     }     
  171.     else     
  172.     {     
  173.         delete buf;     
  174.         return false;     
  175.     }     
  176.     return true;     
  177. }     
  178.      
  179. void sendMessage(char *buf, int length)     
  180. {     
  181.     GramHead sendHead;    
  182.       
  183.     sendHead.COMMAND = C_MESSAGE;     
  184.     strcpy(sendHead.USERNO, loginName);     
  185.      
  186.     unsigned char *szMsg = new unsigned char[BUFFER_SIZE];     
  187.     memset(szMsg, 0, BUFFER_SIZE);     
  188.  
  189.     int nlen = sizeof(addrto);     
  190.     memcpy(szMsg, &sendHead, sizeof(GramHead));     
  191.     memcpy(szMsg + sizeof(GramHead), buf, length);     
  192.          
  193.     if (sendto(sock, (const char *)szMsg, sizeof(GramHead) + length, 0, (sockaddr*)&addrto, nlen)     
  194.         == SOCKET_ERROR)   
  195.     {  
  196.         //cout << "sendMessage" << endl;  
  197.         cout << WSAGetLastError() << endl;  
  198.     }  
  199. }     
  200.      
  201. //接收线程      
  202. void RecvThread()     
  203. {     
  204.     //接收到的SOCK地址      
  205.     sockaddr_in from;     
  206.  
  207.     memset(&from, 0, sizeof(from));     
  208.          
  209.     int fromlength = sizeof(SOCKADDR);     
  210.     char *buf = new char[BUFFER_SIZE];     
  211.  
  212.     memset(buf, 0, BUFFER_SIZE);     
  213.     Sleep(1000);   
  214.       
  215.     while (1)     
  216.     {     
  217.         Sleep(1000);     
  218.  
  219.         recvfrom(sock, buf, BUFFER_SIZE, 0,   
  220.                  (struct sockaddr FAR *)&from,   
  221.                  (int FAR *)&fromlength);     
  222.  
  223.         GramHead msgHead;   //接收到的登陆返回包头      
  224.         memcpy(&msgHead, buf, sizeof(GramHead));    
  225.           
  226.         switch (msgHead.COMMAND)     
  227.         {     
  228.         case C_RECVMESSAGE:     //客户消息      
  229.             cout << msgHead.USERNO << " 说道:" << buf + sizeof(GramHead) << endl;     
  230.             break;     
  231.         case S_MESSAGE:         //系统消息      
  232.             cout << "系统消息:" << buf + sizeof(GramHead) << endl;     
  233.             break;     
  234.         case S_USERLIST:     
  235.             cout << "--------------------------------" << endl;     
  236.             cout << "当前在线用户为:" << endl;     
  237.             cout << buf + sizeof(GramHead);     
  238.             cout << "--------------------------------" << endl;     
  239.             break;     
  240.         case S_LOGOUT:     
  241.             cout << ":::::系统提示:::::" << buf + sizeof(GramHead) << " 离开了聊天室......" << endl;     
  242.             break;     
  243.         case S_USERLOGIN:     
  244.             cout << ":::::系统提示:::::" << buf + sizeof(GramHead) << " 进入了聊天室!" << endl;     
  245.             break;     
  246.         }     
  247.         memset(buf, 0, BUFFER_SIZE);     
  248.     }     
  249.     delete buf;     
  250. }     
  251.      
  252. void viewList()     
  253. {     
  254.     GramHead head;     
  255.     head.COMMAND = C_USERLIST;     
  256.     unsigned char *szMsg = new unsigned char[BUFFER_SIZE];    
  257.       
  258.     memset(szMsg, 0, BUFFER_SIZE);     
  259.  
  260.     int nlen = sizeof(addrto);     
  261.     memcpy(szMsg, &head, sizeof(GramHead));     
  262.          
  263.     if (sendto(sock, (const char *)szMsg, sizeof(GramHead), 0, (sockaddr*)&addrto, nlen)     
  264.         == SOCKET_ERROR)  
  265.     {  
  266.         //cout << "viewList" << endl;  
  267.         cout << WSAGetLastError() << endl;    
  268.     }  
  269.     delete szMsg;     
  270. }     
  271.      
  272. bool exitChatRoom()     
  273. {     
  274.     GramHead head;     
  275.  
  276.     head.COMMAND = C_LOGOUT;     
  277.  
  278.     unsigned char *szMsg = new unsigned char[BUFFER_SIZE];     
  279.     memset(szMsg, 0, BUFFER_SIZE);     
  280.  
  281.     int nlen = sizeof(addrto);     
  282.     memcpy(szMsg, &head, sizeof(GramHead));     
  283.          
  284.     if (sendto(sock, (const char *)szMsg, sizeof(GramHead), 0, (sockaddr*)&addrto, nlen)     
  285.         == SOCKET_ERROR)     
  286.     {     
  287.         //cout << "exitChatRoom" << endl;  
  288.         cout << WSAGetLastError() << endl;     
  289.         delete szMsg;     
  290.         return false;     
  291.     }     
  292.     delete szMsg;     
  293.     return true;     
  294. }   

之前我一直有一个错误,我写的sendto()函数一直无法成功,总是返回SOCKET_ERROR,我调用WSAGetLastError()函数能够看到程序错误码是10038,用Error Lookup查看错误码显示如下:

编写一个基于控制台的聊天室程序_职场

这意味着我sendto()函数的第一个参数存在问题,仔细检查后发现我的socket重定义了。唉~~人生不如意十有八九啊!果断改了,在编写过程中遇到很多这类问题,这里就不一一列举了,反正你也不爱听。

最后效果如图:

 

编写一个基于控制台的聊天室程序_控制台聊天室_02