TCP-消息队列模型

复制代码
#include "main.h"
fd_win_set setSockets;

VOID Server_write_error()
{

}

int cteateserver(HWND hwnd)
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* 使用Windef.h中声明的MAKEWORD(低字节、高字节)宏 */
    wVersionRequested = MAKEWORD(2, 2);

    /*启用网络链接库,调用的封装库命令*/
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return -1;
    }

    /*确认WinSock DLL支持2.2*/

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        //清理网络库
        WSACleanup();
        return -1;
    }


    //创建套接字。 创建网络类型 tcp或者upd
    SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (INVALID_SOCKET == socketServer)
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_socket",ret.c_str(),  0);
        //清理网络库
        WSACleanup();
        return -1;
    }
    //设置sockaddr结构
    sockaddr_in saServer;
    saServer.sin_family = AF_INET;
    saServer.sin_addr.s_addr = INADDR_ANY;
    saServer.sin_port = htons(9999);



    // 绑定本机(服务器)IP和端口
    //sockaddr结构中的信息
    if (SOCKET_ERROR == bind(socketServer, (SOCKADDR*)&saServer, sizeof(saServer)))
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_bind", ret.c_str(),  0);
        //释放stocket
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return -1;
    }

    /*监听本机(服务器)的套接字*/

    if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
    {
        string ret = to_string(WSAGetLastError());
        MessageBoxA(0, "error_listen", ret.c_str(),  0);
        //释放stocket
        closesocket(socketServer);
        //清理网络库
        WSACleanup();
        return -1;
    }

    //将socket和消息绑定 并投递给消息队列管理,和事件类似
    WSAAsyncSelect(socketServer, hwnd, WSAAsyncSelectMsg, FD_ACCEPT);

    /*将服务器socket句柄写进结构体*/
    setSockets.sockall[setSockets.count] = socketServer;
    setSockets.count++;
}


int AsyncSelectMsg(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC DC = GetDC(hWnd);
    //参数三存放的是响应的socket句柄
    SOCKET socket = (SOCKET)wParam;
    //参数四可以获取句柄信息
    //if (0 != HIWORD(lParam))//高位保存错误码 ,退出也属于错误码
    //   return -1;

    switch (LOWORD(lParam))
    {
    case FD_ACCEPT:
    {
        sockaddr_in clientMsg = { 0 };
        int clientMsg_size = sizeof(clientMsg);
        /*得到客户端信息并返回客户端socket*/
        SOCKET socketClient = accept(socket, (sockaddr*)&clientMsg, &clientMsg_size);
        printf("%d.%d.%d.%d.%d \n", clientMsg.sin_addr.S_un.S_un_b.s_b1,
            clientMsg.sin_addr.S_un.S_un_b.s_b2,
            clientMsg.sin_addr.S_un.S_un_b.s_b3,
            clientMsg.sin_addr.S_un.S_un_b.s_b4,
            clientMsg.sin_port);
        TextOutA(DC,0,0,"ACCEPT",strlen("ACCEPT"));
        MessageBox(0,"ACCEPT","",0);
        if (INVALID_SOCKET == socketClient)
            break;
        //将socket和消息绑定 并投递给消息队列管理,和事件类似
        if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hWnd, WSAAsyncSelectMsg, FD_WRITE | FD_READ | FD_CLOSE))
        {
            //释放stocket
            closesocket(socketClient);
            break;
        }
        /*将客户端socket句柄写进结构体*/
        setSockets.sockall[setSockets.count] = socketClient;
        setSockets.count++;
    }
    break;
    case FD_WRITE:
    {
        //accept会触发一次,一般用于链接服务器后初始化
        char sendmsg[] = "connect_success";
        if (SOCKET_ERROR == send(socket, sendmsg, strlen(sendmsg), 0))
        {
            break;
        }
    }
    break;
    case FD_READ:
    {
        char recvmsg[1024] = { 0 };
        if (SOCKET_ERROR == recv(socket, recvmsg, sizeof(recvmsg), 0))
        {
            break;
        }
        printf("read:%s\n", recvmsg);

        char sendmsg[] = "recv_success";
        if (SOCKET_ERROR == send(socket, sendmsg, strlen(sendmsg), 0))
        {
            break;
        }
    }
    break;
    case FD_CLOSE:
    {
        MessageBox(0, "FD_CLOSE", "", 0);
  
        //释放stocket
        closesocket(socket);
        //关闭事件对象
        WSAAsyncSelect(socket, hWnd, 0, 0);

        /*将这个事件和socket移除数组,取巧方法:由于事件是无序的,把数组最后一个数据放进移除的数据的位置,并将数组大小-1*/
        for (UINT i = 0; i < setSockets.count; i++)
        {
            if (socket == setSockets.sockall[i])
            {
                setSockets.sockall[i] = setSockets.sockall[setSockets.count - 1];//数组从0开始,-1才是正确位置
                setSockets.count--;
                break;
            }
        }
    }
    break;
    }

    return 1;
}



/*窗口消息回调,每次只处理一个消息*/
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WSAAsyncSelectMsg:

        AsyncSelectMsg(hWnd, message, wParam, lParam);

        break;

    case WM_CREATE://窗口初始化,只执行一次
        break;

    case WM_DESTROY://关闭释放窗口
        PostQuitMessage(0);//退出
        break;

    }
	//处理我们没有处理的消息
	return DefWindowProc(hWnd, message, wParam, lParam);
}

int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{

	// 注册窗口结构体
	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_VREDRAW| CS_HREDRAW, WindowProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, TEXT("server"), NULL };
	::RegisterClassEx(&wc);

	//创建窗口
	HWND windowshWnd = CreateWindowEx(WS_EX_TOPMOST  , wc.lpszClassName, TEXT("server"), WS_OVERLAPPEDWINDOW,0,0, 500,500, NULL, NULL, wc.hInstance, NULL);

	//显示窗口
	ShowWindow(windowshWnd, SW_SHOWNORMAL);//SW_SHOWNORMAL  或者 SW_SHOW

	//更新窗口
	UpdateWindow(windowshWnd);

    //创建服务器
    cteateserver(windowshWnd);


	// 主消息循环:
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		//将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列
		TranslateMessage(&msg);
		//将消息发送到窗口回调处理
		DispatchMessage(&msg);
	}


    //    /*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
    for (UINT i = 0; i < setSockets.count; i++)
    {
        //取消窗口消息和socket的绑定
        WSAAsyncSelect(setSockets.sockall[i], windowshWnd, 0, 0);
        //释放stocket
        closesocket(setSockets.sockall[i]);
    }
        //清理网络库
    WSACleanup();

	return (int)msg.wParam;

}

#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#define FD_SETSIZE 1024
#include <winsock2.h>
#include <windows.h>
#include <string>

#include <iostream>

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


using namespace std;

//自定义的消息值不能和系统消息冲突
#define WSAAsyncSelectMsg    WM_USER +1

struct fd_es_set
{
    UINT count;
    SOCKET sockall[1024];
    HANDLE eventall[1024];
};


struct fd_win_set
{
    UINT count;
    SOCKET sockall[1024];
};

利用windows的消息队列函数,对比事件消息,好处是消息接收更合理,按客户端发送消息的顺序会依次处理,但是由于send accept recv这些函数天生存在缺陷,是同步处理的,这些函数必须处理完一个消息才能往下处理另一个消息,会造成堵塞,故windows设计了了新的套接字类型WSAsocket ,新函数AccpetEx WSAaccept WSAsend,这些函数采用异步方式,解决了堵塞

相关推荐
BingoGo15 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack15 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo3 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端