一、协议设计
使用流套接字(SOCK_STREAM),流套接字用于面向连接,可靠的数据传输服务。它使用了传输控制协议TCP。在聊天的程序中需要一个服务器一个客户端。
在会话进行的流程中,服务器端先创建套接字(socket())再将其与ip地址和端口绑定(bind()),然后开始监听listen(),再使用accept()阻塞进程直到有客户端连接。客户端创建套接字socket(),再使用connect()建立连接客户端,wirte()请求数据,然后服务器read()接收请求,服务器处理请求之后write()回应数据,客户端用read()接收数据如果数据中没有退出信息,则再回到请求数据的步骤。若有退出指令则客户端退出close()关闭连接然后服务器read()后close()或者服务器先close()关闭连接然后客户端read()后close()。
流程如下图所示:
在通话开始前,用户端和服务器交换彼此的名字开始通话,由客户端先开始发言,完毕后输入over服务器才能发消息回客户端。之后客户端也是在服务器端输入over才能发言。双方任意一方均可输入“exit“来结束对话退出程序。
二、代码实现
//server.cpp
1. #include<iostream>
2. #include<winsock.h>
3. #include<string.h>
4. #pragma comment(lib, "ws2_32.lib")
5. //加载ws2_32.lib库
6. using namespace std;
7. int main() {
8.
9. WSADATA wsaData; //存放被WSAStartup函数调用后返回的Windows Sockets数据的数据结构
10. WSAStartup(MAKEWORD(2, 2), &wsaData);//声明使用socket2.2版本
11. //创建套接字
12. SOCKET ServerSocket;
13. //地址类型为AD_INET,服务类型为流式(SOCK_STREAM),协议采用TCP
14. ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
15. if (ServerSocket == INVALID_SOCKET)
16. {
17. cout << "套接字创建失败";
18. WSACleanup();
19. return 0;
20. }
21.
22. SOCKADDR_IN ServerAddr;
23. ServerAddr.sin_family = AF_INET; //指定IP格式
24. USHORT uPort = 8888; //服务器监听端口
25. ServerAddr.sin_port = htons(uPort); //绑定端口号
26. ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
27. if (bind(ServerSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR) //建立捆绑
28. {
29. cout << "绑定失败";
30. closesocket(ServerSocket);
31. return 0;
32. }
33. char server_name[32] = { 0 };
34. cout << "请输入你的名字:";
35. cin.getline(server_name,32);
36.
37. /*开始监听*/
38. listen(ServerSocket, 1);
39.
40. /*等待连接*/
41. char client_name[32] = { 0 };
42. SOCKET ClientSocket;
43. SOCKADDR_IN ClientAddr;
44. int ClientAddrlen = sizeof(ClientAddr);
45. ClientSocket = accept(ServerSocket,(SOCKADDR*)&ClientAddr,&ClientAddrlen);
46. cout << "等待连接...\n";
47. if (ClientSocket == INVALID_SOCKET)
48. {
49. cout << "客户端发出请求,服务器接收请求失败:\n" << WSAGetLastError();
50. closesocket(ServerSocket);
51. WSACleanup();
52. return 0;
53. }
54. else
55. {
56. cout << "客服端与服务器建立连接成功:\n" ;
57. }
58. char buffer[1024] = { 0 };
59. int relen = 0;
60. int selen = 0;
61. /*通过建立的连接进行通信*/
62. //发送和接受客户端与服务端的名字
63. selen=send(ClientSocket, server_name, strlen(server_name), 0);
64. relen=recv(ClientSocket, client_name, sizeof(client_name), 0);
65. client_name[relen] = '\0';
66. while (1)
67. {
68. while (1)
69. {
70. memset(buffer, 0, sizeof(buffer));//在每次交互前,把之前的buf数据清空,避免缓冲区冗余
71. relen = recv(ClientSocket, buffer, sizeof(buffer), 0);
72. /*buffer[relen] = '\0';*/
73. //接受消息
74. if (strcmp(buffer, "exit") == 0)//输入消息含有exit便退出
75. {
76. cout << "程序将在3秒后退出" << endl;
77. Sleep(3000);
78. closesocket(ServerSocket);
79. closesocket(ClientSocket);
80. WSACleanup();
81. return 0;
82. }
83. else if (strcmp(buffer, "over") == 0)
84. {
85. break;
86. }
87. cout << client_name << ": ";
88. cout << buffer << endl;
89. }
90. while (1) {
91. memset(buffer, 0, sizeof(buffer));
92. cout << server_name << ": ";
93. cin.getline(buffer,1024);
94. //发出消息
95. if (strcmp(buffer, "over") == 0)
96. {
97. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
98. break;
99. }
100. else if(strcmp(buffer, "exit") == 0)
101. {
102. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
103. cout << "程序将在3秒后退出" << endl;
104. Sleep(3000);
105. closesocket(ServerSocket);
106. closesocket(ClientSocket);
107. WSACleanup();
108. return 0;
109. }
110. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
111. }
112. }
113. }
客户端
\\client.cpp
114. #include<iostream>
115. #include<winsock.h>
116. #include<string.h>
117. #pragma comment(lib, "ws2_32.lib")
118. //加载ws2_32.lib库
119. using namespace std;
120. int main() {
121.
122. WSADATA wsaData; //存放被WSAStartup函数调用后返回的Windows Sockets数据的数据结构
123. WSAStartup(MAKEWORD(2, 2), &wsaData);//声明使用socket2.2版本
124. //创建套接字
125. SOCKET ClientSocket;
126. //地址类型为AD_INET,服务类型为流式(SOCK_STREAM),协议采用TCP
127. ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
128. if (ClientSocket == INVALID_SOCKET)
129. {
130. cout << "套接字创建失败";
131. WSACleanup();
132. return 0;
133. }
134.
135. SOCKADDR_IN ServerAddr;
136. ServerAddr.sin_family = AF_INET; //指定IP格式
137. USHORT uPort = 8888; //服务器监听端口
138. ServerAddr.sin_port = htons(uPort); //绑定端口号
139. ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
140. char client_name[32] = { 0 };
141. cout << "请输入你的名字:";
142. cin.getline( client_name,32);
143. connect(ClientSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
144. cout << "连接成功" << endl;
145.
146. /*等待连接*/
147. char server_name[32] = { 0 };
148. char buffer[1024] = { 0 };
149. int relen = 0;
150. int selen = 0;
151. /*通过建立的连接进行通信*/
152. //发送和接受客户端与服务端的名字
153. selen = send(ClientSocket, client_name, strlen(client_name), 0);
154. relen = recv(ClientSocket, server_name, sizeof(server_name), 0);
155. /*server_name[relen] = '\0';*/
156. while (1)
157. {
158. while (1) {
159. cout << client_name << ": ";
160. memset(buffer, 0, sizeof(buffer));
161. cin.getline(buffer,sizeof(buffer));
162. if (strcmp(buffer, "exit") == 0)
163. {
164. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
165. cout << "程序将在3秒后退出" << endl;
166. Sleep(3000);
167. closesocket(ClientSocket);
168. WSACleanup();
169. return 0;
170. }
171. else if (strcmp(buffer, "over") == 0)
172. {
173. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
174. break;
175. }
176. selen = send(ClientSocket, buffer, sizeof(buffer), 0);
177. }
178. while (1) {
179. memset(buffer, 0, sizeof(buffer));//在每次交互前,把之前的buf数据清空,避免缓冲区冗余
180. relen = recv(ClientSocket, buffer, sizeof(buffer), 0);
181. //buffer[relen] = '\0';//接受消息
182. if (strcmp(buffer, "exit") == 0)//输入消息含有exit便退出
183. {
184. cout << buffer <<"程序将在3秒后退出" <<endl;
185. Sleep(3000);
186. closesocket(ClientSocket);
187. WSACleanup();
188. return 0;
189. }
190. else if (strcmp(buffer, "over") == 0)
191. {
192. break;
193. }
194. cout << server_name << ": ";
195. cout << buffer << endl;
196. }
197. }
198. }
199