【基于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.pswp.cn/diannao/79909.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79909.shtml
英文地址,請注明出處:http://en.pswp.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】 開始進入正題…