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,这些函数采用异步方式,解决了堵塞

相关推荐
敲代码不忘补水几秒前
生成式GPT商品推荐:精准满足用户需求
开发语言·python·gpt·产品运营·产品经理
网络安全-杰克4 分钟前
关于网络安全里蜜罐的详细介绍
安全·web安全·php
清风fu杨柳26 分钟前
centos7 arm版本编译qt5.6.3详细说明
开发语言·arm开发·qt
醉颜凉29 分钟前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
_小柏_31 分钟前
C/C++基础知识复习(20)
开发语言
程序员小明z41 分钟前
基于Java的药店管理系统
java·开发语言·spring boot·毕业设计·毕设
我是哈哈hh1 小时前
HTML5和CSS3的进阶_HTML5和CSS3的新增特性
开发语言·前端·css·html·css3·html5·web
Dontla2 小时前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
Neophyte06083 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
慕容复之巅3 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab