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

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

http://www.xdnf.cn/news/794.html

相关文章:

  • 云原生与AI的关系是怎么样的?
  • Jinja2 内置变量和函数详解
  • VScode-py环境
  • 【JS】计算任意字符串的像素宽度(px)
  • VR、AR、互动科技:武汉数字展馆制作引领未来展览新体验
  • 单例模式(线程安全)
  • Docker Compose 使用实例
  • 【漫话机器学习系列】214.停用词(Stop Words)
  • 查看MAC 地址以及简单了解
  • CHAPTER 11 A Pythonic Object
  • 定期检查滚珠丝杆的频率是多久?
  • Rust: 从内存地址信息看内存布局
  • OpenCV 图形API(44)颜色空间转换-----将图像从 BGR 色彩空间转换为 RGB 色彩空间函数BGR2RGB()
  • XMC4800 芯片深度解读:架构、特性、应用与开发指南
  • OpenCV中的图像旋转方法详解
  • 特征选择与类不平衡处理
  • aws服务--S3介绍使用代码集成
  • Missashe考研日记-day23
  • 在Ubuntu下用Chrony做主从机时间同步
  • 栈和字符串,力扣.43.字符串相乘力扣1047.删除字符串中的所有相邻重复项力扣.844比较含退格的字符串力扣227.基本计算器II
  • 《马尼拉》桌游期望计算器
  • Ubuntu下展锐刷机工具spd_dump使用说明
  • Python3网络爬虫开发--爬虫基础
  • Java 设计模式心法之第4篇 - 单例 (Singleton) 的正确打开方式与避坑指南
  • 每天学一个 Linux 命令(30):cut
  • 【React】搜索时高亮被搜索选中的文案
  • 大数据系列 | 详解基于Zookeeper或ClickHouse Keeper的ClickHouse集群部署--完结
  • TensorFlow和PyTorch学习原理解析
  • 掌握常见 HTTP 方法:GET、POST、PUT 到 CONNECT 全面梳理
  • FreeRTos学习记录--2.内存管理