【基于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.mzph.cn/diannao/79909.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA:利用 Apache Tika 提取文件内容的技术指南

1、简述 Apache Tika 是一个强大的工具,用于从各种文件中提取内容和元数据。📄Tika 支持解析文档、📸图像、🎵音频、🎥视频文件以及其他多种格式,非常适合构建🔍搜索引擎、📂内容管理系统和📊数据分析工具。 样例代码:https://gitee.com/lhdxhl/springboot-…

数码管静态显示一位字符(STC89C52单片机)

#include <reg52.h> sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4; //用数组来存储数码管的真值表&#xff0c;数组将在下一章详细介绍 unsigned char code LedChar[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82…

计算机视觉与深度学习 | 工业视觉缺陷检测如何检小缺陷?背景概述,原理,检测难点,常用的检测算法,算法评估指标,新项目算法选择,算法部署

工业视觉小缺陷检测技术解析 背景概述 工业视觉缺陷检测是智能制造中质量控制的核心环节,而小缺陷检测(如微米级划痕、点状污渍、细微裂纹等)因其目标小、易受干扰等特点,成为技术难点。随着制造业对精度要求提升(如3C电子、半导体、精密零部件行业),传统人工目检和基…

OBS 日期时间.毫秒时间脚本 date-and-time.lua

文章目录 OBS 日期时间.毫秒时间脚本&#xff1a;效果 OBS 日期时间.毫秒时间脚本&#xff1a; obs obslua source_name ""last_text "" format_string "" activated false-- 此函数用于获取精确的毫秒级时间戳&#…

进程和线程(1)

前言&#xff1a; 在计算机中cpu就像一座工厂&#xff0c;这个工厂里面有许多的车间&#xff0c;但是假如工厂的电力有限&#xff0c;一次只能供给一个车间使用&#xff0c;也就是说当一个车间在进行工作的时候&#xff0c;其他车间是不能工作的&#xff08;单个cpu只能运行一…

入门-C编程基础部分:16、 预处理器

飞书文档https://x509p6c8to.feishu.cn/wiki/DzSJwsGiTiXkeCkyEYUcuXbKnbf C 预处理是编译过程中一个单独的步骤&#xff0c;是一个文本替换工具而已。所有的预处理命令都是以井号&#xff08;#&#xff09;开头。 指令描述#define定义宏#ifdef如果宏已经定义&#xff0c;则返…

Ubuntu下安装和卸载MySQL

Ubuntu下安装和卸载MySQL 下面的演示系统版本&#xff1a;Ubuntu 24.04 更新系统软件包 在开始安装之前&#xff0c;建议先更新系统的软件包列表&#xff0c;以确保所有依赖项是最新的。 sudo apt update && sudo apt upgrade -y安装MySQL服务器 Ubuntu的官方软件…

【Python爬虫实战篇】--爬取豆瓣电影信息(静态网页)

网站&#xff0c;&#xff1a;豆瓣电影 Top 250 爬取豆瓣前250电影的信息&#xff0c; F12打开网页控制台&#xff0c;查看网页元素&#xff0c; 发现网页数据直接可以查看到&#xff0c;为静态网页数据&#xff0c;较为简单 目录 1.第一步使用urllib库获取网页 2.第二步使…

【Unity知识点详解】Unity中泛型单例的使用,兼容WebGL

今天来讲下Unity中泛型单例的使用&#xff0c;包含普通单例和继承MonoBehaviour的单例。重点是需要两种泛型单例兼容WebGL平台&#xff0c;话不多说直接开始。 泛型单例的设计目标 作为泛型单例&#xff0c;需要实现以下几个目标&#xff1a; 全局唯一&#xff0c;在程序的整个…

Python进程与线程的深度对比

一、核心概念对比 1. 进程&#xff08;Process&#xff09; 操作系统级独立单元&#xff1a;每个进程拥有独立的内存空间&#xff08;堆、栈、代码段&#xff09; 资源隔离性&#xff1a;崩溃不影响其他进程 多核并行&#xff1a;可充分利用多核CPU资源 2. 线程&#xff0…

