Error[8]: Undefined offset: 1238, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

使用多线程来实现可与多个客户端通信的服务端。


当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。

服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。


另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。

服务端

注意下多线程下共享内存,还有printf的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")


/* 服务端相关配置 */
#define SERVER_IP		"127.0.0.1"	//服务端IPV4地址
#define SERVER_PORT		1234		//服务端端口
#define MAX_CLIENT_NUMS	50U			//最大可连接客户端数量

SOCKET serverSocket = { 0 };			//服务端套接字句柄
SOCKADDR_IN serverSocketAddr = { 0 };	//服务端套接字配置结构体
volatile unsigned int currentClientNums = { 0 };	//当前连接的客户端数量


/* 客户端相关配置 */
typedef struct
{
	char isOnline;					//客户端是否在线 1则在线  0则不在线
	SOCKET clientSocket;			//客户端SOCKET套接字句柄
	SOCKADDR_IN clientSocketAddr;	//客户端套接字配置结构体
	HANDLE threadFunc;				//客户端对应的线程句柄
}CLIENT_t, * PCLIENT_t;

CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };

void ClientThreadFunc(void*);		//客户端线程函数

#define RECEIVE_BUFF_SIZE 200U		//接收缓冲区字节大小


/* 线程同步相关 */
CRITICAL_SECTION cs_printf;//关键段(临界区)
CRITICAL_SECTION cs_currentClientNums;

int main()
{
	/* 初始化DLL */
	WSADATA wsadata = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)//版本2.2
	{
		printf("初始化DLL失败!\n");
		return 0;
	}

	/* 创建SOCKET */
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 配置服务端套接字信息 */
	serverSocketAddr.sin_family = AF_INET;//IPV4
	serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
	serverSocketAddr.sin_port = htons(SERVER_PORT);//端口 
	if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)//绑定服务端套接字
	{
		printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 服务端进入监听状态 */
	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 初始化关键段(临界区) */
	if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
	{
		printf("关键段(临界区)cs_printf初始化失败!\n");
		DeleteCriticalSection(&cs_printf);//销毁关键段
		goto END;
	}
	InitializeCriticalSection(&cs_currentClientNums);

	/* 等待客户端连接服务端 */
	printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));

	while (1)
	{
		static SOCKET clientSocket = { 0 };//客户端套接字句柄
		static SOCKADDR_IN clientSocketAddr = { 0 };//客户端套接字配置结构体
		static unsigned int cLength = sizeof(clientSocketAddr);//结构体长度
		static unsigned int temp = { 0 };

		/* 接收客户端的连接请求 */
		/*
		* 没有新的连接程序会阻塞在这,accept是阻塞式函数
		* 直到出现异常,或者有新的连接
		*/
		clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
		if (clientSocket == SOCKET_ERROR)
		{
			EnterCriticalSection(&cs_printf);//进入关键段
			printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
			LeaveCriticalSection(&cs_printf);//离开关键段
			continue;//继续等待下一次连接
		}
		else
		{
			/* 判断是否超过允许连接的客户端数量 */
			EnterCriticalSection(&cs_currentClientNums);//进入关键段
			temp = currentClientNums + 1;
			LeaveCriticalSection(&cs_currentClientNums);//离开关键段
			if (temp > MAX_CLIENT_NUMS)
			{
				send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
				EnterCriticalSection(&cs_printf);//进入关键段
				printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
				LeaveCriticalSection(&cs_printf);//离开关键段
				closesocket(clientSocket);//断开与客户端的连接
			}
			else
			{
				for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
				{
					/* 遍历哪个客户端断开连接了,如果断开连接则将相应的线程句柄关闭 */
					if (client[i].isOnline == 0)
					{
						client[i].isOnline = 1;//设置客户端在线
						client[i].clientSocket = clientSocket;//保存客户端SOCKET套接字
						memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));//保存客户端SOCKET配置结构体
						/* 创建客户端线程 */
						//注意:_endthread 会自动关闭线程句柄。

(该行为与 Win32 ExitThread API 不同。

//因此,当你使用 _beginthread 和 _endthread 时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]); EnterCriticalSection(&cs_currentClientNums);//进入关键段 EnterCriticalSection(&cs_printf);//进入关键段 currentClientNums++;//当前在线客户端数量加一 printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS); LeaveCriticalSection(&cs_printf);//离开关键段 LeaveCriticalSection(&cs_currentClientNums);//离开关键段 break; } } } } } DeleteCriticalSection(&cs_currentClientNums);//销毁关键段 END: /* 断开服务端套接字 */ closesocket(serverSocket); /* 终止DLL的调用 */ WSACleanup(wsadata); return 0; } /* 客户端线程 */ void ClientThreadFunc(void* arg) { PCLIENT_t client = (PCLIENT_t)arg; char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };//接收来自客户端的数据缓冲区 int result = { 0 };//recv函数返回值 unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);//客户端端口 IN_ADDR clientIP = client->clientSocketAddr.sin_addr;//客户端IP EnterCriticalSection(&cs_printf);//进入关键段 printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort); LeaveCriticalSection(&cs_printf);//离开关键段 /* 发送欢迎信息至客户端 */ send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0); while (1) { /* 接收来自客户端的数据 */ /* * recv函数 的实质就是从socket的缓冲区里拷贝出数据 * 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0); if (result > 0) { //添加字符串结束标志,便于用字符串输出 if (result >= RECEIVE_BUFF_SIZE) { receiveBuff[RECEIVE_BUFF_SIZE - 1] = ';'} else [ { receiveBuff]result= ';' }send ( ,,client->clientSocketstrlen receiveBuff( ),receiveBuff0) ;//数据回传EnterCriticalSection( &);cs_printf//进入关键段printf( "收到来自IP:%s,端口:%d客户端的信息:%s\n",inet_ntoa( ),clientIP,) clientPort; receiveBuffLeaveCriticalSection( &);cs_printf//离开关键段}else if ( == 0result ) //result == 0 说明客户端断开连接EnterCriticalSection( { &);cs_printf//进入关键段printf( "客户端断开连接,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }else //result < 0 说明出现异常 EnterCriticalSection( { &);cs_printf//进入关键段printf( "与客户端通信时发生异常,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }} closesocket ( );client->clientSocket//断开连接=0 client->isOnline ; //设置客户端不在线EnterCriticalSection( &);cs_currentClientNums//进入关键段--; currentClientNums//当前在线客户端数量减一LeaveCriticalSection( &);cs_currentClientNums//离开关键段_endthread( );//终止当前线程}# include

客户端
#include 
#include 
#pragma 
comment( ,"ws2_32.lib"lib)/* 服务端信息 */#


define
SERVER_IP"127.0.0.1" //服务端IPV4地址			#	define
SERVER_PORT1234 //服务端端口			=		0
SOCKADDR_IN serverSocketAddr } { ; //服务端套接字配置结构体/* 客户端相关配置 */	=

0
SOCKET clientSocket } { ; //客户端SOCKET套接字#			define
BUFF_SIZE200U //缓冲区字节大小 char					[
] sendBuff=BUFF_SIZE0 } { ; //发送缓冲区char		[
] receiveBuff=BUFF_SIZE0 } { ; //接收缓冲区void	RecevieThreadFunc
( void*);//客户端接收来自服务端的数据线程函数volatile			char
= 0 isOnline } { ; //客户端是否在线 1则在线  0则不在线/* 线程同步相关 */			;

//关键段(临界区)
CRITICAL_SECTION cs_printf;int
CRITICAL_SECTION cs_isOnlinemain


( )/* 初始化DLL */=
{
	0
	WSADATA wsadata } { ; if(
	WSAStartup (MAKEWORD(2,2) ,&) !=wsadata0 ) //版本2.2printf(
	{
		"初始化DLL失败!\n");return0
		; }/* 创建SOCKET */
	=

	socket
	clientSocket ( ,,AF_INET) SOCK_STREAM; IPPROTO_TCP//TCP套接字if(
	== )clientSocket printf INVALID_SOCKET(
	{
		"创建客户端TCP套接字失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 配置服务端套接字信息并连接服务端 */
	.

	=
	serverSocketAddr;sin_family //IPV4 AF_INET..
	serverSocketAddr.sin_addr=S_uninet_addrS_addr ( );SERVER_IP//IP地址.=
	serverSocketAddrhtonssin_port ( );SERVER_PORT//端口if(
	connect (,&clientSocket, sizeofserverSocketAddr( ))serverSocketAddr==) //连接服务端 SOCKET_ERRORprintf(
	{
		"连接服务端失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 初始化关键段(临界区) */
	if

	(
	! InitializeCriticalSectionAndSpinCount(&,4000cs_printf) )printf(
	{
		"关键段(临界区)cs_printf初始化失败!\n");DeleteCriticalSection(
		&);cs_printf//销毁关键段goto;
		} ENDInitializeCriticalSection
	(
	&);cs_isOnline/* 客户端连接上了服务端 */printf

	(
	"已经连接上服务端,服务端IP:%s,端口:%d\n",inet_ntoa( .)serverSocketAddr,sin_addrntohs( .)serverSocketAddr)sin_port;=1
	isOnline ; /* 创建接收来自服务端数据的线程 */_beginthread

	(
	,0RecevieThreadFunc, NULL) ;/* 接收用户的输入并发送至服务端 */while

	(
	1 )EnterCriticalSection(
	{
		&);cs_isOnline//进入关键段if(
		! )//判断客户端是否在线isOnlineLeaveCriticalSection(
		{
			&);cs_isOnlinegoto;
			} ENDLeaveCriticalSection
		(
		&);cs_isOnline//离开关键段EnterCriticalSection(

		&);cs_printf//进入关键段printf(
		"请输入需要发送至服务端的数据:\n");LeaveCriticalSection(
		&);cs_printf//离开关键段scanf(
		"%s",); sendBuffsend(
		,,clientSocketstrlen sendBuff( ),sendBuff0) ;//发送至服务端Sleep(
		100);}DeleteCriticalSection
	(

	&);cs_isOnline//销毁关键段:/* 断开服务端套接字 */

