首页 > 编程语言 >基于WSAAsyncSelect模型的通信程序设计

基于WSAAsyncSelect模型的通信程序设计

时间:2023-11-28 11:23:52浏览次数:43  
标签:SOCKET int 模型 WSAAsyncSelect wndclass 程序设计 接字 NULL 质数

基于WSAAsyncSelect模型的通信程序设计

一、问题描述

编写Win32程序模拟实现基于WSAAsyncSelect模型的两台计算机之间的通信,要求编程实现服务器端与客户端之间双向数据传递。客户端向服务器端发送“请输出从1到1000内所有的质数”,服务器回应客户端给出结果。

二、代码实现

①CInitSock.h具体代码:

 

 1 #pragma once
 2 #include <winsock2.h>
 3 #pragma comment(lib,"WS2_32")
 4 class CInitSock
 5 {
 6 public:
 7     CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
 8     {
 9         //初始化W2_32.dll
10         WSADATA wsaData;
11         WORD sockVersion = MAKEWORD(minorVer, majorVer);
12         if (::WSAStartup(sockVersion, &wsaData) != 0)
13         {
14             exit(0);
15         }
16     }
17     ~CInitSock()
18     {
19         ::WSACleanup();
20     }
21 };

 

 

 

②服务器端代码:

 

  1 #include "CInitSock.h"
  2 #include<iostream>
  3 #include<sstream>
  4 using namespace std;
  5 CInitSock initSock;
  6 
  7 //自定义网络通知消息:
  8 #define WM_SOCKET (WM_USER + 1)
  9 
 10 //判断质数的函数:
 11 boolean PNumJudgment(int num);
 12 char* AllThePNum(int start, int end);
 13 //判断质数的函数2:
 14 string calculatePrimeNumbers(int start, int end);
 15 
 16 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 17 int main()
 18 {
 19     char szClassName[] = "MainWClass";
 20     WNDCLASSEX wndclass;
 21     
 22     //用描述主窗口的参数填充WNDCLASSEX结构
 23     wndclass.cbSize = sizeof(wndclass);
 24     wndclass.style = CS_HREDRAW | CS_VREDRAW;
 25     wndclass.lpfnWndProc = WindowProc;
 26     wndclass.cbClsExtra = 0;
 27     wndclass.cbWndExtra = 0;
 28     wndclass.hInstance = NULL;
 29     wndclass.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
 30     wndclass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
 31     wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
 32     wndclass.lpszMenuName = NULL;
 33     wndclass.lpszClassName = szClassName;
 34     wndclass.hIconSm = NULL;
 35     ::RegisterClassEx(&wndclass);
 36 
 37 
 38     //创建主窗口
 39     HWND hWnd = ::CreateWindowEx(
 40         0,
 41         szClassName,
 42         "",
 43         WS_OVERLAPPEDWINDOW,
 44         CW_USEDEFAULT,
 45         CW_USEDEFAULT,
 46         CW_USEDEFAULT,
 47         CW_USEDEFAULT,
 48         NULL,
 49         NULL,
 50         NULL,
 51         NULL);
 52     if (hWnd == NULL)
 53     {
 54         ::MessageBox(NULL, "创建窗口出错!", "error", MB_OK);
 55         return -1;
 56     }
 57     USHORT nPort = 4567;
 58 
 59     //创建监听套接字
 60     SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 61     sockaddr_in sin;
 62     sin.sin_family = AF_INET;
 63     sin.sin_port = htons(nPort);
 64     sin.sin_addr.S_un.S_addr = INADDR_ANY;
 65 
 66     //绑定套接字到本地机器
 67     if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
 68     {
 69         printf("Failed bind()\n");
 70         return -1;
 71     }
 72 
 73     //将套接字设为窗口通知消息类型
 74     ::WSAAsyncSelect(sListen, hWnd, WM_SOCKET, FD_ACCEPT | FD_CLOSE);
 75     ::listen(sListen, 5);
 76 
 77     //从消息队列中取出消息
 78     MSG msg;
 79     while (::GetMessage(&msg, NULL, 0, 0))
 80     {
 81         ::TranslateMessage(&msg);//转化键盘消息 : 将键盘消息翻译成对应的字符消息
 82         ::DispatchMessage(&msg);//将消息发送到相应的窗口函数进行处理
 83     }
 84     return msg.wParam; //当GetMessage返回0时的程序结束
 85 
 86 }
 87 
 88 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 89 {
 90     switch (uMsg)
 91     {
 92     case WM_SOCKET:
 93     {
 94         SOCKET s = wParam;//取得有事件发生的套接字句柄
 95         //查看是否出错
 96         if (WSAGETSELECTERROR(lParam))
 97         {
 98             ::closesocket(s);
 99             return 0;
100         }
101         //处理发生的事件
102         switch (WSAGETSELECTEVENT(lParam))
103         {
104         case FD_ACCEPT: //监听中的套接字检测到有连接进入
105         {
106             SOCKET client = ::accept(s, NULL, NULL);
107             ::WSAAsyncSelect(client, hWnd, WM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE);//sListen client
108         }
109         break;
110         case FD_WRITE:
111         {}
112         break;
113         case FD_READ:
114         {
115             char szText[1024] = { 0 };
116             if (::recv(s, szText, 1024, 0) == -1)
117                 ::closesocket(s);
118             else
119             {
120                 printf("接收到消息: %s", szText);
121 
122                 /*补充:向客户端发送数据*/
123                 int start, end, add_sum;
124                 char response[256];
125                 string calString;
126                 if (sscanf(szText, "请输出%d到%d内的所有质数!", &start, &end) == 2) {//请输出从1到1000内所有的质数
127                     string calString = calculatePrimeNumbers(start, end);
128                     ::send(wParam, calString.c_str(), calString.length(), 0);
129                 }
130                 else {
131                     strcpy(response, "无效的指令,请重新发送指令");
132                     ::send(wParam, response, strlen(response), 0);
133                 }
134             }
135         }
136         break;
137         case FD_CLOSE:
138         {
139             ::closesocket(s);
140         }
141         break;
142         }
143     }
144     return 0;
145     case WM_DESTROY:
146         ::PostQuitMessage(0);
147         return 0;
148     }
149     //我们不需要的消息,就交给系统默认处理
150     return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
151 }
152 
153 boolean PNumJudgment(int num)
154 {
155     boolean flag = true;
156     for (int i = 2; i < (num / 2); i++) {
157         if (num % i == 0)
158             flag == false;
159     }
160     return flag;
161 }
162 
163 char* AllThePNum(int start, int end)
164 {
165     char PNumArr[4096];
166     for (int i = start; i <= end; i++)
167     {
168         if (PNumJudgment(i))
169         {
170             PNumArr[i] = i;
171 
172         }
173     }
174     return PNumArr;
175 }
176 
177 string calculatePrimeNumbers(int start, int end)
178 {
179     stringstream calResult;
180     for (int i = start; i <= end; i++)
181     {
182         if (i == 1)
183             continue; //1既不是质数也不是合数
184         bool isPrime = true;
185         for (int j = 2; j < i; j++)
186         {
187             if (i % j == 0)
188             {
189                 isPrime = false;
190                 break;
191             }
192         }
193         if (isPrime)
194         {
195             calResult << i << " ";
196         }
197     }
198     return calResult.str();
199 }

 

 

