目录
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;
}