ENDclosesocket
	(
	);clientSocket/* 终止DLL的调用 */WSACleanup

	(
	);wsadatareturn0

	; }void
RecevieThreadFunc


( void*)int arg=
{
	0 result } { ; //recv函数返回值while(

	1 )/* 接收来自服务端的数据 */=
	{
		recv
		/*
		* recv函数 的实质就是从socket的缓冲区里拷贝出数据
		* 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result ( ,,clientSocket, receiveBuff0 BUFF_SIZE) ;if( 0 )result > //添加字符串结束标志,便于用字符串输出if { ( ) [result >= BUFF_SIZE- { receiveBuff1BUFF_SIZE ] =';' } else[ ] = { receiveBuff';'result} EnterCriticalSection (& ) ;//进入关键段printfcs_printf("收到来自服务端的信息:%s\n", );LeaveCriticalSection( receiveBuff&) ;//离开关键段}cs_printfelseif( == 0 ) //result == 0 说明服务端断开连接result EnterCriticalSection (&) { ;//进入关键段EnterCriticalSectioncs_isOnline(&) ;//进入关键段printfcs_printf("服务端断开连接\n") ;=0;LeaveCriticalSection isOnline ( &) ;//离开关键段LeaveCriticalSectioncs_printf(&) ;//离开关键段breakcs_isOnline;}else //result < 0 说明出现异常EnterCriticalSection ( &) { ;//进入关键段printfcs_printf("与服务端通信时发生异常\n") ;LeaveCriticalSection(&) ;//离开关键段breakcs_printf;}} closesocket( ) ; //断开连接_endthreadclientSocket(); //终止当前线程}[+++][+++][+++] [+++]

结果

服务端

客户端

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1239, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

使用多线程来实现可与多个客户端通信的服务端。


当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。

服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。


另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。

服务端

注意下多线程下共享内存,还有printf的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")


/* 服务端相关配置 */
#define SERVER_IP		"127.0.0.1"	//服务端IPV4地址
#define SERVER_PORT		1234		//服务端端口
#define MAX_CLIENT_NUMS	50U			//最大可连接客户端数量

SOCKET serverSocket = { 0 };			//服务端套接字句柄
SOCKADDR_IN serverSocketAddr = { 0 };	//服务端套接字配置结构体
volatile unsigned int currentClientNums = { 0 };	//当前连接的客户端数量


/* 客户端相关配置 */
typedef struct
{
	char isOnline;					//客户端是否在线 1则在线  0则不在线
	SOCKET clientSocket;			//客户端SOCKET套接字句柄
	SOCKADDR_IN clientSocketAddr;	//客户端套接字配置结构体
	HANDLE threadFunc;				//客户端对应的线程句柄
}CLIENT_t, * PCLIENT_t;

CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };

void ClientThreadFunc(void*);		//客户端线程函数

#define RECEIVE_BUFF_SIZE 200U		//接收缓冲区字节大小


/* 线程同步相关 */
CRITICAL_SECTION cs_printf;//关键段(临界区)
CRITICAL_SECTION cs_currentClientNums;

