阻塞模式下socket多线程通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/youkawa/article/details/7577972

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

客户端client.c


/* 头文件 */
#include <stdio.h>
#include "winsock2.h"
#include <conio.h>
/* 常量 */
#define RECV_BUFFER_SIZE 8192

// 变量定义
SOCKADDR_IN clientService;// 地址
SOCKET ConnectSocket;// socket
WSADATA 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
	)
{

	// 获得参数sokcet
	SOCKET socket = (SOCKET)lpParameter;
	// 为接收数据分配空间
	LPSTR szRequest = HeapAlloc(GetProcessHeap(),0,RECV_BUFFER_SIZE);
	int iResult = 0;
	int bytesSent;// 用于保存send的返回值,实际发送的数据的大小
	while(1)
	{
		// 接收数据
		iResult = recv(socket, // socket
			szRequest, // 接收缓存
			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");
			}
		}
	}
	// 释放接收数据缓存,关闭socket
	HeapFree(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 mutex
		INFINITE);  // no time-out interval
	WSACleanup();

	return;
}



void InitSocket()
{

	// 初始化socket库,	保存ws2_32.dll已经加载
	int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
	if (iResult != NO_ERROR)
		printf("Error at WSAStartup()\n");

	// 创建socket
	ConnectSocket = socket(AF_INET, // IPv4
		SOCK_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, // socket
		sendbuf,// 发送的数据 
		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;// 监听socket
SOCKET ClientSocket = INVALID_SOCKET;// 连接socket
struct 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
	)
{

	// 获得参数sokcet
	SOCKET socket = (SOCKET)lpParameter;
	// 为接收数据分配空间
	LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MAX_REQUEST);
	int iResult;
	int bytesSent;// 用于保存send的返回值,实际发送的数据的大小
	while(1)
	{
		// 接收数据
		iResult = recv(socket, // socket
			szRequest, // 接收缓存
			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");
			}
		}
	}
	// 释放接收数据缓存,关闭socket
	HeapFree(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,IPv4
		result->ai_socktype, // 类型,SOCK_STREAM
		result->ai_protocol);// 通信协议,TCP
	if (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函数返回,说明已经有客户端连接
		// 返回连接socket
		printf("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, // socket
		sendbuf,// 发送的数据 
		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 );
}
 
 
 

猜你喜欢

转载自blog.csdn.net/youkawa/article/details/7577972