阻塞模式下socket多线程通信

来源:互联网 发布:淘宝怎么报名活动 编辑:程序博客网 时间:2024/06/01 07:51

客户端创建线程负责收发数据,其余操作封装在函数中,方便其他应用程序调用。服务端创建了两个线程,一个用于收发数据,对于接收连接accept部分开一个线程,这样主窗口就不会因阻塞而挂掉。( 参考资料:《Windows API开发详解——函数、接口、编程实例》第十四章)

客户端client.c


/* 头文件 */#include <stdio.h>#include "winsock2.h"#include <conio.h>/* 常量 */#define RECV_BUFFER_SIZE 8192// 变量定义SOCKADDR_IN clientService;// 地址SOCKET ConnectSocket;// socketWSADATA wsaData;// 库HANDLE hThread;char sendbuf[32] = "get information";// 默认发送的数据void SendStr(LPSTR sendbuf,SOCKET socket1);void InitSocket();void CreateMyThread();DWORD WINAPI CommunicationThread(LPVOID lpParameter);/************************************** CommunicationThread* 功能用于接收和发送数据的线程*为每一个连接的客户端创建一个接收发送数据的线程,*可以使用多个客户端同时连接到服务端* 参数lpParameter,SOKCET**************************************/DWORD WINAPI CommunicationThread(LPVOID lpParameter){// 获得参数sokcetSOCKET socket = (SOCKET)lpParameter;// 为接收数据分配空间LPSTR szRequest = HeapAlloc(GetProcessHeap(),0,RECV_BUFFER_SIZE);int iResult = 0;int bytesSent;// 用于保存send的返回值,实际发送的数据的大小while(1){// 接收数据iResult = recv(socket, // socketszRequest, // 接收缓存RECV_BUFFER_SIZE, // 缓存大小0);// 标志if (iResult == 0)// 接收数据失败,连接已经关闭{printf("Connection closing...\n");HeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 1;}else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误{printf("recv failed: %d\n", WSAGetLastError());HeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 1;}else if (iResult > 0) // 接收数据成功{// 显示接收到的数据printf("Bytes received: %d\tContent: %s\n",iResult,szRequest);// 如果接收到的数据是"download file"if (lstrcmpi(szRequest, "start listen") == 0){SendStr("COMING",socket);}// 如果接收到的数据是"get information"else if (lstrcmpi(szRequest, "YEAH") == 0){SendStr("123",socket);}else// 收到未知数据{printf ("unreferenced request\n");}}}// 释放接收数据缓存,关闭socketHeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 0;}/************************************** main* 功能 socket通信客户端**************************************/void main(int argc, char* argv[]){InitSocket();CreateMyThread();SendStr("request listen",ConnectSocket);getch();WaitForSingleObject( hThread,    // handle to mutexINFINITE);  // no time-out intervalWSACleanup();return;}void InitSocket(){// 初始化socket库,保存ws2_32.dll已经加载int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);if (iResult != NO_ERROR)printf("Error at WSAStartup()\n");// 创建socketConnectSocket = socket(AF_INET, // IPv4SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信IPPROTO_TCP// 使用TCP协议);if (ConnectSocket == INVALID_SOCKET){printf("Error at socket(): %ld\n", WSAGetLastError());WSACleanup();return;}// 设置服务端的通信协议、IP地址、端口clientService.sin_family = AF_INET;clientService.sin_addr.s_addr = inet_addr( "222.31.66.209" );clientService.sin_port = htons( 10000 );// 连接到服务端if ( connect(ConnectSocket, // socket(SOCKADDR*) &clientService, // 地址sizeof(clientService) // 地址的大小) == SOCKET_ERROR){printf( "Failed to connect(%d)\n",WSAGetLastError() );WSACleanup();return;}}void SendStr(LPSTR sendbuf,SOCKET socket1){int bytesSent;// 准备发送数据// 如果输入参数是-d,那么发送的数据是“download file”否则是"get information"// 向服务端发送数据bytesSent = send( socket1, // socketsendbuf,// 发送的数据 lstrlen(sendbuf)+1,// 数据长度0 );// 无标志if(bytesSent == SOCKET_ERROR){printf( "send error (%d)\n", WSAGetLastError());closesocket(socket1);return;}printf( "Bytes Sent: %ld\t Content: %s\n", bytesSent,sendbuf);}void CreateMyThread(){// 为每一个连接创建一个数据发送的接收线程,// 使服务端又可以立即接收其他客户端的连接hThread = CreateThread(NULL,0,CommunicationThread, // 线程函数(LPVOID)ConnectSocket, // 将socket作为参数0,NULL);if(!hThread){printf("Create Thread error (%d)", GetLastError());}}









服务端 server.c