int main()
{
	/* 初始化DLL */
	WSADATA wsadata = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)//版本2.2
	{
		printf("初始化DLL失败!\n");
		return 0;
	}

	/* 创建SOCKET */
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 配置服务端套接字信息 */
	serverSocketAddr.sin_family = AF_INET;//IPV4
	serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
	serverSocketAddr.sin_port = htons(SERVER_PORT);//端口 
	if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)//绑定服务端套接字
	{
		printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 服务端进入监听状态 */
	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 初始化关键段(临界区) */
	if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
	{
		printf("关键段(临界区)cs_printf初始化失败!\n");
		DeleteCriticalSection(&cs_printf);//销毁关键段
		goto END;
	}
	InitializeCriticalSection(&cs_currentClientNums);

	/* 等待客户端连接服务端 */
	printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));

	while (1)
	{
		static SOCKET clientSocket = { 0 };//客户端套接字句柄
		static SOCKADDR_IN clientSocketAddr = { 0 };//客户端套接字配置结构体
		static unsigned int cLength = sizeof(clientSocketAddr);//结构体长度
		static unsigned int temp = { 0 };

		/* 接收客户端的连接请求 */
		/*
		* 没有新的连接程序会阻塞在这,accept是阻塞式函数
		* 直到出现异常,或者有新的连接
		*/
		clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
		if (clientSocket == SOCKET_ERROR)
		{
			EnterCriticalSection(&cs_printf);//进入关键段
			printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
			LeaveCriticalSection(&cs_printf);//离开关键段
			continue;//继续等待下一次连接
		}
		else
		{
			/* 判断是否超过允许连接的客户端数量 */
			EnterCriticalSection(&cs_currentClientNums);//进入关键段
			temp = currentClientNums + 1;
			LeaveCriticalSection(&cs_currentClientNums);//离开关键段
			if (temp > MAX_CLIENT_NUMS)
			{
				send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
				EnterCriticalSection(&cs_printf);//进入关键段
				printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
				LeaveCriticalSection(&cs_printf);//离开关键段
				closesocket(clientSocket);//断开与客户端的连接
			}
			else
			{
				for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
				{
					/* 遍历哪个客户端断开连接了,如果断开连接则将相应的线程句柄关闭 */
					if (client[i].isOnline == 0)
					{
						client[i].isOnline = 1;//设置客户端在线
						client[i].clientSocket = clientSocket;//保存客户端SOCKET套接字
						memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));//保存客户端SOCKET配置结构体
						/* 创建客户端线程 */
						//注意:_endthread 会自动关闭线程句柄。

(该行为与 Win32 ExitThread API 不同。

//因此,当你使用 _beginthread 和 _endthread 时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]); EnterCriticalSection(&cs_currentClientNums);//进入关键段 EnterCriticalSection(&cs_printf);//进入关键段 currentClientNums++;//当前在线客户端数量加一 printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS); LeaveCriticalSection(&cs_printf);//离开关键段 LeaveCriticalSection(&cs_currentClientNums);//离开关键段 break; } } } } } DeleteCriticalSection(&cs_currentClientNums);//销毁关键段 END: /* 断开服务端套接字 */ closesocket(serverSocket); /* 终止DLL的调用 */ WSACleanup(wsadata); return 0; } /* 客户端线程 */ void ClientThreadFunc(void* arg) { PCLIENT_t client = (PCLIENT_t)arg; char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };//接收来自客户端的数据缓冲区 int result = { 0 };//recv函数返回值 unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);//客户端端口 IN_ADDR clientIP = client->clientSocketAddr.sin_addr;//客户端IP EnterCriticalSection(&cs_printf);//进入关键段 printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort); LeaveCriticalSection(&cs_printf);//离开关键段 /* 发送欢迎信息至客户端 */ send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0); while (1) { /* 接收来自客户端的数据 */ /* * recv函数 的实质就是从socket的缓冲区里拷贝出数据 * 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0); if (result > 0) { //添加字符串结束标志,便于用字符串输出 if (result >= RECEIVE_BUFF_SIZE) { receiveBuff[RECEIVE_BUFF_SIZE - 1] = ';'} else [ { receiveBuff]result= ';' }send ( ,,client->clientSocketstrlen receiveBuff( ),receiveBuff0) ;//数据回传EnterCriticalSection( &);cs_printf//进入关键段printf( "收到来自IP:%s,端口:%d客户端的信息:%s\n",inet_ntoa( ),clientIP,) clientPort; receiveBuffLeaveCriticalSection( &);cs_printf//离开关键段}else if ( == 0result ) //result == 0 说明客户端断开连接EnterCriticalSection( { &);cs_printf//进入关键段printf( "客户端断开连接,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }else //result < 0 说明出现异常 EnterCriticalSection( { &);cs_printf//进入关键段printf( "与客户端通信时发生异常,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }} closesocket ( );client->clientSocket//断开连接=0 client->isOnline ; //设置客户端不在线EnterCriticalSection( &);cs_currentClientNums//进入关键段--; currentClientNums//当前在线客户端数量减一LeaveCriticalSection( &);cs_currentClientNums//离开关键段_endthread( );//终止当前线程}# include

客户端
#include 
#include 
#pragma 
comment( ,"ws2_32.lib"lib)/* 服务端信息 */#


define
SERVER_IP"127.0.0.1" //服务端IPV4地址			#	define
SERVER_PORT1234 //服务端端口			=		0
SOCKADDR_IN serverSocketAddr } { ; //服务端套接字配置结构体/* 客户端相关配置 */	=

0
SOCKET clientSocket } { ; //客户端SOCKET套接字#			define
BUFF_SIZE200U //缓冲区字节大小 char					[
] sendBuff=BUFF_SIZE0 } { ; //发送缓冲区char		[
] receiveBuff=BUFF_SIZE0 } { ; //接收缓冲区void	RecevieThreadFunc
( void*);//客户端接收来自服务端的数据线程函数volatile			char
= 0 isOnline } { ; //客户端是否在线 1则在线  0则不在线/* 线程同步相关 */			;

//关键段(临界区)
CRITICAL_SECTION cs_printf;int
CRITICAL_SECTION cs_isOnlinemain


( )/* 初始化DLL */=
{
	0
	WSADATA wsadata } { ; if(
	WSAStartup (MAKEWORD(2,2) ,&) !=wsadata0 ) //版本2.2printf(
	{
		"初始化DLL失败!\n");return0
		; }/* 创建SOCKET */
	=

	socket
	clientSocket ( ,,AF_INET) SOCK_STREAM; IPPROTO_TCP//TCP套接字if(
	== )clientSocket printf INVALID_SOCKET(
	{
		"创建客户端TCP套接字失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 配置服务端套接字信息并连接服务端 */
	.

	=
	serverSocketAddr;sin_family //IPV4 AF_INET..
	serverSocketAddr.sin_addr=S_uninet_addrS_addr ( );SERVER_IP//IP地址.=
	serverSocketAddrhtonssin_port ( );SERVER_PORT//端口if(
	connect (,&clientSocket, sizeofserverSocketAddr( ))serverSocketAddr==) //连接服务端 SOCKET_ERRORprintf(
	{
		"连接服务端失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 初始化关键段(临界区) */
	if

	(
	! InitializeCriticalSectionAndSpinCount(&,4000cs_printf) )printf(
	{
		"关键段(临界区)cs_printf初始化失败!\n");DeleteCriticalSection(
		&);cs_printf//销毁关键段goto;
		} ENDInitializeCriticalSection
	(
	&);cs_isOnline/* 客户端连接上了服务端 */printf

	(
	"已经连接上服务端,服务端IP:%s,端口:%d\n",inet_ntoa( .)serverSocketAddr,sin_addrntohs( .)serverSocketAddr)sin_port;=1
	isOnline ; /* 创建接收来自服务端数据的线程 */_beginthread

	(
	,0RecevieThreadFunc, NULL) ;/* 接收用户的输入并发送至服务端 */while

	(
	1 )EnterCriticalSection(
	{
		&);cs_isOnline//进入关键段if(
		! )//判断客户端是否在线isOnlineLeaveCriticalSection(
		{
			&);cs_isOnlinegoto;
			} ENDLeaveCriticalSection
		(
		&);cs_isOnline//离开关键段EnterCriticalSection(

		&);cs_printf//进入关键段printf(
		"请输入需要发送至服务端的数据:\n");LeaveCriticalSection(
		&);cs_printf//离开关键段scanf(
		"%s",); sendBuffsend(
		,,clientSocketstrlen sendBuff( ),sendBuff0) ;//发送至服务端Sleep(
		100);}DeleteCriticalSection
	(

	&);cs_isOnline//销毁关键段:/* 断开服务端套接字 */

ENDclosesocket
	(
	);clientSocket/* 终止DLL的调用 */WSACleanup

	(
	);wsadatareturn0

	; }void
RecevieThreadFunc


