网络编程 异步选择模型

目录

1.概念

 2.代码样例


1.概念

        基本概念,在这一个模型中的代码使用到了vs中窗口应用程序,可以看这一片文章https://blog.csdn.net/weixin_62859191/article/details/128415737?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_62859191/article/details/128415737?spm=1001.2014.3001.5501

 2.代码样例

        代码实现的流程

        在这里使用了vs的窗口信息,代码可以在上面的链接中去拿,在异步选择模型中代码主要的区别是函数WSAAsyncSelect,该函数把事件和socket绑定并且投递给系统,函数原型

int WSAAPI WSAAsyncSelect(
  [in] SOCKET s,
  [in] HWND   hWnd,
  [in] u_int  wMsg,
  [in] long   lEvent
);

参数1 s:需要绑定的客户端socket

参数2 HWND:控制台窗口的句柄

参数3 wMsg:客户端发出的消息

参数4 lEvent:投递的事件

        返回值:成功返回0,失败返回SOCKET_ERROR

##代码实例

在代码中,把事件进行分类处理都需要在窗口的回调函数中实现,回调函数中的第三个参数可以获取到服务器的socket

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include<stdio.h>
#include<Winsock2.h>
#pragma comment(lib, "ws2_32.lib")

#define UM_ANYNCSELECT WM_USER+1//WM_USER表示系统处理消息所占数量,在这之后就是客户端的消息
//定义一个数组记录socket
#define MAX_SOCK_COUNT 1024
SOCKET fd_socket[MAX_SOCK_COUNT];
int sock_count = 0;
int y = 0;//让输出的位置不断往下移

LRESULT CALLBACK WinBackProc(HWND hWnd, UINT msgID, WPARAM wparam, LPARAM lparam);//窗口的回调函数
SOCKET SocketServer();//把网络库封装成函数

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nShowCmd)
{
	//第一步 创建结构体窗口
	WNDCLASSEX wc;
	wc.cbClsExtra = 0;//类的额外空间
	wc.cbWndExtra = 0;//窗口的额外空间
	wc.cbSize = sizeof(WNDCLASSEX);//窗口大小
	wc.hbrBackground = NULL; //背景颜色
	wc.hCursor = NULL;//鼠标图标
	wc.hIcon = NULL;//左上角图标
	wc.hIconSm = NULL;//任务栏图标
	wc.hInstance = hInstance;//窗口句柄
	wc.lpfnWndProc = WinBackProc;//回调函数
	wc.lpszClassName = "异步选择模型";//窗口名称
	wc.lpszMenuName = NULL;//菜单栏名称
	wc.style = CS_HREDRAW | CS_VREDRAW;//窗口风格 可以水平和垂直拉伸

	//第二步 注册结构体
	RegisterClassEx(&wc);

	//第三步 创建窗口
	HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "异步选择模型", "async", WS_OVERLAPPEDWINDOW, 200, 200, 800, 600,
		NULL, NULL, hInstance, NULL);
	if (hWnd == NULL)
	{
		return 0;
	}

	//第四步 显示窗口
	ShowWindow(hWnd, nShowCmd);

	//更新窗口
	UpdateWindow(hWnd);

	//创建socket
	SOCKET socketServer = SocketServer();
	//绑定事件和socket
	if (SOCKET_ERROR == WSAAsyncSelect(socketServer, hWnd, UM_ANYNCSELECT, FD_ACCEPT))//成功返回0,出错返回SOCKET_ERROR
	{
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//记录socket
	fd_socket[sock_count] = socketServer;
	sock_count++;

	//第五步 消息循环
	MSG msg;//结构体
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);//把获取到的消息翻译成代码,就是字符串
		DispatchMessage(&msg);//把消息分发
	}

	for (int i = 0; i < sock_count; i++)
	{
		closesocket(fd_socket[i]);
	}
	WSACleanup();
	return 0;
}