③客户端代码:

 

 1 #include "CInitSock.h"
 2 #include<stdio.h>
 3 CInitSock initSock;
 4 int main()
 5 {
 6     //创建套接字
 7     SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 8     if (s == INVALID_SOCKET)
 9     {
10         printf("Failed socket()\n");
11         return 0;
12     }
13     //填充sockaddr_in结构
14     sockaddr_in servAddr;
15     servAddr.sin_family = AF_INET;
16     servAddr.sin_port = htons(4567);
17 
18     //
19     servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
20     //绑定这个套接字到一个本地地址
21     if (::connect(s, (LPSOCKADDR)&servAddr, sizeof(servAddr)) == -1)
22     {
23         printf("Falied connect()\n");
24         return 0;
25     }
26     //给服务器端发信息
27     char szText1[] = "请输出1到1000内的所有质数!";
28     ::send(s, szText1, strlen(szText1), 0);
29 
30     //接收数据
31     char buff[4096];
32     int nRecv = ::recv(s, buff, 4096, 0);
33     if (nRecv > 0)
34     {
35         buff[nRecv] = '\0';
36         printf("接收到数据:%s", buff);
37     }
38 
39     //关闭监听套接字
40     ::closesocket(s);
41     return 0;
42 }

 

 

三、运行结果

①服务器端接收到请求消息:

 

②客户端收到服务器端的回复:

 

四、关于质数求解的不同方法

①基本的嵌套循环判断每个数是否为质数

这是最常使用的方法,也是上述服务器中使用到的方法:

 1 bool isPrime(int num) {
 2     if (num < 2) {
 3         return false;
 4     }
 5     for (int i = 2; i * i <= num; i++) {
 6         if (num % i == 0) {
 7             return false;
 8         }
 9     }
10     return true;
11 }
12 
13 void findPrimes(int start, int end) {
14     printf("质数列表: ");
15     for (int num = start; num <= end; num++) {
16         if (isPrime(num)) {
17             printf("%d ", num);
18         }
19     }
20     printf("\n");
21 }

 

服务器上判断质数的代码:(和上述代码的思路相同)

 1 string calculatePrimeNumbers(int start, int end)
 2 {
 3     stringstream calResult;
 4     for (int i = start; i <= end; i++)
 5     {
 6         if (i == 1)
 7             continue; //1既不是质数也不是合数
 8         bool isPrime = true;
 9         for (int j = 2; j < i; j++)
10         {
11             if (i % j == 0)
12             {
13                 isPrime = false;
14                 break;
15             }
16         }
17         if (isPrime)
18         {
19             calResult << i << " ";
20         }
21     }
22     return calResult.str();
23 }

 

②埃及筛法判断质数

思路:

一个素数的整数倍必定为合数

算法思想:

1.创建一个长度为n+1的数组sieve;

(长度为n+1是因为数组的下标是从0开始的,若n=10,你创建一个长度为n的数组,数组是从0--9的,并不包含10。)

2.初始化sieve数组的所有元素都为true,即默认所有的数都是质数;

3.进入for循环,进行判断;此时范围为for(int i=2;i<=n;i++);

(i从2开始,是因为数组中下标为0的数用不上,下标为1的数是用来表示数字1的,而1不是质数,所以要从数字2进行判断。)

4.在for循环中,如果prim[i]==1(i为质数),则将i的所有整数倍置为合数。

对i进行整数倍置为合数操作的时候,本应该 2i 3i 4i......都要置为合数的,但是有些数在之前的i-1已经操作过,所以此时不必在进行操作,我们只需要从i*i开始即可。

举个例子:

i=2,prim[i]==1---->if(i*i<=n)---->4 6 8 10 12 14 16 18 20 22......都置为合数

i=3,prim[i]==1---->if(i*i<=n)----->6 9 12 15 18 21 24 ......置为合数,此时6已经操作过了