( void*)int arg=
{
	0 result } { ; //recv函数返回值while(

	1 )/* 接收来自服务端的数据 */=
	{
		recv
		/*
		* recv函数 的实质就是从socket的缓冲区里拷贝出数据
		* 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result ( ,,clientSocket, receiveBuff0 BUFF_SIZE) ;if( 0 )result > //添加字符串结束标志,便于用字符串输出if { ( ) [result >= BUFF_SIZE- { receiveBuff1BUFF_SIZE ] =';' } else[ ] = { receiveBuff';'result} EnterCriticalSection (& ) ;//进入关键段printfcs_printf("收到来自服务端的信息:%s\n", );LeaveCriticalSection( receiveBuff&) ;//离开关键段}cs_printfelseif( == 0 ) //result == 0 说明服务端断开连接result EnterCriticalSection (&) { ;//进入关键段EnterCriticalSectioncs_isOnline(&) ;//进入关键段printfcs_printf("服务端断开连接\n") ;=0;LeaveCriticalSection isOnline ( &) ;//离开关键段LeaveCriticalSectioncs_printf(&) ;//离开关键段breakcs_isOnline;}else //result < 0 说明出现异常EnterCriticalSection ( &) { ;//进入关键段printfcs_printf("与服务端通信时发生异常\n") ;LeaveCriticalSection(&) ;//离开关键段breakcs_printf;}} closesocket( ) ; //断开连接_endthreadclientSocket(); //终止当前线程}[+++][+++] [+++]

结果

服务端

客户端

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1240, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

使用多线程来实现可与多个客户端通信的服务端。


当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。

服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。


另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。

服务端

注意下多线程下共享内存,还有printf的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")


/* 服务端相关配置 */
#define SERVER_IP		"127.0.0.1"	//服务端IPV4地址
#define SERVER_PORT		1234		//服务端端口
#define MAX_CLIENT_NUMS	50U			//最大可连接客户端数量

SOCKET serverSocket = { 0 };			//服务端套接字句柄
SOCKADDR_IN serverSocketAddr = { 0 };	//服务端套接字配置结构体
volatile unsigned int currentClientNums = { 0 };	//当前连接的客户端数量


/* 客户端相关配置 */
typedef struct
{
	char isOnline;					//客户端是否在线 1则在线  0则不在线
	SOCKET clientSocket;			//客户端SOCKET套接字句柄
	SOCKADDR_IN clientSocketAddr;	//客户端套接字配置结构体
	HANDLE threadFunc;				//客户端对应的线程句柄
}CLIENT_t, * PCLIENT_t;

CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };

void ClientThreadFunc(void*);		//客户端线程函数

#define RECEIVE_BUFF_SIZE 200U		//接收缓冲区字节大小


/* 线程同步相关 */
CRITICAL_SECTION cs_printf;//关键段(临界区)
CRITICAL_SECTION cs_currentClientNums;

int main()
{
	/* 初始化DLL */
	WSADATA wsadata = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)//版本2.2
	{
		printf("初始化DLL失败!\n");
		return 0;
	}

	/* 创建SOCKET */
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 配置服务端套接字信息 */
	serverSocketAddr.sin_family = AF_INET;//IPV4
	serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
	serverSocketAddr.sin_port = htons(SERVER_PORT);//端口 
	if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)//绑定服务端套接字
	{
		printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 服务端进入监听状态 */
	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 初始化关键段(临界区) */
	if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
	{
		printf("关键段(临界区)cs_printf初始化失败!\n");
		DeleteCriticalSection(&cs_printf);//销毁关键段
		goto END;
	}
	InitializeCriticalSection(&cs_currentClientNums);

	/* 等待客户端连接服务端 */
	printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));

	while (1)
	{
		static SOCKET clientSocket = { 0 };//客户端套接字句柄
		static SOCKADDR_IN clientSocketAddr = { 0 };//客户端套接字配置结构体
		static unsigned int cLength = sizeof(clientSocketAddr);//结构体长度
		static unsigned int temp = { 0 };

		/* 接收客户端的连接请求 */
		/*
		* 没有新的连接程序会阻塞在这,accept是阻塞式函数
		* 直到出现异常,或者有新的连接
		*/
		clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
		if (clientSocket == SOCKET_ERROR)
		{
			EnterCriticalSection(&cs_printf);//进入关键段
			printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
			LeaveCriticalSection(&cs_printf);//离开关键段
			continue;//继续等待下一次连接
		}
		else
		{
			/* 判断是否超过允许连接的客户端数量 */
			EnterCriticalSection(&cs_currentClientNums);//进入关键段
			temp = currentClientNums + 1;
			LeaveCriticalSection(&cs_currentClientNums);//离开关键段
			if (temp > MAX_CLIENT_NUMS)
			{
				send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
				EnterCriticalSection(&cs_printf);//进入关键段
				printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
				LeaveCriticalSection(&cs_printf);//离开关键段
				closesocket(clientSocket);//断开与客户端的连接
			}
			else
			{
				for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
				{
					/* 遍历哪个客户端断开连接了,如果断开连接则将相应的线程句柄关闭 */
					if (client[i].isOnline == 0)
					{
						client[i].isOnline = 1;//设置客户端在线
						client[i].clientSocket = clientSocket;//保存客户端SOCKET套接字
						memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));//保存客户端SOCKET配置结构体
						/* 创建客户端线程 */
						//注意:_endthread 会自动关闭线程句柄。

(该行为与 Win32 ExitThread API 不同。

//因此,当你使用 _beginthread 和 _endthread 时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]); EnterCriticalSection(&cs_currentClientNums);//进入关键段 EnterCriticalSection(&cs_printf);//进入关键段 currentClientNums++;//当前在线客户端数量加一 printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS); LeaveCriticalSection(&cs_printf);//离开关键段 LeaveCriticalSection(&cs_currentClientNums);//离开关键段 break; } } } } } DeleteCriticalSection(&cs_currentClientNums);//销毁关键段 END: /* 断开服务端套接字 */ closesocket(serverSocket); /* 终止DLL的调用 */ WSACleanup(wsadata); return 0; } /* 客户端线程 */ void ClientThreadFunc(void* arg) { PCLIENT_t client = (PCLIENT_t)arg; char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };//接收来自客户端的数据缓冲区 int result = { 0 };//recv函数返回值 unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);//客户端端口 IN_ADDR clientIP = client->clientSocketAddr.sin_addr;//客户端IP EnterCriticalSection(&cs_printf);//进入关键段 printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort); LeaveCriticalSection(&cs_printf);//离开关键段 /* 发送欢迎信息至客户端 */ send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0); while (1) { /* 接收来自客户端的数据 */ /* * recv函数 的实质就是从socket的缓冲区里拷贝出数据 * 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0); if (result > 0) { //添加字符串结束标志,便于用字符串输出 if (result >= RECEIVE_BUFF_SIZE) { receiveBuff[RECEIVE_BUFF_SIZE - 1] = ';'} else [ { receiveBuff]result= ';' }send ( ,,client->clientSocketstrlen receiveBuff( ),receiveBuff0) ;//数据回传EnterCriticalSection( &);cs_printf//进入关键段printf( "收到来自IP:%s,端口:%d客户端的信息:%s\n",inet_ntoa( ),clientIP,) clientPort; receiveBuffLeaveCriticalSection( &);cs_printf//离开关键段}else if ( == 0result ) //result == 0 说明客户端断开连接EnterCriticalSection( { &);cs_printf//进入关键段printf( "客户端断开连接,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }else //result < 0 说明出现异常 EnterCriticalSection( { &);cs_printf//进入关键段printf( "与客户端通信时发生异常,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }} closesocket ( );client->clientSocket//断开连接=0 client->isOnline ; //设置客户端不在线EnterCriticalSection( &);cs_currentClientNums//进入关键段--; currentClientNums//当前在线客户端数量减一LeaveCriticalSection( &);cs_currentClientNums//离开关键段_endthread( );//终止当前线程}# include

客户端
#include 
#include 
#pragma 
comment( ,"ws2_32.lib"lib)/* 服务端信息 */#


define
SERVER_IP"127.0.0.1" //服务端IPV4地址			#	define
SERVER_PORT1234 //服务端端口			=		0
SOCKADDR_IN serverSocketAddr } { ; //服务端套接字配置结构体/* 客户端相关配置 */	=

0
SOCKET clientSocket } { ; //客户端SOCKET套接字#			define
BUFF_SIZE200U //缓冲区字节大小 char					[
] sendBuff=BUFF_SIZE0 } { ; //发送缓冲区char		[
] receiveBuff=BUFF_SIZE0 } { ; //接收缓冲区void	RecevieThreadFunc
( void*);//客户端接收来自服务端的数据线程函数volatile			char
= 0 isOnline } { ; //客户端是否在线 1则在线  0则不在线/* 线程同步相关 */			;

//关键段(临界区)
CRITICAL_SECTION cs_printf;int
CRITICAL_SECTION cs_isOnlinemain


( )/* 初始化DLL */=
{
	0
	WSADATA wsadata } { ; if(
	WSAStartup (MAKEWORD(2,2) ,&) !=wsadata0 ) //版本2.2printf(
	{
		"初始化DLL失败!\n");return0
		; }/* 创建SOCKET */
	=

	socket
	clientSocket ( ,,AF_INET) SOCK_STREAM; IPPROTO_TCP//TCP套接字if(
	== )clientSocket printf INVALID_SOCKET(
	{
		"创建客户端TCP套接字失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 配置服务端套接字信息并连接服务端 */
	.

	=
	serverSocketAddr;sin_family //IPV4 AF_INET..
	serverSocketAddr.sin_addr=S_uninet_addrS_addr ( );SERVER_IP//IP地址.=
	serverSocketAddrhtonssin_port ( );SERVER_PORT//端口if(
	connect (,&clientSocket, sizeofserverSocketAddr( ))serverSocketAddr==) //连接服务端 SOCKET_ERRORprintf(
	{
		"连接服务端失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 初始化关键段(临界区) */
	if

	(
	! InitializeCriticalSectionAndSpinCount(&,4000cs_printf) )printf(
	{
		"关键段(临界区)cs_printf初始化失败!\n");DeleteCriticalSection(
		&);cs_printf//销毁关键段goto;
		} ENDInitializeCriticalSection
	(
	&);cs_isOnline/* 客户端连接上了服务端 */printf

	(
	"已经连接上服务端,服务端IP:%s,端口:%d\n",inet_ntoa( .)serverSocketAddr,sin_addrntohs( .)serverSocketAddr)sin_port;=1
	isOnline ; /* 创建接收来自服务端数据的线程 */_beginthread

	(
	,0RecevieThreadFunc, NULL) ;/* 接收用户的输入并发送至服务端 */while

	(
	1 )EnterCriticalSection(
	{
		&);cs_isOnline//进入关键段if(
		! )//判断客户端是否在线isOnlineLeaveCriticalSection(
		{
			&);cs_isOnlinegoto;
			} ENDLeaveCriticalSection
		(
		&);cs_isOnline//离开关键段EnterCriticalSection(

		&);cs_printf//进入关键段printf(
		"请输入需要发送至服务端的数据:\n");LeaveCriticalSection(
		&);cs_printf//离开关键段scanf(
		"%s",); sendBuffsend(
		,,clientSocketstrlen sendBuff( ),sendBuff0) ;//发送至服务端Sleep(
		100);}DeleteCriticalSection
	(

	&);cs_isOnline//销毁关键段:/* 断开服务端套接字 */

