#include<WinSock2.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<stdlib.h>
#pragma comment(lib,"ws2_32.lib")
//消息的本身就是一个数
#define UM_ASYNCSELECTMSG WM_USER+1//用WM_USER是一个宏,该宏以下的是系统定义的消息,该宏往上的则是自定义的消息
LRESULT CALLBACK WinBackProc(HWND hwndw, UINT msgID, WPARAM wparam, LPARAM lparam);//声明回调函数
//解释一下句柄:就是一个东西的ID的意思,如窗口的ID就是窗口的句柄,菜单的ID就是菜单的句柄,其他的以此类推;
//定义一个socket结构体方便每次使用后删除socket
#define MAX_SOCK_COUNT 1024
SOCKET g_sockALL[MAX_SOCK_COUNT];
int g_count = 0;
int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hPreInstance,LPSTR IpCmdLine,int nShowCmd)//参数列表(窗口句柄、窗口句柄、命令行参数传递、窗口显示方式)
{
//创建窗口结构体
WNDCLASSEX wc;
wc.cbClsExtra = 0;//窗口结构体额外的空间数据,一般用不到,既设置为0即可
wc.cbSize = sizeof(WNDCLASSEX);//结构体的大小
wc.cbWndExtra =0;//同带外数据
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 hwndw = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,"我的窗口","我的子窗口", WS_OVERLAPPEDWINDOW,200,200,600,400,NULL,NULL, hinstance, WinBackProc);//参数:样式,关联窗口的名称,当前的窗口名字,当前窗口的属性,起始坐标x,y两个参数,窗口的大小(两个参数),父窗口的句柄,菜单,当前窗口的句柄,回调函数
if (NULL== hwndw)
{
return 0;
}
//显示窗口
ShowWindow(hwndw,nShowCmd);
//更新窗口
UpdateWindow(hwndw);
WORD wdVersion = MAKEWORD(2, 2);//定义网路库的版本,当前版本号为2.2版;
WSADATA wdScokMsg;//结构体定义网络库的信息;
int nRes = WSAStartup(wdVersion, &wdScokMsg);//打开网络库,返回值为int类型
if (0 != nRes)//返回值为0则代表打开网络库成功,否则则返回相应的错误码;
{
switch (nRes)//打开失败
{
case WSASYSNOTREADY:
printf("重启计算机!");
break;
case WSAEINPROGRESS:
printf("更新网络库!");
break;
}
return 0;//每次的打开失败都应该设定返回值为0结束程序的运行
}
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//地址的类型,套接字的类型,协议的类型。
if (INVALID_SOCKET == socketServer)
{
int a = WSAGetLastError();//检测错误,检测离其最近的函数的错误码
//执行成功之后返回的值为0;
WSACleanup();
return 0;
}
if (2 != HIBYTE(wdScokMsg.wVersion) || LOBYTE(wdScokMsg.wVersion) != 2)
{
WSACleanup();//关闭网络库
return 0;
}
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int res = bind(socketServer, (const struct sockaddr*) & si, sizeof(si));
if (SOCKET_ERROR == res)
{
int a = WSAGetLastError();//有错误
closesocket(socketServer);//关闭当前的套接字。要在关闭网络库之前。
WSACleanup();//关闭网络库
return 0;
}
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))//监听事件函数,第一个参数套接字对象,第二个是积压的缓存队列,如上定义后则是让系统自己取寻找合适的值。
{
int a = WSAGetLastError();//有错误
closesocket(socketServer);//关闭当前的套接字。要在关闭网络库之前。
WSACleanup();//关闭网络库
return 0;
}
if(SOCKET_ERROR == WSAAsyncSelect(socketServer, hwndw, UM_ASYNCSELECTMSG, FD_ACCEPT))//有客户端的socket链接则发出自定义的消息
{
int a = WSAGetLastError();//有错误
closesocket(socketServer);//关闭当前的套接字。要在关闭网络库之前。
WSACleanup();//关闭网络库
return 0;
}
g_sockALL[g_count] = socketServer;//储存服务器结构体
g_count++;
//消息循环
MSG msg;
while (GetMessage(&msg,NULL,0,0))//得到消息
{
TranslateMessage(&msg);//翻译消息
DispatchMessageA(&msg);//分发消息-》调用回调函数
}
//关闭socket
for (int i=0;i<g_count;i++)
{
closesocket(g_sockALL[i]);
}
return 0;
}
int x = 0;
/*
解释调用约定
常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。
stdcall调用约定声明的语法为(以前文的那个函数为例):
int __stdcall function(int a,int b)
stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈 3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸*/
LRESULT CALLBACK WinBackProc(HWND hwndw,UINT msgID,WPARAM wparam,LPARAM lparam)//回调函数 WPARAM:获取发送消息的客户端的socket
{
HDC hdc = GetDC(hwndw);//得到窗口内执行区域的句柄
switch (msgID)
{
case UM_ASYNCSELECTMSG://将自定义的消息绑定到回调函数中
{
//MessageBoxA(NULL, "有信号", "提示窗口", MB_OK);
SOCKET sock = (SOCKET)wparam;//收到的是服务器的套接字
//获取消息
if (0 != HIWORD(lparam))//lparam接受具体的消息,分高位与低位,高位表示错误码,低位表示具体的消息
{
break;
}
//具体消息
switch (LOWORD(lparam))
{
case FD_ACCEPT://收到服务器socket传来的accept信息并做accept处理
{
//在窗口上打印输出
TextOut(hdc,0,x,"accept",strlen("accept"));//用strlen输出的长度不带/0/用sizeof输出的长度带/0
x += 15;
SOCKET socketClient = accept(sock, NULL, NULL);//做accept处理建立新的客户端socket
if (INVALID_SOCKET==socketClient)
{
//出错了
int a = WSAGetLastError();
break;
}
//将客户端投递给消息队列
if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hwndw, UM_ASYNCSELECTMSG, FD_READ| FD_WRITE|FD_CLOSE))//有客户端的socket发出消息则继续处理
{
//出错了
int a = WSAGetLastError();
//释放
closesocket(socketClient);
break;
}
g_sockALL[g_count] = socketClient;//储存客户端结构体
g_count++;
}
break;
case FD_READ:
{
TextOut(hdc, 0, x, "read", strlen("read"));
char str[1024] = { 0 };
if (SOCKET_ERROR == recv(sock, str, 1023, 0))//收到read消息调用recv函数
{
break;
}
TextOut(hdc, 30, x, str, strlen(str));
x += 15;
}
break;
case FD_WRITE:
TextOut(hdc, 0, x, "write", strlen("write"));
x += 15;
break;
case FD_CLOSE:
//关闭该socket上的消息
WSAAsyncSelect(sock,hwndw,0,0);//继续调用WSAAsyncSelect函数将后两个参数置零便是关闭消息
//关闭socket
closesocket(sock);
//记录数组中删除该socket
for (int i=0;i<g_count;i++)
{
if (sock==g_sockALL[i])
{
g_sockALL[i] = g_sockALL[g_count - 1];
g_count--;
break;
}
}
break;
}
}
break;
case WM_DESTROY://WM开头的是系统给的消息
PostQuitMessage(0);
break;
}
ReleaseDC(hwndw,hdc);//关闭窗口内执行区域的句柄
return DefWindowProc(hwndw,msgID,wparam,lparam);
//DefWindowProc(hwndw,msgID,wparam,lparam);函数功能:该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。
//该函数确保每一个消息得到处理。调用DefWindowProc函数时使用窗口过程接收的相同参数。
}
C语言底层网络编程——异步选择模型(服务器端)
最新推荐文章于 2024-08-04 21:56:41 发布