/* 头文件 */#include <winsock2.h>#include <ws2tcpip.h>#include <stdio.h>/* 常量 */#define DEFAULT_PORT "10000" // 端口#define MAX_REQUEST 1024 // 接收数据的缓存大小#define BUF_SIZE 4096 // 发送数据的缓存大小WSADATA wsaData;SOCKET ListenSocket = INVALID_SOCKET;// 监听socketSOCKET ClientSocket = INVALID_SOCKET;// 连接socketstruct addrinfo *result = NULL,hints;int iResult;// 保存返回结果void InitSocket();DWORD WINAPI AcceptThead();void SendStr(LPSTR sendbuf,SOCKET socket);/************************************** CommunicationThread* 功能用于接收和发送数据的线程*为每一个连接的客户端创建一个接收发送数据的线程,*可以使用多个客户端同时连接到服务端* 参数lpParameter,SOKCET**************************************/DWORD WINAPI CommunicationThread(LPVOID lpParameter){// 获得参数sokcetSOCKET socket = (SOCKET)lpParameter;// 为接收数据分配空间LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MAX_REQUEST);int iResult;int bytesSent;// 用于保存send的返回值,实际发送的数据的大小while(1){// 接收数据iResult = recv(socket, // socketszRequest, // 接收缓存MAX_REQUEST, // 缓存大小0);// 标志if (iResult == 0)// 接收数据失败,连接已经关闭{printf("Connection closing...\n");HeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 1;}else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误{printf("recv failed: %d\n", WSAGetLastError());HeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 1;}else if (iResult > 0) // 接收数据成功{// 显示接收到的数据printf("Bytes received: %d\tContent: %s\n",iResult, szRequest);// 如果接收到的数据是"download file"if (lstrcmpi(szRequest, "request listen") == 0){SendStr("start listen",socket);}// 如果接收到的数据是"get information"else if (lstrcmpi(szRequest, "COMING") == 0){// 发送数据SendStr("YEAH",socket);}else if (lstrcmpi(szRequest, "123") == 0){SendStr("333",socket);}else if (lstrcmpi(szRequest, "close") == 0){break;}else// 收到未知数据{printf ("unreferenced request\n");}}}// 释放接收数据缓存,关闭socketHeapFree(GetProcessHeap(), 0 ,szRequest);closesocket(socket);return 0;}/************************************** int __cdecl main(void)* 功能socket服务端**************************************/int __cdecl main(void){InitSocket();AcceptThead();// 循环退出,释放DLL。WSACleanup();return 0;}void InitSocket(){// 初始化Winsock,保证Ws2_32.dll已经加载iResult = WSAStartup(MAKEWORD(2,2), &wsaData);if (iResult != 0){printf("WSAStartup failed: %d\n", iResult);return 1;}// 地址ZeroMemory(&hints, sizeof(hints));hints.ai_family = AF_INET;hints.ai_socktype = SOCK_STREAM;hints.ai_protocol = IPPROTO_TCP;hints.ai_flags = AI_PASSIVE;// 获取主机地址,保证网络协议可用等iResult = getaddrinfo(NULL, // 本机DEFAULT_PORT, // 端口&hints, // 使用的网络协议,连接类型等&result);// 结果if ( iResult != 0 ){printf("getaddrinfo failed: %d\n", iResult);WSACleanup();return 1;}// 创建socket,用于监听ListenSocket = socket(result->ai_family, // 网络协议,AF_INET,IPv4result->ai_socktype, // 类型,SOCK_STREAMresult->ai_protocol);// 通信协议,TCPif (ListenSocket == INVALID_SOCKET){printf("socket failed: %ld\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();return 1;}// 绑定到端口iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);if (iResult == SOCKET_ERROR){printf("bind failed: %d\n", WSAGetLastError());freeaddrinfo(result);closesocket(ListenSocket);WSACleanup();return 1;}printf("bind\n");freeaddrinfo(result);// reuslt不再使用// 开始监听iResult = listen(ListenSocket, SOMAXCONN);printf("start listen......\n");if (iResult == SOCKET_ERROR){printf("listen failed: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}if(!CreateThread(NULL,0,AcceptThead, // 线程函数(LPVOID)ClientSocket, // 将socket作为参数0,NULL)){printf("Create Thread error (%d)", GetLastError());}}DWORD WINAPI AcceptThead(){while (1){// 接收客户端的连接,accept函数会等待,直到连接建立printf("ready to accept\n");ClientSocket = accept(ListenSocket, NULL, NULL);// accept函数返回,说明已经有客户端连接// 返回连接socketprintf("accept a connetion\n");if (ClientSocket == INVALID_SOCKET){printf("accept failed: %d\n", WSAGetLastError());closesocket(ListenSocket);break;// 等待连接错误,退出循环}// 为每一个连接创建一个数据发送的接收线程,// 使服务端又可以立即接收其他客户端的连接if(!CreateThread(NULL,0,CommunicationThread, // 线程函数(LPVOID)ClientSocket, // 将socket作为参数0,NULL)){printf("Create Thread error (%d)", GetLastError());break;}}}void SendStr(LPSTR sendbuf,SOCKET socket){int bytesSent;// 准备发送数据// 如果输入参数是-d,那么发送的数据是“download file”否则是"get information"// 向服务端发送数据bytesSent = send( socket, // socketsendbuf,// 发送的数据 lstrlen(sendbuf)+1,// 数据长度0 );// 无标志if(bytesSent == SOCKET_ERROR){printf( "send error (%d)\n", WSAGetLastError());closesocket(socket);return;}printf( "Bytes Sent: %ld\tContent: %s\n", bytesSent,sendbuf );}