ENDclosesocket
	(
	);clientSocket/* 终止DLL的调用 */WSACleanup

	(
	);wsadatareturn0

	; }void
RecevieThreadFunc


( void*)int arg=
{
	0 result } { ; //recv函数返回值while(

	1 )/* 接收来自服务端的数据 */=
	{
		recv
		/*
		* recv函数 的实质就是从socket的缓冲区里拷贝出数据
		* 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result ( ,,clientSocket, receiveBuff0 BUFF_SIZE) ;if( 0 )result > //添加字符串结束标志,便于用字符串输出if { ( ) [result >= BUFF_SIZE- { receiveBuff1BUFF_SIZE ] =';' } else[ ] = { receiveBuff';'result} EnterCriticalSection (& ) ;//进入关键段printfcs_printf("收到来自服务端的信息:%s\n", );LeaveCriticalSection( receiveBuff&) ;//离开关键段}cs_printfelseif( == 0 ) //result == 0 说明服务端断开连接result EnterCriticalSection (&) { ;//进入关键段EnterCriticalSectioncs_isOnline(&) ;//进入关键段printfcs_printf("服务端断开连接\n") ;=0;LeaveCriticalSection isOnline ( &) ;//离开关键段LeaveCriticalSectioncs_printf(&) ;//离开关键段breakcs_isOnline;}else //result < 0 说明出现异常EnterCriticalSection ( &) { ;//进入关键段printfcs_printf("与服务端通信时发生异常\n") ;LeaveCriticalSection(&) ;//离开关键段breakcs_printf;}} closesocket( ) ; //断开连接_endthreadclientSocket(); //终止当前线程}[+++] [+++]

结果

服务端

客户端

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1241, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

使用多线程来实现可与多个客户端通信的服务端。


当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。

服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。


另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。

服务端

注意下多线程下共享内存,还有printf的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")


/* 服务端相关配置 */
#define SERVER_IP		"127.0.0.1"	//服务端IPV4地址
#define SERVER_PORT		1234		//服务端端口
#define MAX_CLIENT_NUMS	50U			//最大可连接客户端数量

SOCKET serverSocket = { 0 };			//服务端套接字句柄
SOCKADDR_IN serverSocketAddr = { 0 };	//服务端套接字配置结构体
volatile unsigned int currentClientNums = { 0 };	//当前连接的客户端数量


/* 客户端相关配置 */
typedef struct
{
	char isOnline;					//客户端是否在线 1则在线  0则不在线
	SOCKET clientSocket;			//客户端SOCKET套接字句柄
	SOCKADDR_IN clientSocketAddr;	//客户端套接字配置结构体
	HANDLE threadFunc;				//客户端对应的线程句柄
}CLIENT_t, * PCLIENT_t;

CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };

void ClientThreadFunc(void*);		//客户端线程函数

#define RECEIVE_BUFF_SIZE 200U		//接收缓冲区字节大小


/* 线程同步相关 */
CRITICAL_SECTION cs_printf;//关键段(临界区)
CRITICAL_SECTION cs_currentClientNums;