i=5,prim[i]==1---->if(i*i<=n)----->10 15 20 25 30 35 ......置为合数,此时10、15、20已经置为合数过了,所以我们应从i*i开始操作

 1 #include <stdio.h>
 2 #include <stdbool.h>
 3 #include <math.h>
 4 
 5 void findPrimes(int start, int end) {
 6     bool sieve[1024];
 7     for (int i = 0; i <= end; i++) {
 8         sieve[i] = true;
 9     }
10     for (int p = 2; p <= sqrt(end); p++) {
11         if (sieve[p]) {
12             for (int i = p * p; i <= end; i += p) {
13                 sieve[i] = false;
14             }
15         }
16     }
17     printf("使用优化的Sieve of Eratosthenes求出的质数列表: ");
18     for (int num = start; num <= end; num++) {
19         if (num >= 2 && sieve[num] == true) {
20             printf("%d ", num);
21         }
22     }
23     printf("\n");
24 }
25 
26 int main() {
27     int start = 1;
28     int end = 1000;
29     findPrimes(start, end);
30     return 0;
31 }

运行结果:

 

五、总结

①实验中遇到的问题及其解决办法:

错误①:

问题描述:

Unicode字符集下,不可将char*类型的值分配到“LPCWSTR”类型的实体。

 

原因:

“从Visual C2005开始,编译器不再进行从char到LPCWSTR的隐式转换了”

 

解决方法:

修改工程属性,项目属性->高级->字符集->使用Unicode字符集改为未设置。

修改完之后便可正常赋值:

 

错误②:

问题描述:

Visual Studio中找不到WM_SOCKET这个消息常量

 

解决办法:

在 Visual Studio 中,WM_SOCKET 不是一个内置的消息常量,这是因为 WM_SOCKET 是一个自定义的消息,它通常与网络编程和异步套接字(Socket)相关联。

需要补充自定义网络通知消息代码:

#define WM_SOCKET (WM_USER + 1)

 

理解:

在 Windows 系统中,当使用异步套接字编程模型时,可以通过 WSAAsyncSelect 函数为套接字关联一个窗口,并指定一个自定义的消息号,以便在套接字事件发生时通过消息机制通知该窗口。具体来说,WSAAsyncSelect 函数将 FD_SOCKET 事件与指定的套接字关联,并在事件发生时向关联的窗口发送一个消息。

②对于WSAAsyncSelect模型的了解:

1.WSAAsyncSelect模型的工作过程分析:

  ①创建套接字:

  首先,需要使用socket函数创建一个套接字。套接字是网络通信的端点,可以在应用程序中发送和接收数据。

  ②注册事件通知:

  使用WSAAsyncSelect函数将套接字与特定的窗口关联起来,并为套接字注册感兴趣的网络事件,例如连接请求、数据到达等。该函数还指定了在事件发生时应该通知的窗口消息。

  ③创建消息循环:

  在接收事件通知之前,需要创建一个消息循环来处理系统发送的消息。消息循环可以使用GetMessage函数来获取消息,并将其分派给相应的窗口过程进行处理。

  ④接收事件通知:

  当套接字接收到感兴趣的网络事件时,系统将发送一个消息给消息循环。应用程序可以使用TranslateMessage和DispatchMessage函数将该消息传递给关联的窗口过程进行处理。

  ⑤处理事件:

  在窗口过程中,应用程序可以通过解析消息的参数来确定发生的事件类型。例如,如果收到FD_READ事件,表示有数据可以从套接字中读取。

  ⑥处理数据:

  根据具体的应用需求,可以使用相应的函数(如recv函数或send函数)来接收或发送数据。数据的读取或发送过程可以在事件处理函数中完成。

  ⑦继续循环:

  处理完事件后,窗口过程返回,并再次进入消息循环等待下一个事件的到来。循环将一直进行,直至应用程序退出或调用WSACleanup函数释放资源。

标签:SOCKET,int,模型,WSAAsyncSelect,wndclass,程序设计,接字,NULL,质数
From: https://www.cnblogs.com/refrain-yu/p/17861269.html

