当前位置: 首页 > news >正文

【基于WSAAsyncSelec模型的通信程序设计】

文章目录

    • 一、实验背景与目的
    • 二、实验设计与实现思路
      • 1. 设计思想
      • 2. 核心代码实现
    • 总结

一、实验背景与目的

这次实验主要是为了让大家了解基于 WSAAsyncSelect 模型通信程序的编写、编译和执行过程。通过实践操作,深入掌握这种模型在实现计算机之间通信时的应用。
任务是编写一个 Win32 程序,模拟两台计算机之间的双向通信。具体来说,客户端要向服务器端发送 “请输出从 1 到 1000 内所有的质数” 这条指令,然后服务器端要能准确回应结果,把 1 到 1000 内的质数全部找出来并发回给客户端。

二、实验设计与实现思路

1. 设计思想

在设计思想上,采用了WSAAsyncSelect 模型。这个模型基于消息通知机制,服务器端通过窗口过程函数来接收网络事件消息,比如有客户端连接请求(FD_ACCEPT)、可读数据(FD_READ)、连接关闭(FD_CLOSE)等,然后根据不同事件做出相应处理,实现高效的异步通信。

2. 核心代码实现

(1)客户端

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib
class CInitSock 
{public:CInitSock(BYTE minorVer = 2, BYTE majorVer = 2){// 初始化WS2_32.dllWSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if(::WSAStartup(sockVersion, &wsaData) != 0)return;}~CInitSock(){   ::WSACleanup(); }
};
CInitSock theSock;       //加载套接字库int main()
{// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(s == INVALID_SOCKET){printf(" Failed socket() \n");return 0;}// 也可以在这里调用bind函数绑定一个本地地址,无则系统将会自动安排// 填写远程地址信息sockaddr_in servAddr; servAddr.sin_family = AF_INET;servAddr.sin_port = htons(4567);//要连接的服务器地址servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//没有联网,直接使用127.0.0.1即可if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1){printf(" Failed connect() \n");return 0;}//发送数据char buf[] = "请输出从1到1000内所有的质数";printf("发送数据:%s\n",buf);Sleep(6);// 接收数据char buff[3000];int nRecv = ::recv(s, buff, 3000, 0);// 关闭套节字::closesocket(s);return 0;
}

(2)服务器端

#include <stdio.h>
#include <winsock2.h>
#include <math.h>#pragma comment(lib, "WS2_32.lib")#define WM_SOCKET WM_USER + 101 // 自定义消息class CInitSock {
public:CInitSock(BYTE minorVer = 2, BYTE majorVer = 2) {// 初始化WS2_32.dllWSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if (::WSAStartup(sockVersion, &wsaData) != 0)return;}~CInitSock() {::WSACleanup();}
};CInitSock theSock; // 加载套接字库bool isprime(int p) {if (p < 2) return false;int sq = (int)sqrt(p);for (int i = 2; i <= sq; i++) {if (p % i == 0)return false;}return true;
}char* getallprime(int n) {static char szprime[4096];strcpy(szprime, "质数:");for (int i = 2; i <= n; i++) {if (isprime(i)) {char sznum[10];itoa(i, sznum, 10);strcat(szprime, sznum);strcat(szprime, ","); // 附加分隔符}}size_t len = strlen(szprime);if (len > 0) {szprime[len - 1] = '\0'; // 替换最后一个逗号为字符串结束符}return szprime;
}LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);int main() {wchar_t szClassName[] = L"MainWClass"; // 使用宽字符WNDCLASSEX wndclass;wndclass.cbSize = sizeof(wndclass);wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WindowProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = NULL;wndclass.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = NULL;wndclass.lpszClassName = szClassName;wndclass.hIconSm = NULL;::RegisterClassEx(&wndclass);HWND hWnd = ::CreateWindowExW(0,szClassName,L"", // 用 L 表示宽字符WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,NULL,NULL);if (hWnd == NULL) {::MessageBoxW(NULL, L"创建窗口出错!", L"error", MB_OK);return -1;}USHORT nPort = 4567; // 此服务器监听的端口号SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(nPort);sin.sin_addr.S_un.S_addr = INADDR_ANY;if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) {printf("Failed bind() \n");return -1;}::WSAAsyncSelect(sListen, hWnd, WM_SOCKET, FD_ACCEPT | FD_CLOSE);::listen(sListen, 5);MSG msg;while (::GetMessage(&msg, NULL, 0, 0)) {::TranslateMessage(&msg);::DispatchMessage(&msg);}return msg.wParam;
}LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {switch (uMsg) {case WM_SOCKET: {SOCKET s = wParam;if (WSAGETSELECTERROR(lParam)) {::closesocket(s);return 0;}switch (WSAGETSELECTEVENT(lParam)) {case FD_ACCEPT: {SOCKET client = ::accept(s, NULL, NULL);::WSAAsyncSelect(client, hWnd, WM_SOCKET, FD_READ | FD_CLOSE);break;}case FD_READ: {char szText[1024] = { 0 };if (::recv(s, szText, sizeof(szText) - 1, 0) == SOCKET_ERROR) {::closesocket(s);}else {printf("接收数据:%s\n", szText);char* szReply = getallprime(1000);::send(s, szReply, strlen(szReply), 0);}break;}case FD_CLOSE: {::closesocket(s);break;}}}return 0;case WM_DESTROY:::PostQuitMessage(0);return 0;}return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

总结

在调试过程中,刚开始还遇到了一些小问题,比如数据接收不完整或者格式不对,但通过仔细检查代码,调整缓冲区大小、发送接收的顺序等,最终都顺利解决了。现在放一张关键截图,内容是服务器端成功接收客户端消息并回复以及客户端成功接收服务器返回质数结果的截图。
在这里插入图片描述

  • 遇到的问题
    1.端口号冲突问题
    一开始,我在设置服务器端口号的时候,随意选了一个号码,结果程序运行不起来。后来我琢磨着可能是端口号出了问题,于是换了一个没被占用的端口号,比如 4567 号,这才解决了问题。这让我认识到网络编程中端口号的选择很关键,要是选了已经被其他程序占用了的端口号,那通信肯定就建立不起来了。
    2.客户端连接失败问题
    客户端连接服务器的时候,也出现了连接不上的情况。我先是反复检查网络连接,确定没问题后,怀疑是服务器端的设置有误。后来仔细检查代码,发现是服务器端没有正确监听对应的端口号,把服务器端的监听端口号和客户端要连接的端口号设置成一致后,客户端就能成功连接了。
    3.数据接收不完整问题
    当服务器端向客户端发送数据时,有时候客户端接收的数据不完整。一开始我还以为是数据发送那边出了问题,后来仔细排查,发现是因为接收缓冲区的大小设置不够。于是我把客户端接收缓冲区的大小调大,这样一来,客户端就能完整地接收服务器端发送过来的数据了,这让我明白要根据实际的数据量大小合理设置缓冲区,不然很容易出现数据丢失或者接收不全的情况。
    4.网络通信中的超时问题

这次实验让我深深体会到网络编程的魅力和挑战。通过实际编写代码,我对 WSAAsyncSelect 模型有了更深入的理解,也掌握了如何利用消息循环处理异步网络事件。自己搭建起一个能正常通信的客户端 - 服务器系统,那种成就感简直爆棚。而且在这个过程中,我学会了更好地调试程序,关注错误处理,这对我今后学习更复杂的网络编程技术打下了坚实基础。在这个信息时代,网络通信是如此的重要,通过这次实验,我仿佛看到了网络背后那复杂而又精妙的运行机制,也更加坚定了我在网络安全领域深入学习的决心。我期待以后能参与更多有意思的项目,不断提升自己的编程能力和专业知识水平。

相关文章:

  • 每天学一个 Linux 命令(29):tail
  • JavaScript 中的单例模式
  • 单例模式(线程安全)
  • Uniapp 自定义TabBar + 动态菜单实现教程(Vuex状态管理详解)
  • Nginx详细使用
  • 乐家桌面安卓版2025下载-乐家桌面软件纯净版安装分享码大全
  • buildadmin 自定义单元格渲染
  • AOSP Android14 Launcher3——点击桌面图标启动应用动画流程
  • Docker安装beef-xss
  • Python3网络爬虫开发--爬虫基础
  • 多语言商城系统开发流程
  • 【Easylive】consumes = MediaType.MULTIPART_FORM_DATA_VALUE 与 @RequestPart
  • 【英语语法】词法---连词
  • Spring是如何实现资源文件的加载
  • LX5-STM32F103C8T6引脚分布与定义
  • longchain使用通义千问
  • 如何对只能有一个`public`顶层类这句话的理解
  • 大文件分片上传进阶版(新增md5校验、上传进度展示、并行控制,智能分片、加密上传、断点续传、自动重试),实现四位一体的网络感知型大文件传输系统‌
  • Maxscript调用Newtonsoft.Json解析Json
  • 从 TinyZero 到 APR:语言模型推理能力的探索与自适应并行化
  • 重返母校:哈佛大学医学院博士后陈则宇入职北大基础医学院
  • 福建浯屿岛垃圾排海追踪:堆存已清理,当地称今后会日产日清日运
  • 世界史圆桌|16-18世纪的跨太平洋贸易
  • 商务部:中国加快推进服务业扩大开放综合试点为世界注入更多确定性
  • 收缩非洲业务专注挖矿,裁减大批外交官,“美国务院改革草案”引争议
  • 俄方因复活节停止战斗行动,外交部:乐见一切通往停火的努力