int main()
{
	/* 初始化DLL */
	WSADATA wsadata = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)//版本2.2
	{
		printf("初始化DLL失败!\n");
		return 0;
	}

	/* 创建SOCKET */
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 配置服务端套接字信息 */
	serverSocketAddr.sin_family = AF_INET;//IPV4
	serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
	serverSocketAddr.sin_port = htons(SERVER_PORT);//端口 
	if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)//绑定服务端套接字
	{
		printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 服务端进入监听状态 */
	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 初始化关键段(临界区) */
	if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
	{
		printf("关键段(临界区)cs_printf初始化失败!\n");
		DeleteCriticalSection(&cs_printf);//销毁关键段
		goto END;
	}
	InitializeCriticalSection(&cs_currentClientNums);

	/* 等待客户端连接服务端 */
	printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));

	while (1)
	{
		static SOCKET clientSocket = { 0 };//客户端套接字句柄
		static SOCKADDR_IN clientSocketAddr = { 0 };//客户端套接字配置结构体
		static unsigned int cLength = sizeof(clientSocketAddr);//结构体长度
		static unsigned int temp = { 0 };

		/* 接收客户端的连接请求 */
		/*
		* 没有新的连接程序会阻塞在这,accept是阻塞式函数
		* 直到出现异常,或者有新的连接
		*/
		clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
		if (clientSocket == SOCKET_ERROR)
		{
			EnterCriticalSection(&cs_printf);//进入关键段
			printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
			LeaveCriticalSection(&cs_printf);//离开关键段
			continue;//继续等待下一次连接
		}
		else
		{
			/* 判断是否超过允许连接的客户端数量 */
			EnterCriticalSection(&cs_currentClientNums);//进入关键段
			temp = currentClientNums + 1;
			LeaveCriticalSection(&cs_currentClientNums);//离开关键段
			if (temp > MAX_CLIENT_NUMS)
			{
				send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
				EnterCriticalSection(&cs_printf);//进入关键段
				printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
				LeaveCriticalSection(&cs_printf);//离开关键段
				closesocket(clientSocket);//断开与客户端的连接
			}
			else
			{
				for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
				{
					/* 遍历哪个客户端断开连接了,如果断开连接则将相应的线程句柄关闭 */
					if (client[i].isOnline == 0)
					{
						client[i].isOnline = 1;//设置客户端在线
						client[i].clientSocket = clientSocket;//保存客户端SOCKET套接字
						memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));//保存客户端SOCKET配置结构体
						/* 创建客户端线程 */
						//注意:_endthread 会自动关闭线程句柄。

(该行为与 Win32 ExitThread API 不同。

//因此,当你使用 _beginthread 和 _endthread 时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]); EnterCriticalSection(&cs_currentClientNums);//进入关键段 EnterCriticalSection(&cs_printf);//进入关键段 currentClientNums++;//当前在线客户端数量加一 printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS); LeaveCriticalSection(&cs_printf);//离开关键段 LeaveCriticalSection(&cs_currentClientNums);//离开关键段 break; } } } } } DeleteCriticalSection(&cs_currentClientNums);//销毁关键段 END: /* 断开服务端套接字 */ closesocket(serverSocket); /* 终止DLL的调用 */ WSACleanup(wsadata); return 0; } /* 客户端线程 */ void ClientThreadFunc(void* arg) { PCLIENT_t client = (PCLIENT_t)arg; char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };//接收来自客户端的数据缓冲区 int result = { 0 };//recv函数返回值 unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);//客户端端口 IN_ADDR clientIP = client->clientSocketAddr.sin_addr;//客户端IP EnterCriticalSection(&cs_printf);//进入关键段 printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort); LeaveCriticalSection(&cs_printf);//离开关键段 /* 发送欢迎信息至客户端 */ send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0); while (1) { /* 接收来自客户端的数据 */ /* * recv函数 的实质就是从socket的缓冲区里拷贝出数据 * 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0); if (result > 0) { //添加字符串结束标志,便于用字符串输出 if (result >= RECEIVE_BUFF_SIZE) { receiveBuff[RECEIVE_BUFF_SIZE - 1] = ';'} else [ { receiveBuff]result= ';' }send ( ,,client->clientSocketstrlen receiveBuff( ),receiveBuff0) ;//数据回传EnterCriticalSection( &);cs_printf//进入关键段printf( "收到来自IP:%s,端口:%d客户端的信息:%s\n",inet_ntoa( ),clientIP,) clientPort; receiveBuffLeaveCriticalSection( &);cs_printf//离开关键段}else if ( == 0result ) //result == 0 说明客户端断开连接EnterCriticalSection( { &);cs_printf//进入关键段printf( "客户端断开连接,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }else //result < 0 说明出现异常 EnterCriticalSection( { &);cs_printf//进入关键段printf( "与客户端通信时发生异常,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }} closesocket ( );client->clientSocket//断开连接=0 client->isOnline ; //设置客户端不在线EnterCriticalSection( &);cs_currentClientNums//进入关键段--; currentClientNums//当前在线客户端数量减一LeaveCriticalSection( &);cs_currentClientNums//离开关键段_endthread( );//终止当前线程}# include

客户端
#include 
#include 
#pragma 
comment( ,"ws2_32.lib"lib)/* 服务端信息 */#


define
SERVER_IP"127.0.0.1" //服务端IPV4地址			#	define
SERVER_PORT1234 //服务端端口			=		0
SOCKADDR_IN serverSocketAddr } { ; //服务端套接字配置结构体/* 客户端相关配置 */	=

0
SOCKET clientSocket } { ; //客户端SOCKET套接字#			define
BUFF_SIZE200U //缓冲区字节大小 char					[
] sendBuff=BUFF_SIZE0 } { ; //发送缓冲区char		[
] receiveBuff=BUFF_SIZE0 } { ; //接收缓冲区void	RecevieThreadFunc
( void*);//客户端接收来自服务端的数据线程函数volatile			char
= 0 isOnline } { ; //客户端是否在线 1则在线  0则不在线/* 线程同步相关 */			;

//关键段(临界区)
CRITICAL_SECTION cs_printf;int
CRITICAL_SECTION cs_isOnlinemain


( )/* 初始化DLL */=
{
	0
	WSADATA wsadata } { ; if(
	WSAStartup (MAKEWORD(2,2) ,&) !=wsadata0 ) //版本2.2printf(
	{
		"初始化DLL失败!\n");return0
		; }/* 创建SOCKET */
	=

	socket
	clientSocket ( ,,AF_INET) SOCK_STREAM; IPPROTO_TCP//TCP套接字if(
	== )clientSocket printf INVALID_SOCKET(
	{
		"创建客户端TCP套接字失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 配置服务端套接字信息并连接服务端 */
	.

	=
	serverSocketAddr;sin_family //IPV4 AF_INET..
	serverSocketAddr.sin_addr=S_uninet_addrS_addr ( );SERVER_IP//IP地址.=
	serverSocketAddrhtonssin_port ( );SERVER_PORT//端口if(
	connect (,&clientSocket, sizeofserverSocketAddr( ))serverSocketAddr==) //连接服务端 SOCKET_ERRORprintf(
	{
		"连接服务端失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 初始化关键段(临界区) */
	if

	(
	! InitializeCriticalSectionAndSpinCount(&,4000cs_printf) )printf(
	{
		"关键段(临界区)cs_printf初始化失败!\n");DeleteCriticalSection(
		&);cs_printf//销毁关键段goto;
		} ENDInitializeCriticalSection
	(
	&);cs_isOnline/* 客户端连接上了服务端 */printf

	(
	"已经连接上服务端,服务端IP:%s,端口:%d\n",inet_ntoa( .)serverSocketAddr,sin_addrntohs( .)serverSocketAddr)sin_port;=1
	isOnline ; /* 创建接收来自服务端数据的线程 */_beginthread

	(
	,0RecevieThreadFunc, NULL) ;/* 接收用户的输入并发送至服务端 */while

	(
	1 )EnterCriticalSection(
	{
		&);cs_isOnline//进入关键段if(
		! )//判断客户端是否在线isOnlineLeaveCriticalSection(
		{
			&);cs_isOnlinegoto;
			} ENDLeaveCriticalSection
		(
		&);cs_isOnline//离开关键段EnterCriticalSection(

		&);cs_printf//进入关键段printf(
		"请输入需要发送至服务端的数据:\n");LeaveCriticalSection(
		&);cs_printf//离开关键段scanf(
		"%s",); sendBuffsend(
		,,clientSocketstrlen sendBuff( ),sendBuff0) ;//发送至服务端Sleep(
		100);}DeleteCriticalSection
	(

	&);cs_isOnline//销毁关键段:/* 断开服务端套接字 */

ENDclosesocket
	(
	);clientSocket/* 终止DLL的调用 */WSACleanup

	(
	);wsadatareturn0

	; }void
RecevieThreadFunc


