基于visual c++之windows核心编程代码分析(10)实现socket通信分类:VC++编程技术Visual C++2010编程技术Visual Studio2012 Windows8 2011-12-17 11:32 120人阅读评论(0) 收藏举报在多台计算机之间实现通信,最常见的方法有两种:Socket通信与UDP通信。
Socket是一种基于TCP/IP协议,建立稳定连接的点对点通信,它的特点是安全性高,数据不会丢失,但是很占系统资源。
在JAVA中,ServerSocket类和Socket类为我们实现了Socket 通信,建立通信的一般步骤是:1。
建立服务器ServerSocket ss = new ServerSocket(端口号);Socket socket = ss.accept();这样,我们就已经建立了服务器,其中accept()方法会阻塞,知道有客户发送一个连接请求,我们可以通过socket.getInputStream()和socket.getOutputStream()来获得输入输出流,如调用socket.getInputStream()获得一个输入流,实际上这个流就是连接对方的一个输出流,流的操作与文件流操作相同,我们可以用操作文件的方法来操作它们。
2。
建立客户端Socket socket = new Socket(主机名,端口号)客户端只需这一句代码就可以与服务器取得连接,这里的主机名应为服务器的IP地址,端口号是服务器用来监听该程序的端口,同样可以通过socket.getInputStream()和socket.getOutputStream()来获得输入输出流。
在以上程序中,已经实现了一个最简单的客户端和服务器的通信。
但是,还有一些问题。
首先,这个通信只执行一次,程序就将结束。
因为我们只读了一次输入流,如果想要建立客户与服务器之间的稳定的会话,就要用到多线程:Thread thread = new Thread(new Sender());thread.start();InputStream input = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(input));while(true){br.readLine();}其中,Sender是一个实现了Runnable接口的类,用它来专门负责发送数据,主线程只需要不听地接受数据就行。
Sender类中的run()方法应该具有以下代码:PrintWriter pw = newPrintWriter(socket.getOutputStream());while(true){pw.println(数据);}即使按上面的方式做了,程序还是有问题,因为它只能在一个时间内为一个客户服务,不能同时为多个客户服务,如多要想同时为多个客户服务,服务器应具有类似以下的代码:ServerSocker ss = new ServerSocker(端口号);socket = null;while(true){socket = ss.accept();Thread thread1 = new Thread(new Sender());thread1.start();Thread thread2 = new Thread(new Receiver());thread2.start();}在这里,新开启了2个线程分别负责接收和发送。
Receiver是一个与Sender非常相似的类,它主要用来接收数据。
在客户端,我们同样应开启2个线程:Socket socket = new Socket(服务器IP,端口号);Sender sender = new Sender(socket);Thread thread1 = new Thread(sender);thread1.start();Receiver receiver = new Receiver(socket);Thread thread2 = new Thread(receiver);thread2.start();我们来亲自动手实现案例实现客户端view plaincopy to clipboardprint?1. /* 头文件 */2. #include <stdio.h>3. #include "winsock2.h"4. /* 常量 */5. #define RECV_BUFFER_SIZE 81926.7. /*************************************8. * main9. * 功能 socket通信客户端10. **************************************/11. void main(int argc, char* argv[])12. {13. // 变量定义14. SOCKADDR_IN clientService;// 地址15. SOCKET ConnectSocket;// socket16. WSADATA wsaData;// 库17. LPVOID recvbuf;// 接收缓存18. int bytesSent;19. int bytesRecv = 0;20. char sendbuf[32] = "get information";// 默认发送的数据21.22. // 初始化socket库,保存ws2_32.dll已经加载23. int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);24. if (iResult != NO_ERROR)25. printf("Error at WSAStartup()\n");26.27. // 创建socket28. ConnectSocket = socket(AF_INET, // IPv429. SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信30. IPPROTO_TCP// 使用TCP协议31. );32. if (ConnectSocket == INVALID_SOCKET)33. {34. printf("Error at socket(): %ld\n", WSAGetLastE35. WSACleanup();36. return;37. }38.39. // 设置服务端的通信协议、IP地址、端口40. clientService.sin_family = AF_INET;41. clientService.sin_addr.s_addr = inet_addr( "127.0.42. clientService.sin_port = htons( 10000 );43.44. // 连接到服务端45. if ( connect(46. ConnectSocket, // socket47. (SOCKADDR*) &clientService, // 地址48. sizeof(clientService) // 地址的大小49. ) == SOCKET_ERROR)50. {51. printf( "Failed to connect(%d)\n",WSAGetLastEr52. WSACleanup();53. return;54. }55. // 准备发送数据56. // 如果输入参数是-d,那么发送的数据是“download file”否则是"get information"57. if(argc ==2 && (!lstrcmp(argv[1], "-d")))58. {59. lstrcpyn(sendbuf, "download file", 32);60. }61. // 向服务端发送数据62. bytesSent = send( ConnectSocket, // socket63. sendbuf,// 发送的数据64. lstrlen(sendbuf)+1,// 数据长度65. 0 );// 无标志66.67. if(bytesSent == SOCKET_ERROR)68. {69. printf( "send error (%d)\n", WSAGetLastError()70. closesocket(ConnectSocket);71. return;72. }73. printf( "Bytes Sent: %ld\n", bytesSent );74.75. // 准备接收数据76. recvbuf = HeapAlloc(GetProcessHeap(), 0, RECV_BUFF77. // 循环接收78. while( bytesRecv != SOCKET_ERROR )79. {80. //Sleep(50);81. bytesRecv = recv( ConnectSocket, // socket82. recvbuf, // 接收数据缓存83. RECV_BUFFER_SIZE,// 缓存大小84. 0 );// 无标志85. if ( bytesRecv == 0 )86. {87. printf( "Connection Closed.\n");88. break;89. }90. // TODO,处理接收的数据,这里只简单的将收到的数据大小显示91. printf( "Bytes Recv: %ld\n", bytesRecv );92. }93. HeapFree(GetProcessHeap(), 0, recvbuf);94. WSACleanup();95. return;96. }实现服务端view plaincopy to clipboardprint?1. /* 头文件 */2. #include <winsock2.h>3. #include <ws2tcpip.h>4. #include <stdio.h>5. /* 常量 */6. #define DEFAULT_PORT "10000" // 端口7. #define MAX_REQUEST 1024 // 接收数据的缓存大小8. #define BUF_SIZE 4096 // 发送数据的缓存大小9.10. /*************************************11. * CommunicationThread12. * 功能用于接收和发送数据的线程13. * 为每一个连接的客户端创建一个接收发送数据的线程,14. * 可以使用多个客户端同时连接到服务端15. * 参数 lpParameter,SOKCET16. **************************************/17. DWORD WINAPI CommunicationThread(18. LPVOID lpParameter19. )20. {21. DWORD dwTid = GetCurrentThreadId();22. // 获得参数sokcet23. SOCKET socket = (SOCKET)lpParameter;24. // 为接收数据分配空间25. LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MA26. int iResult;27. int bytesSent;// 用于保存send的返回值,实际发送的数据的大小28.29. // 接收数据30. iResult = recv(socket, // socket31. szRequest, // 接收缓存32. MAX_REQUEST, // 缓存大小33. 0);// 标志34. if (iResult == 0)// 接收数据失败,连接已经关闭35. {36. printf("Connection closing...\n");37. HeapFree(GetProcessHeap(), 0 ,szRequest);38. closesocket(socket);39. return 1;40. }41. else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误42. {43. printf("recv failed: %d\n", WSAGetLastError())44. HeapFree(GetProcessHeap(), 0 ,szRequest);45. closesocket(socket);46. return 1;47. }48. else if (iResult > 0) // 接收数据成功49. {50. // 显示接收到的数据51. printf("\tCommunicationThread(%d)\tBytes recei52. printf("\tCommunicationThread(%d)\trequest str53.54. // 如果接收到的数据是"download file"55. if (lstrcmpi(szRequest, "download file") == 0)56. {57. // 读取文件download.txt将发送58. HANDLE hFile;59. LPVOID lpReadBuf; // 发送缓存60. DWORD dwBytesRead;61. DWORD dwFileSize;62. DWORD dwSendFile = 0;63. hFile = CreateFile("download.txt",64. GENERIC_READ,65. FILE_SHARE_READ,66. NULL,67. OPEN_EXISTING,68. FILE_ATTRIBUTE_NORMAL,69. NULL);70.71. if (hFile == INVALID_HANDLE_VALUE)72. {73. printf("\tCommunicationThread\tCould n74. GetLastError());75. send(socket, "error", 6, 0);76. closesocket(socket);77. return 1;78. }79. // 分配发送数据缓存80. lpReadBuf = HeapAlloc(GetProcessHeap(), 081. // 获取文件大小82. dwFileSize = GetFileSize(hFile, NULL);83. // 循环发送84. while(1)85. {86. // 读文件到缓存87. if(!ReadFile(hFile, lpReadBuf, BUF_SIZ88. {89. printf("\tCommunicationThread\tCou90. GetLastError());91. closesocket(socket);92. CloseHandle(hFile);93. return 1;94. }95. // 发送读取的文件数据96. bytesSent = send(socket, lpReadBuf, dw97. if( bytesSent == SOCKET_ERROR)98. {99. printf("\tCommunicationThread\tsen 100. WSAGetLastError());101. closesocket(socket);102. CloseHandle(hFile);103. return 1;104. }105. // 显示发送数据的大小106. printf("\tCommunicationThread(%d)\tsen 107. // 累加,已经发送的大小108. dwSendFile += dwBytesRead;109. // 如果所有文件数据都已经发送110. if(dwSendFile == dwFileSize)111. {112. printf("\tCommunicationThread\tFil 113. break;// 退出循环114. }115. }116. // 释放内存、关闭连接,关闭文件117. HeapFree(GetProcessHeap(), 0 , lpReadBuf); 118. CloseHandle(hFile);119. closesocket(socket);120. }121. // 如果接收到的数据是"get information"122. else if (lstrcmpi(szRequest, "get information" 123. {124. // 发送数据125. bytesSent = send(socket, // socket126. "this is information", // 数据127. lstrlen("this is information")+1, // 数据长度128. 0);// 标志129. // 判断是否成功130. if( bytesSent == SOCKET_ERROR)131. {132. printf("\tCommunicationThread\tsend er 133. WSAGetLastError());134. closesocket(socket);135. return 1;136. }137. printf("\tCommunicationThread(%d)\tsend %d 138. }139. else// 收到未知数据140. {141. printf ("unreferenced request\n"); 142. }143. }144. // 释放接收数据缓存,关闭socket145. HeapFree(GetProcessHeap(), 0 ,szRequest); 146. closesocket(socket);147. return 0;148. }149.150. /*************************************151. * int __cdecl main(void)152. * 功能 socket服务端153. **************************************/154. int __cdecl main(void)155. {156. WSADATA wsaData;157. SOCKET ListenSocket = INVALID_SOCKET;// 监听socket158. SOCKET ClientSocket = INVALID_SOCKET;// 连接socket159. struct addrinfo *result = NULL,160. hints;161. int iResult;// 保存返回结果162.163. // 初始化Winsock,保证Ws2_32.dll已经加载164. iResult = WSAStartup(MAKEWORD(2,2), &wsaData); 165. if (iResult != 0)166. {167. printf("WSAStartup failed: %d\n", iResult); 168. return 1;169. }170. // 地址171. ZeroMemory(&hints, sizeof(hints));172. hints.ai_family = AF_INET;173. hints.ai_socktype = SOCK_STREAM;174. hints.ai_protocol = IPPROTO_TCP;175. hints.ai_flags = AI_PASSIVE;176.177. // 获取主机地址,保证网络协议可用等178. iResult = getaddrinfo(NULL, // 本机179. DEFAULT_PORT, // 端口180. &hints, // 使用的网络协议,连接类型等181. &result);// 结果182. if ( iResult != 0 )183. {184. printf("getaddrinfo failed: %d\n", iResult); 185. WSACleanup();186. return 1;187. }188.189. // 创建socket,用于监听190. ListenSocket = socket(191. result->ai_family, // 网络协议,AF_INET,IPv4192. result->ai_socktype, // 类型,SOCK_STREAM193. result->ai_protocol);// 通信协议,TCP194. if (ListenSocket == INVALID_SOCKET)195. {196. printf("socket failed: %ld\n", WSAGetLastError 197. freeaddrinfo(result);198. WSACleanup();199. return 1;200. }201. // 绑定到端口202. iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);203. if (iResult == SOCKET_ERROR)204. {205. printf("bind failed: %d\n", WSAGetLastError()) 206. freeaddrinfo(result);207. closesocket(ListenSocket);208. WSACleanup();209. return 1;210. }211. printf("bind\n");212.213. freeaddrinfo(result);// reuslt不再使用214.215. // 开始监听216. iResult = listen(ListenSocket, SOMAXCONN); 217. printf("start listen......\n");218. if (iResult == SOCKET_ERROR)219. {220. printf("listen failed: %d\n", WSAGetLastError( 221. closesocket(ListenSocket);222. WSACleanup();223. return 1;224. }225. while (1)226. {227. // 接收客户端的连接,accept函数会等待,直到连接建立228. printf("ready to accept\n");229. ClientSocket = accept(ListenSocket, NULL, NULL 230. // accept函数返回,说明已经有客户端连接231. // 返回连接socket232. printf("accept a connetion\n");233. if (ClientSocket == INVALID_SOCKET)234. {235. printf("accept failed: %d\n", WSAGetLastEr 236. closesocket(ListenSocket);237. break;// 等待连接错误,退出循环238. }239. // 为每一个连接创建一个数据发送的接收线程,240. // 使服务端又可以立即接收其他客户端的连接241. if(!CreateThread(242. NULL,243. 0,244. CommunicationThread, // 线程函数245. (LPVOID)ClientSocket, // 将socket 作为参数246. 0,247. NULL))248. {249. printf("Create Thread error (%d)", GetLast 250. break;251. }252. }253. // 循环退出,释放DLL。