//回调函数
LRESULT CALLBACK WinBackProc(HWND hWnd, UINT msgID, WPARAM wparam, LPARAM lparam)
{
	HDC hdc = GetDC(hWnd);//获取窗口的画布信息
	switch (msgID)
	{
	case UM_ANYNCSELECT://在switch里是不可以直接定义变量的
	{
		//MessageBox(NULL, L"有信号连接", L"提示窗口", MB_OK);
		//获取socket
		SOCKET sock = (SOCKET)wparam;
		//获取消息
		if (0 != HIWORD(lparam))//lparam存放的时是客户端触发的消息,使用HIWORD可以判断消息是否正确
		{
			if (WSAECONNABORTED == HIWORD(lparam))
			{
				TextOut(hdc, 0, y, "close", (int)strlen("close"));
				y += 15;
				//关闭socket上的消息
				WSAAsyncSelect(sock, hWnd, 0, 0);//把后面两个参数置为0即为删除
				//删除数组中的socket
				for (int i = 0; i < sock_count; i++)
				{
					if (sock == fd_socket[i])
					{
						fd_socket[i] = fd_socket[sock_count - 1];
						sock_count--;
						break;
					}
				}
				//关闭socket
				closesocket(sock);
			}
			break;
		}
		//具体消息
		switch (LOWORD(lparam))//可以获取到具体的消息
		{
		case FD_ACCEPT://有请求连接
		{
			SOCKET socketClient = accept(sock, NULL, NULL);
			if (socketClient == INVALID_SOCKET)
			{
				printf("创建客户端socket失败\n");
				//出错了
				int a = WSAGetLastError();
				break;
			}
			//绑定客户端信息,将客户端投递给消息队列
			if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hWnd, UM_ANYNCSELECT, FD_READ | FD_WRITE | FD_CLOSE))
			{
				//出错了
				int a = WSAGetLastError();
				closesocket(socketClient);
				break;
			}
			TextOut(hdc, 0, y, "accept succee", (int)strlen("accept succee"));//在窗口输出信息
			y += 15;
			//记录socket
			fd_socket[sock_count] = socketClient;
			sock_count++;
		}
			break;
		case FD_READ:
		{
			TextOut(hdc, 0, y, "read:", (int)strlen("read:"));
			char buf[1500] = { 0 };
			if (SOCKET_ERROR == recv(sock, buf, 1499, 0))
			{
				int a = WSAGetLastError();
				break;
			}
			TextOut(hdc, 35, y, buf, (int)strlen(buf));
			y += 15;
		}
			break;
		case FD_WRITE:
			TextOut(hdc, 0, 15, "write", (int)strlen("write"));
			y += 15;
			break;
		case FD_CLOSE:
			TextOut(hdc, 0, y, "close", (int)strlen("close"));
			y += 15;
			//关闭socket上的消息
			WSAAsyncSelect(sock, hWnd, 0, 0);//把后面两个参数置为0即为删除
			//删除数组中的socket
			for (int i = 0; i < sock_count; i++)
			{
				if (sock == fd_socket[i])
				{
					fd_socket[i] = fd_socket[sock_count - 1];
					sock_count--;
					break;
				}
			}
			//关闭socket
			closesocket(sock);
			break;
		}
	}
		break;
	case WM_CREATE://初始化 只执行一次
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	ReleaseDC(hWnd,hdc);//释放窗口画布信息
	return DefWindowProc(hWnd, msgID, wparam, lparam);//默认处理消息的函数
}

SOCKET SocketServer()
{
	//第一步 打开网络库并校验版本
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocketMsg;
	int nRes = WSAStartup(wdVersion, &wdSocketMsg);
	if (nRes != 0)
	{
		printf("打开网络库失败\n");
		return 0;
	}
	if (HIBYTE(wdSocketMsg.wVersion) != 2 || LOBYTE(wdSocketMsg.wVersion) != 2)
	{
		printf("网络库版本出错\n");
		WSACleanup();
		return 0;
	}
	//第二步 创建socket
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (socketServer == INVALID_SOCKET)
	{
		printf("创建的socket无效\n");
		WSACleanup();
		return 0;
	}
	//第三步 绑定ip地址和端口号
	struct sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = htons(12332);
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof(si)))
	{
		printf("绑定失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	//第四步 开始监听
	if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
	{
		printf("监听失败\n");
		closesocket(socketServer);
		WSACleanup();
		return 0;
	}
	return socketServer;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱笑的蛐蛐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值