( void*)int arg=
{
	0 result } { ; //recv函数返回值while(

	1 )/* 接收来自服务端的数据 */=
	{
		recv
		/*
		* recv函数 的实质就是从socket的缓冲区里拷贝出数据
		* 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result ( ,,clientSocket, receiveBuff0 BUFF_SIZE) ;if( 0 )result > //添加字符串结束标志,便于用字符串输出if { ( ) [result >= BUFF_SIZE- { receiveBuff1BUFF_SIZE ] =';' } else[ ] = { receiveBuff';'result} EnterCriticalSection (& ) ;//进入关键段printfcs_printf("收到来自服务端的信息:%s\n", );LeaveCriticalSection( receiveBuff&) ;//离开关键段}cs_printfelseif( == 0 ) //result == 0 说明服务端断开连接result EnterCriticalSection (&) { ;//进入关键段EnterCriticalSectioncs_isOnline(&) ;//进入关键段printfcs_printf("服务端断开连接\n") ;=0;LeaveCriticalSection isOnline ( &) ;//离开关键段LeaveCriticalSectioncs_printf(&) ;//离开关键段breakcs_isOnline;}else //result < 0 说明出现异常EnterCriticalSection ( &) { ;//进入关键段printfcs_printf("与服务端通信时发生异常\n") ;LeaveCriticalSection(&) ;//离开关键段breakcs_printf;}} closesocket( ) ; //断开连接_endthreadclientSocket(); //终止当前线程} [+++]

结果

服务端

客户端

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
TCPIP协议学习笔记_C_内存溢出

TCPIP协议学习笔记

TCPIP协议学习笔记,第1张

使用多线程来实现可与多个客户端通信的服务端。


当客户端连接上服务端之后,为该客户端创建一个新的线程,在该线程中与客户端进行通信。

服务端程序中的主线程负责监听并接受客户端的连接请求,创建与客户端通信的线程。


另外,这是一个在windows下实现的回音(客户端发啥服务端就回传给客户端啥)服务端。

服务端

注意下多线程下共享内存,还有printf的使用,需要线程之间同步,共享资源同一时间只能有一个线程使用。

#include 
#include 
#include 
#pragma comment(lib,"ws2_32.lib")


/* 服务端相关配置 */
#define SERVER_IP		"127.0.0.1"	//服务端IPV4地址
#define SERVER_PORT		1234		//服务端端口
#define MAX_CLIENT_NUMS	50U			//最大可连接客户端数量

SOCKET serverSocket = { 0 };			//服务端套接字句柄
SOCKADDR_IN serverSocketAddr = { 0 };	//服务端套接字配置结构体
volatile unsigned int currentClientNums = { 0 };	//当前连接的客户端数量


/* 客户端相关配置 */
typedef struct
{
	char isOnline;					//客户端是否在线 1则在线  0则不在线
	SOCKET clientSocket;			//客户端SOCKET套接字句柄
	SOCKADDR_IN clientSocketAddr;	//客户端套接字配置结构体
	HANDLE threadFunc;				//客户端对应的线程句柄
}CLIENT_t, * PCLIENT_t;

CLIENT_t client[MAX_CLIENT_NUMS] = { 0 };

void ClientThreadFunc(void*);		//客户端线程函数

#define RECEIVE_BUFF_SIZE 200U		//接收缓冲区字节大小


/* 线程同步相关 */
CRITICAL_SECTION cs_printf;//关键段(临界区)
CRITICAL_SECTION cs_currentClientNums;

int main()
{
	/* 初始化DLL */
	WSADATA wsadata = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)//版本2.2
	{
		printf("初始化DLL失败!\n");
		return 0;
	}

	/* 创建SOCKET */
	serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//TCP套接字
	if (serverSocket == INVALID_SOCKET)
	{
		printf("创建服务端TCP套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 配置服务端套接字信息 */
	serverSocketAddr.sin_family = AF_INET;//IPV4
	serverSocketAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);//IP地址
	serverSocketAddr.sin_port = htons(SERVER_PORT);//端口 
	if (bind(serverSocket, &serverSocketAddr, sizeof(serverSocketAddr)) == SOCKET_ERROR)//绑定服务端套接字
	{
		printf("绑定服务端套接字失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 服务端进入监听状态 */
	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		printf("服务端进入监听状态失败,错误代码为:%d\n", WSAGetLastError());
		goto END;
	}

	/* 初始化关键段(临界区) */
	if (!InitializeCriticalSectionAndSpinCount(&cs_printf, 4000))
	{
		printf("关键段(临界区)cs_printf初始化失败!\n");
		DeleteCriticalSection(&cs_printf);//销毁关键段
		goto END;
	}
	InitializeCriticalSection(&cs_currentClientNums);

	/* 等待客户端连接服务端 */
	printf("服务端已经开启,IP:%s,端口:%d\n", inet_ntoa(serverSocketAddr.sin_addr), ntohs(serverSocketAddr.sin_port));

	while (1)
	{
		static SOCKET clientSocket = { 0 };//客户端套接字句柄
		static SOCKADDR_IN clientSocketAddr = { 0 };//客户端套接字配置结构体
		static unsigned int cLength = sizeof(clientSocketAddr);//结构体长度
		static unsigned int temp = { 0 };

		/* 接收客户端的连接请求 */
		/*
		* 没有新的连接程序会阻塞在这,accept是阻塞式函数
		* 直到出现异常,或者有新的连接
		*/
		clientSocket = accept(serverSocket, &clientSocketAddr, &cLength);
		if (clientSocket == SOCKET_ERROR)
		{
			EnterCriticalSection(&cs_printf);//进入关键段
			printf("客户端连接错误,错误代码为:%d\n", WSAGetLastError());
			LeaveCriticalSection(&cs_printf);//离开关键段
			continue;//继续等待下一次连接
		}
		else
		{
			/* 判断是否超过允许连接的客户端数量 */
			EnterCriticalSection(&cs_currentClientNums);//进入关键段
			temp = currentClientNums + 1;
			LeaveCriticalSection(&cs_currentClientNums);//离开关键段
			if (temp > MAX_CLIENT_NUMS)
			{
				send(clientSocket, "连接已断开,已经超过可连接的最大数量!\n", strlen("连接已断开,已经超过可连接的最大数量!\n"), 0);
				EnterCriticalSection(&cs_printf);//进入关键段
				printf("有新的客户端连接请求,IP:%s,端口:%d,但已经超过可连接的最大数量!\n", inet_ntoa(clientSocketAddr.sin_addr), ntohs(clientSocketAddr.sin_port));
				LeaveCriticalSection(&cs_printf);//离开关键段
				closesocket(clientSocket);//断开与客户端的连接
			}
			else
			{
				for (size_t i = 0; i < MAX_CLIENT_NUMS; i++)
				{
					/* 遍历哪个客户端断开连接了,如果断开连接则将相应的线程句柄关闭 */
					if (client[i].isOnline == 0)
					{
						client[i].isOnline = 1;//设置客户端在线
						client[i].clientSocket = clientSocket;//保存客户端SOCKET套接字
						memcpy(&client[i].clientSocketAddr, &clientSocketAddr, sizeof(clientSocketAddr));//保存客户端SOCKET配置结构体
						/* 创建客户端线程 */
						//注意:_endthread 会自动关闭线程句柄。

(该行为与 Win32 ExitThread API 不同。

//因此,当你使用 _beginthread 和 _endthread 时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

client[i].threadFunc = _beginthread(ClientThreadFunc, 0, &client[i]); EnterCriticalSection(&cs_currentClientNums);//进入关键段 EnterCriticalSection(&cs_printf);//进入关键段 currentClientNums++;//当前在线客户端数量加一 printf("当前已有 %d 个客户端连接,最多可连接 %d 个客户端\n", currentClientNums, MAX_CLIENT_NUMS); LeaveCriticalSection(&cs_printf);//离开关键段 LeaveCriticalSection(&cs_currentClientNums);//离开关键段 break; } } } } } DeleteCriticalSection(&cs_currentClientNums);//销毁关键段 END: /* 断开服务端套接字 */ closesocket(serverSocket); /* 终止DLL的调用 */ WSACleanup(wsadata); return 0; } /* 客户端线程 */ void ClientThreadFunc(void* arg) { PCLIENT_t client = (PCLIENT_t)arg; char receiveBuff[RECEIVE_BUFF_SIZE] = { 0 };//接收来自客户端的数据缓冲区 int result = { 0 };//recv函数返回值 unsigned short clientPort = ntohs(client->clientSocketAddr.sin_port);//客户端端口 IN_ADDR clientIP = client->clientSocketAddr.sin_addr;//客户端IP EnterCriticalSection(&cs_printf);//进入关键段 printf("有新的客户端连接,IP:%s,端口:%d\n", inet_ntoa(clientIP), clientPort); LeaveCriticalSection(&cs_printf);//离开关键段 /* 发送欢迎信息至客户端 */ send(client->clientSocket, "Hello i am server\n", strlen("Hello i am server\n"), 0); while (1) { /* 接收来自客户端的数据 */ /* * recv函数 的实质就是从socket的缓冲区里拷贝出数据 * 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result = recv(client->clientSocket, receiveBuff, RECEIVE_BUFF_SIZE, 0); if (result > 0) { //添加字符串结束标志,便于用字符串输出 if (result >= RECEIVE_BUFF_SIZE) { receiveBuff[RECEIVE_BUFF_SIZE - 1] = ';'} else [ { receiveBuff]result= ';' }send ( ,,client->clientSocketstrlen receiveBuff( ),receiveBuff0) ;//数据回传EnterCriticalSection( &);cs_printf//进入关键段printf( "收到来自IP:%s,端口:%d客户端的信息:%s\n",inet_ntoa( ),clientIP,) clientPort; receiveBuffLeaveCriticalSection( &);cs_printf//离开关键段}else if ( == 0result ) //result == 0 说明客户端断开连接EnterCriticalSection( { &);cs_printf//进入关键段printf( "客户端断开连接,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }else //result < 0 说明出现异常 EnterCriticalSection( { &);cs_printf//进入关键段printf( "与客户端通信时发生异常,IP:%s,端口:%d\n",inet_ntoa( ),clientIP); clientPortLeaveCriticalSection( &);cs_printf//离开关键段break; }} closesocket ( );client->clientSocket//断开连接=0 client->isOnline ; //设置客户端不在线EnterCriticalSection( &);cs_currentClientNums//进入关键段--; currentClientNums//当前在线客户端数量减一LeaveCriticalSection( &);cs_currentClientNums//离开关键段_endthread( );//终止当前线程}# include

客户端
#include 
#include 
#pragma 
comment( ,"ws2_32.lib"lib)/* 服务端信息 */#


define
SERVER_IP"127.0.0.1" //服务端IPV4地址			#	define
SERVER_PORT1234 //服务端端口			=		0
SOCKADDR_IN serverSocketAddr } { ; //服务端套接字配置结构体/* 客户端相关配置 */	=

0
SOCKET clientSocket } { ; //客户端SOCKET套接字#			define
BUFF_SIZE200U //缓冲区字节大小 char					[
] sendBuff=BUFF_SIZE0 } { ; //发送缓冲区char		[
] receiveBuff=BUFF_SIZE0 } { ; //接收缓冲区void	RecevieThreadFunc
( void*);//客户端接收来自服务端的数据线程函数volatile			char
= 0 isOnline } { ; //客户端是否在线 1则在线  0则不在线/* 线程同步相关 */			;

//关键段(临界区)
CRITICAL_SECTION cs_printf;int
CRITICAL_SECTION cs_isOnlinemain


( )/* 初始化DLL */=
{
	0
	WSADATA wsadata } { ; if(
	WSAStartup (MAKEWORD(2,2) ,&) !=wsadata0 ) //版本2.2printf(
	{
		"初始化DLL失败!\n");return0
		; }/* 创建SOCKET */
	=

	socket
	clientSocket ( ,,AF_INET) SOCK_STREAM; IPPROTO_TCP//TCP套接字if(
	== )clientSocket printf INVALID_SOCKET(
	{
		"创建客户端TCP套接字失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 配置服务端套接字信息并连接服务端 */
	.

	=
	serverSocketAddr;sin_family //IPV4 AF_INET..
	serverSocketAddr.sin_addr=S_uninet_addrS_addr ( );SERVER_IP//IP地址.=
	serverSocketAddrhtonssin_port ( );SERVER_PORT//端口if(
	connect (,&clientSocket, sizeofserverSocketAddr( ))serverSocketAddr==) //连接服务端 SOCKET_ERRORprintf(
	{
		"连接服务端失败,错误代码为:%d\n",WSAGetLastError( ));goto;
		} END/* 初始化关键段(临界区) */
	if

	(
	! InitializeCriticalSectionAndSpinCount(&,4000cs_printf) )printf(
	{
		"关键段(临界区)cs_printf初始化失败!\n");DeleteCriticalSection(
		&);cs_printf//销毁关键段goto;
		} ENDInitializeCriticalSection
	(
	&);cs_isOnline/* 客户端连接上了服务端 */printf

	(
	"已经连接上服务端,服务端IP:%s,端口:%d\n",inet_ntoa( .)serverSocketAddr,sin_addrntohs( .)serverSocketAddr)sin_port;=1
	isOnline ; /* 创建接收来自服务端数据的线程 */_beginthread

	(
	,0RecevieThreadFunc, NULL) ;/* 接收用户的输入并发送至服务端 */while

	(
	1 )EnterCriticalSection(
	{
		&);cs_isOnline//进入关键段if(
		! )//判断客户端是否在线isOnlineLeaveCriticalSection(
		{
			&);cs_isOnlinegoto;
			} ENDLeaveCriticalSection
		(
		&);cs_isOnline//离开关键段EnterCriticalSection(

		&);cs_printf//进入关键段printf(
		"请输入需要发送至服务端的数据:\n");LeaveCriticalSection(
		&);cs_printf//离开关键段scanf(
		"%s",); sendBuffsend(
		,,clientSocketstrlen sendBuff( ),sendBuff0) ;//发送至服务端Sleep(
		100);}DeleteCriticalSection
	(

	&);cs_isOnline//销毁关键段:/* 断开服务端套接字 */

