网络编程

网络编程--客户端和服务器端

  1. 客户端和服务器端实现连接和通信

服务器端:

通过sock套接字进行连接通信

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
	//初始化网络环境
	WSADATA date{ 0 };
	WSAStartup(MAKEWORD(2, 2), &date);//WSAStartup 函数通过进程启动 Winsock DLL 的使用
	//创建socket
	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定socket的端口号IP地址
	sockaddr_in severaddr{ 0 };
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &severaddr.sin_addr);
	bind(server, (SOCKADDR*)&severaddr, sizeof(severaddr));
	//监听
	listen(server, SOMAXCONN);
	//会话
	sockaddr_in clientaddr{ 0 };
	int size = sizeof(clientaddr);
	while (1)
	{
		//获取客户端socket
		printf("————等待客户端连接————");
		SOCKET client = accept(server, (SOCKADDR*)&clientaddr, &size);
		char buff[0x100]{ 0 };
		recv(client, buff, 0x00, 0);
		printf("客户端发送:%s\n", buff);
		send(client, "你还有什么请求?", 16, 0);
	}
	return 0;
}

客户端:

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
	//初始化网络环境
	WSADATA date{ 0 };
	WSAStartup(MAKEWORD(2, 2), &date);//WSAStartup 函数通过进程启动 Winsock DLL 的使用
	//创建socket
	SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定socket的端口号IP地址
	sockaddr_in severaddr{ 0 };
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &severaddr.sin_addr);
	int result = connect(client, (SOCKADDR*)&severaddr, sizeof(severaddr));
	if (result)
	{
		printf("连接失败");
		return 0;
	}
	printf("正在建立连接");
	send(client, "dadadaddadada\n", 24, 0);
	char buff[0x100]{ 0 };
	recv(client, buff, 0x100, 0);
	system("pause");
	return 0;
}
  1. 上面的结果只能实现的是1对1,服务器和客户端进行单独的对话,那么我们要怎么样创建的是一个多人聊天的呢?

我们通过建立线程的形式,将每一个客户端与服务器进行会话的socket通过STL中的vector容器以数组的形式进行存储,通过回调函数proc来实现我们的多人聊天对话

服务器:

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<WS2tcpip.h>
#include<vector>
using std::vector;
vector<SOCKET> client_chat;
#pragma comment(lib,"ws2_32.lib")
DWORD WINAPI Chat_fun(
	LPVOID lpParameter
)
{
	int recvSize = 0;
	char buff[0x100]{ 0 };
	SOCKET client = (SOCKET)lpParameter;
	while (
		(recvSize = recv(client, buff, 0x100, 0)) > 0
		)
	{
		for (int i = 0; i < client_chat.size(); i++)
		{
			if (client_chat[i] != client)
			{
				send(client_chat[i], buff, recvSize, 0);
			}
		}
	}
	for (int i = 0; i < client_chat.size(); i++)
	{
		if (client_chat[i] == client)
		{
			closesocket(client);
			client_chat.erase(client_chat.begin() + i);
			printf("客户端%u断开了连接\n", client);
		}
	}
	return 0;
}
int main()
{
	//初始化网络环境
	WSADATA date{ 0 };
	WSAStartup(MAKEWORD(2, 2), &date);//WSAStartup 函数通过进程启动 Winsock DLL 的使用
	//创建socket
	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定socket的端口号IP地址
	sockaddr_in severaddr{ 0 };
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &severaddr.sin_addr);
	bind(server, (SOCKADDR*)&severaddr, sizeof(severaddr));
	//监听
	listen(server, SOMAXCONN);
	//会话
	sockaddr_in clientaddr{ 0 };
	int size = sizeof(clientaddr);
	while (1)
	{
		//获取客户端socket 
		SOCKET client = accept(server, (SOCKADDR*)&clientaddr, &size);//为了实现我们的多人通信,多个客户端与服务端对话时需要我们去存储多个客户端的socket,实现我们的多人聊天
		//所以这里我们通过线程来控制,通过c++中的vector容器来存储我们的socket
		client_chat.push_back(client);
		CreateThread(NULL, 0X100, Chat_fun, (VOID*)&client, 0, NULL);
		printf("客户端%u连接到了服务器\n", client);
	}
	return 0;
}

客户端:

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
DWORD WINAPI Chat_fun(
	LPVOID lpParameter
)
{
	char buff[0x100]{ 0 };
	SOCKET client = (SOCKET)lpParameter;
	while (recv(client,buff,0x100,0)>0)
	{
		printf("%s\n", buff);
	}
	return 0;
}