相关文章

  • 金蝶与千帆大模型开发平台共创智能企业管理
    随着科技的飞速发展和信息化时代的到来,企业管理面临越来越多的挑战。如何将先进的技术与企业管理相结合,提高企业的运营效率和管理水平,成为当前企业发展的重要课题。金蝶作为国内知名的企业管理软件提供商,基于百度智能云千帆大模型平台,打造了新一代企业管理产品,为企业提供了更加智能......
  • 倾斜摄影三维模型的根节点合并的轻量化技术方法分析
    倾斜摄影三维模型的根节点合并的轻量化技术方法分析 倾斜摄影三维模型的根节点合并是一种轻量化技术,旨在减小模型数据的大小,提高渲染效率和加载速度。在本文中,我们将探讨关于倾斜摄影三维模型根节点合并的轻量化技术方法。1、LOD(层次细节)技术:LOD是一种常用的轻量化技术,通过......
  • R语言和Python对copula模型Gaussian、t、Clayton 和 Gumbel 族可视化理论概念和文献计
    原文链接:http://tecdat.cn/?p=27240 原文出处:拓端数据部落公众号最近我们被客户要求撰写关于copula的研究报告,包括一些图形和统计输出。本文包含一些直观的示例来说明copula理论的核心概念。以下是脚本及其各自用途的简短列表:首先演示如何使用高斯copula来模拟具有任意......
  • 调和级数枚举倍数模型
    调和级数枚举倍数模型参考博客:算法学习笔记27:素数筛法【埃氏筛法、线性筛法】OI&ACM]调和级数枚举倍数模型板子(时间复杂度\(O(nlogn)\)):for(inti=1;i<=n;i++){for(intj=i;j<=n;j+=i){??? }}应用:目前较常见的用处:\(f[i]:最大公因数为i的倍......
  • 关于Java内存模型
    (面试被问到,想到之前有个笔记,整理一下发出来。)内存模型可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。不同架构的物理机器可以拥有不一样的内存模型,而Java虚拟机也有自己的内存模型。Java内存模型(JavaMemoryModel,JMM)是来屏蔽各种硬件和操作系统......
  • 生产效率的革新:腾讯混元大模型实测!
    自从年初以ChatGPT为代表的人工智能大模型爆火之后,市场上随之出现了很多大模型相关的工具。作为一个IT行业的技术人员,肯定不会放过这种对新技术尝鲜的机会。最近腾讯推出了自己的大模型:混元。目前刚开始内测,为此我特意找认识的腾讯小伙伴帮我开通了内测资格,提前体验了一下腾讯的......
  • 如何在langchain中对大模型的输出进行格式化
    简介我们知道在大语言模型中,不管模型的能力有多强大,他的输入和输出基本上都是文本格式的,文本格式的输入输出虽然对人来说非常的友好,但是如果我们想要进行一些结构化处理的话还是会有一点点的不方便。不用担心,langchain已经为我们想到了这个问题,并且提出了完满的解决方案。lan......
  • PubMedBERT:生物医学自然语言处理领域的特定预训练模型
    今年大语言模型的快速发展导致像BERT这样的模型都可以称作“小”模型了。KaggleLLM比赛LLMScienceExam的第四名就只用了deberta,这可以说是一个非常好的成绩了。所以说在特定的领域或者需求中,大语言模型并不一定就是最优的解决方案,“小”模型也有一定的用武之地,所以今天我们来......
  • Walrus 0.4发布:单一配置、多态运行,体验下一代应用交付模型
    今天,我们高兴地宣布云原生统一应用平台Walrus0.4正式发布,这是一个里程碑式的版本更新。新版本采用了全新的应用模型——仅需进行单一配置,即可在多种模态的基础设施及环境中运行包括应用服务及周边依赖资源在内的完整应用系统。“Youbuildit,yourunit”在这一版本中成为现......
  • 2023-2024-1 20231309 《计算机基础与程序设计》第九周学习总结
    2023-2024-120231309《计算机基础与程序设计》第八周学习总结作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第九周作业这个作业的目标作业正文2023-2024-120231309《计算机基础与程......