ENDclosesocket
	(
	);clientSocket/* 终止DLL的调用 */WSACleanup

	(
	);wsadatareturn0

	; }void
RecevieThreadFunc


( void*)int arg=
{
	0 result } { ; //recv函数返回值while(

	1 )/* 接收来自服务端的数据 */=
	{
		recv
		/*
		* recv函数 的实质就是从socket的缓冲区里拷贝出数据
		* 返回值就是拷贝出字节数的大小。

* 当缓冲区内没有内容的时候,会处于阻塞 * 状态,这个while函数会停在这里。

直到新的数据进来或者出现异常。

*/ result ( ,,clientSocket, receiveBuff0 BUFF_SIZE) ;if( 0 )result > //添加字符串结束标志,便于用字符串输出if { ( ) [result >= BUFF_SIZE- { receiveBuff1BUFF_SIZE ] =';' } else[ ] = { receiveBuff';'result} EnterCriticalSection (& ) ;//进入关键段printfcs_printf("收到来自服务端的信息:%s\n", );LeaveCriticalSection( receiveBuff&) ;//离开关键段}cs_printfelseif( == 0 ) //result == 0 说明服务端断开连接result EnterCriticalSection (&) { ;//进入关键段EnterCriticalSectioncs_isOnline(&) ;//进入关键段printfcs_printf("服务端断开连接\n") ;=0;LeaveCriticalSection isOnline ( &) ;//离开关键段LeaveCriticalSectioncs_printf(&) ;//离开关键段breakcs_isOnline;}else //result < 0 说明出现异常EnterCriticalSection ( &) { ;//进入关键段printfcs_printf("与服务端通信时发生异常\n") ;LeaveCriticalSection(&) ;//离开关键段breakcs_printf;}} closesocket( ) ; //断开连接_endthreadclientSocket(); //终止当前线程}

结果

服务端

客户端

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/674336.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存