int main()
{
	//初始化网络环境
	WSADATA date{ 0 };
	WSAStartup(MAKEWORD(2, 2), &date);//WSAStartup 函数通过进程启动 Winsock DLL 的使用
	//创建socket
	SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定socket的端口号IP地址
	sockaddr_in severaddr{ 0 };
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &severaddr.sin_addr);
	int result = connect(client, (SOCKADDR*)&severaddr, sizeof(severaddr));
	if (result)
	{
		printf("连接失败");
		return 0;
	}
	HANDLE hthread =  CreateThread(NULL, 0X100, Chat_fun, (VOID*)&client, 0, NULL);
	char buff[0x100]{ 0 };
	while (scanf_s("%s",buff,0x100)&&strcmp(buff,"exit"))
	{
		send(client, buff, strlen(buff) + 1, 0);
	}
	closesocket(client);
	CloseHandle(hthread);
	return 0;
}
  1. 这样的情况对于我们的多人聊天是已经完成了,但是每有一个客户端进行多人聊天时,我们就需要进行线程的开辟,那么假如有成千上万的客户端进行通讯(比如qq,wechat),那我们申请的内存将会是巨大的。

所以面对于这样的一种情况,我们需要做的就是通过IOCP (input/output completion port),通过cup能够承受的最合理的线程进行发配线程,通过队列的形式,假如有聊天的通信就步入队列,让IOCP来分配线程进行聊天

最合理线程数=cup内核数量*2

优化服务器:

#include<WinSock2.h>
#include<Windows.h>
#include<iostream>
#include<WS2tcpip.h>
#include<vector>
typedef struct _my_overlapped
{
	WSABUF buff;
	OVERLAPPED overlapped;
}MT_OVERLAPPED, * LPMY_OVERAPPED;
using std::vector;
vector<SOCKET> client_chat;
#pragma comment(lib,"ws2_32.lib")
DWORD WINAPI Chat_fun(
	LPVOID lpParameter
)
{
	BOOL result = FALSE;
	HANDLE hComPort = (HANDLE)lpParameter;
	DWORD num_byte_Tranf;
	ULONG_PTR ComPortKey;
	LPMY_OVERAPPED LP_Over_Lapped;
	DWORD flag;
	while (true)
	{
		result = GetQueuedCompletionStatus(hComPort, &num_byte_Tranf, &ComPortKey, (LPOVERLAPPED*)&LP_Over_Lapped, INFINITE);
		if (result && num_byte_Tranf > 0)
		{
			for (int i = 0; i < client_chat.size(); i++)
			{
				if (client_chat[i] != ComPortKey)
				{
					send(client_chat[i], LP_Over_Lapped->buff.buf, LP_Over_Lapped->buff.len, 0);
				}
			}
			//清空并重新初始化overlapped
			memset(&LP_Over_Lapped->overlapped, 0, sizeof(OVERLAPPED));
			memset(LP_Over_Lapped->buff.buf, 0, 0x100);
			WSARecv(ComPortKey, &LP_Over_Lapped->buff, num_byte_Tranf, &flag, 0, (LPWSAOVERLAPPED)LP_Over_Lapped, NULL);

		}
		else
		{
			for (int i = 0; i < client_chat.size(); i++)
				if (client_chat[i] == ComPortKey)
				{
					closesocket(ComPortKey);
					client_chat.erase(client_chat.begin() + i);
					printf("客户端%u断开了连接\n", ComPortKey);
				}
		}
	}
	return 0;
}
int main()
{
	//创建IOCP对象
	HANDLE hCompletPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
	//获取内核数量
	SYSTEM_INFO sys_info{ 0 };
	GetNativeSystemInfo(&sys_info);
	for (int i = 0; i < sys_info.dwNumberOfProcessors * 2; i++)
	{
		CreateThread(NULL, NULL, Chat_fun, hCompletPort, NULL, NULL);
	}
	//初始化网络环境
	WSADATA date{ 0 };
	WSAStartup(MAKEWORD(2, 2), &date);//WSAStartup 函数通过进程启动 Winsock DLL 的使用
	//创建socket
	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//绑定socket的端口号IP地址
	sockaddr_in severaddr{ 0 };
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", &severaddr.sin_addr);
	bind(server, (SOCKADDR*)&severaddr, sizeof(severaddr));
	//监听
	listen(server, SOMAXCONN);
	//会话
	sockaddr_in clientaddr{ 0 };
	int size = sizeof(clientaddr);
	while (1)
	{
		//获取客户端socket 
		SOCKET client = accept(server, (SOCKADDR*)&clientaddr, &size);//为了实现我们的多人通信,多个客户端与服务端对话时需要我们去存储多个客户端的socket,实现我们的多人聊天
		//所以这里我们通过线程来控制,通过c++中的vector容器来存储我们的socket
		client_chat.push_back(client);
		//将socket关联到IOCP
		CreateIoCompletionPort((HANDLE)client, hCompletPort, client, 0);//创建输入/输出 (I/O) 完成端口,并将其与指定的文件句柄相关联,或者创建尚未与文件句柄关联的 I/O 完成端口,以便在以后进行关联。
		LPMY_OVERAPPED LP_Over_Lapped = new MT_OVERLAPPED;
		LP_Over_Lapped->buff.buf = new char[0x100];
		LP_Over_Lapped->buff.len = 0x100;
		printf("客户端%u连接到了服务器\n", client);
	}
	return 0;
}
posted @ 2024-05-31 15:39  fisherman-ovo  阅读(39)  评论(0)    收藏  举报