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

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

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

最后效果如图:

转载于:https://blog.51cto.com/rangercyh/546013

编写一个基于控制台的聊天室程序相关推荐

  1. 用Asp.Net创建基于Ajax的聊天室程序

    原作者Dahan Abdo 译自CodeProject 如要下载源代码,请到我的网站,地址:http://www.vczx.com/article/show.php?id=1796 简 介 我的第一个 ...

  2. java websocket netty_用SpringBoot集成Netty开发一个基于WebSocket的聊天室

    前言 基于SpringBoot,借助Netty控制长链接,使用WebSocket协议做一个实时的聊天室. 项目效果 项目统一登录路径:http://localhost:8080/chat/netty ...

  3. 【Vaadin教程】利用IDEA开发基于Vaadin网络聊天室程序

    为什么80%的码农都做不了架构师?>>>    利用Vaadin,我们可以轻松的开发出丰富的web界面,像写Swing一样写GUI,感觉什么extjs之类的弱爆了!下面是我做的一个网 ...

  4. php3.2搭建临时聊天系统,基于swoole搭建聊天室程序

    1. 创建websocket服务器 swoole从1.7.9版本开始, 内置了websocket服务器功能,我们只需几行简单的PHP代码,就可以创建出一个异步非阻塞多进程的WebSocket服务器. ...

  5. 编写一个基于控制台的购书系统实现购书功能

    输出图书信息,提示购买数量,输出顾客订单信息 import java.util.Scanner;class Book{private int id; //IBMprivate String name; ...

  6. java udp简单聊天程序_Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些java方面的技术应用. 今天没什么事做,基于udp协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对java网络编程方面的一个简单回忆. ...

  7. java udp 聊天室_Java基于UDP协议实现简单的聊天室程序

    最近比较闲,一直在抽空回顾一些Java方面的技术应用. 今天没什么事做,基于UDP协议,写了一个非常简单的聊天室程序. 现在的工作,很少用到socket,也算是对Java网络编程方面的一个简单回忆. ...

  8. 基于python的聊天室_Python实现文字聊天室

    你是否想过用所学的Python开发一个图形界面的聊天室程序啊? 像这样的: image 如果你想开发这样一个有点怀旧风格的聊天程序,那么可以接着看: 要开发这个聊天程序,你需要具备以下知识点: asy ...

  9. 需要求三个长方体的体积,请编写一个基于对象的程序。

    // 121218 第八章习题6.cpp : 定义控制台应用程序的入口点. // /* * Copyright (c) 2012, 烟台大学计算机学院 * All rights reserved. * ...

  10. C语言输出长方柱的体积,需要求3个长方柱的体积,请编写一个基于对象的程序。数据成员包括length(长)、width(宽)、 height(高)。要求用成员函数实现以下功能...

    需要求3个长方柱的体积,请编写一个基于对象的程序.数据成员包括length(长).width(宽). height(高).要求用成员函数实现以下功能: (1) 由键盘分别输入3个长方柱的长.宽.高: ...

最新文章

  1. gcc 自动识别的文件扩展名,gcc/g++ -x 选项指定语言,不同 gcc 版本 -std 编译选项支持列表
  2. MySQL查询时构建自增ID
  3. 【C++基础】自定义异常类与多重捕获
  4. ubuntu14.04安装hadoop2.7.1伪分布式和错误解决
  5. 10份数据中台资料分享(附下载)
  6. 开源字体不香吗?五款 GitHub 上的爆红字体任君选
  7. 01.php面向对象
  8. WebSocket+HTML5实现在线聊天室
  9. 【更新】ReSharper v2018.3发布
  10. 写给准备看CCNA题库的朋友们 希望有些帮助
  11. 下一代半导体表面清洁技术
  12. 窗宽窗位与其处理方法
  13. 自定义插件——zBox
  14. html规范eml文件,eml 文件头解析
  15. smartbi服务器缓存文件,导出资源 - Smartbi V10帮助中心 -
  16. 常见数据库id号编码
  17. 量子计算(十七):量子计算机硬件
  18. Java汉字转拼音工具类(支持首字母和全拼)
  19. UML面向对象分析与建模
  20. 解决vc6卡死的办法就是打上官方的原版VC6sp6补丁|VS6sp6补丁

热门文章

  1. 一篇 JPA 总结
  2. 微信小程序组件解读和分析:十二、picker滚动选择器
  3. Codeforces #345 Div.1
  4. Swift - 高级运算符介绍
  5. JAVA的反射机制原理
  6. 【转】我应该直接学Swift还是Objective-C?
  7. PHP undefined index的几种解决方法
  8. 哎呀!可能有弹出式窗口拦截器生成Gmail无法打开该网页。如果您使用弹出式窗口拦截器,请将其关闭以便打开窗口。...
  9. LeetCode:路径总和II【113】
  10. 【很好的分享】zookeeper系列