#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或者updSOCKET 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);//释放stocketclosesocket(socketServer);//清理网络库WSACleanup();return -1;}/*监听本机(服务器)的套接字*/if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)){string ret = to_string(WSAGetLastError());MessageBoxA(0, "error_listen", ret.c_str(), 0);//释放stocketclosesocket(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)){//释放stocketclosesocket(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);//释放stocketclosesocket(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);//释放stocketclosesocket(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 +1struct 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,这些函数采用异步方式,解决了堵塞