Django 入门指南:构建强大的 Web 应用程序

什么是 Django&#xff1f; Django 是一个开源的高层次 Python Web 框架&#xff0c;旨在快速开发安全且可维护的网站。它通过简化常见的 Web 开发任务&#xff0c;帮助开发者专注于开发应用的核心功能。Django 实现了“快速开发”和“尽量少的重复”的理念&#xff0c;提供了…

ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(显示输出类外设之IS31FL3216)

目录 ESP-ADF外设子系统深度解析&#xff1a;esp_peripherals组件架构与核心设计&#xff08;显示输出类外设之IS31FL3216&#xff09;简介模块概述功能定义架构位置核心特性 IS31FL3216外设分析IS31FL3216外设概述IS31FL3216外设层次架构图 IS31FL3216外设API和数据结构外设层…

【计算机网络 | 第三篇】常见的网络协议(二)

没有看过我写的关于网络协议的第一篇博客可以看【计算机网络 | 第二篇】常见的通信协议&#xff08;一&#xff09;-CSDN博客 TCP的三次握手和四次挥手 TCP三次握手 三次握手是TCP协议建立可靠连接的过程&#xff0c;目的是确保客户端和服务端双方的双向通信能力正常&#x…

HAL库(STM32CubeMX)——高级ADC学习、HRTIM(STM32G474RBT6)

系列文章目录 文章目录 系列文章目录前言存在的问题HRTIMcubemx配置前言 对cubemx的ADC的设置进行补充 ADCs_Common_Settings Mode:ADC 模式 Independent mod 独立 ADC 模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时是双模式,在双模式下还有很多细分模式可选 ADC_Se…

Unity接入安卓SDK(3)厘清Gradle的版本

接入过程中&#xff0c;很多人遇到gradle的各种错误&#xff0c;由于对各种gradle版本的概念不甚了了&#xff0c;模模糊糊一顿操作猛如虎&#xff0c;糊弄的能编译通过就万事大吉&#xff0c;下次再遇到又是一脸懵逼。所以我们还是一起先厘清gradle的版本概念。 1 明晰概念 …

python-67-基于plotly的绘图可视化和智能推荐图表

文章目录 1 各种图表的适用场景1.1 面积图1.2 饼图1.3 散点图1.3.1 散点1.3.2 散点加线1.4 折线图1.5 箱线图1.5.1 不同类别的箱线图1.5.2 一个变量的箱线图1.5.3 多个变量的箱线图1.6 小提琴图1.6.1 不同类别的小提琴图1.6.2 一个变量的小提琴图1.7 直方图1.7.1 直方图1.7.2 分…

Spring AI MCP

MCP是什么 MCP是模型上下文协议&#xff08;Model Context Protocol&#xff09;的简称&#xff0c;是一个开源协议&#xff0c;由Anthropic&#xff08;Claude开发公司&#xff09;开发&#xff0c;旨在让大型语言模型&#xff08;LLM&#xff09;能够以标准化的方式连接到外…

c++_csp-j算法 (3)

弗洛伊德算法&#xff08;Floyd&#xff09; Floyd算法又称为插点法&#xff0c;是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法&#xff0c;与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特弗洛伊…

QT常见输入类控件及其属性

Line Edit QLineEdit用来表示单行输入框&#xff0c;可以输入一段文本&#xff0c;但是不能换行 核心属性&#xff1a; 核心信号 信号 说明 void cursorPositionChanged(int old,int new) 当鼠标移动时发出此型号&#xff0c;old为先前位置&#xff0c;new为新位置 void …

【k8s系列1】一主两从结构的环境准备

环境准备 虚拟机软件准备及安装&#xff0c;这里就不详细展开了&#xff0c;可以看文章:【一、虚拟机vmware安装】 linux环境准备及下载&#xff0c;下载镜像centOS7.9&#xff0c;以前也有写过这个步骤的文章&#xff0c;可以看&#xff1a;【二、安装centOS】 开始进入正题…