日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

windows socket网络编程一:最简单的服务器和客户端搭建

發布時間:2024/1/18 windows 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 windows socket网络编程一:最简单的服务器和客户端搭建 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • 服務器
    • 網絡版本
    • 1、打開網絡庫
    • 2、校驗版本
    • 3、創建socket
    • 4、綁定地址和端口
    • 5、監聽
    • 6、接受鏈接
    • 7、與客戶端收發消息
  • 客戶端
    • 1、打開網絡庫
    • 2、校驗版本
    • 3、創建socket
    • 4、連接服務器
    • 5、與客戶端收發消息
    • 類比
  • 運行結果
  • 源碼鏈接
  • 遇到的問題
    • 頭文件沖突

簡介

socket又是什么?
將網絡底層復雜的協議體系,執行流程,進行了封裝后就是SOCKET了,也就是說,SOCKET是我們調用協議進行通信的操作接口,將復雜的協議過程與我們編程人員分開,我們直接操作一個簡單SOCKET就行了,對于底層的協議 過程細節,我們可以完全不用知道。
在編譯器轉定義后就是一個unsigned int,目測當作id使用。

最簡單的客戶端、服務器通信流程大致如下:

收發信息可以進行很多次。

服務器

網絡版本

我們使用新版本第二版

網絡版本一:

#include <winsock.h> #pragma comment(lib, "wsock32.lib")

網絡版本二:

#include <WinSock2.h> #pragma comment(lib, "ws2_32.lib")

注:不管是64編譯環境還是32編譯環境,都是用這個ws2_32.lib,并沒有ws2_64.lib。

1、打開網絡庫

int WSAAPI WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData );

功能:打開網絡庫/啟動網絡庫(函數名字分析W—windows、S—socket、A—Asynchronous 異步)
參數:

  • wVersionRequested — 調用者可以使用的Windows套接字規范的最高版本。高位字節指定次版本號;低位字節指定主版本號。(支持版本:1.0、1.1、2.0、2.1、2.2)
  • lpWSAData — 接收Windows套接字實現的詳細信息。struct WSAData {WORD wVersion; // 我們要使用的版本WORD wHighVersion; // 系統能提供給我們最高的版本unsigned short iMaxSockets; // 返回可用的socket的數量,二版本之后就沒用了unsigned short iMaxUdpDg; // UDP數據報信息的大小,2版本之后就沒用了char *lpVendorInfo; // 供應商特定的信息,2版本之后就沒用了char szDescription[WSADESCRIPTION_LEN + 1]; // 當前庫的描述信息char szSystemStatus[WSASYS_STATUS_LEN + 1]; } 程序調試截圖:

返回值:

  • 如果成功,返回零。
  • 如果失敗,返回錯誤碼。 錯誤碼宏展開直翻原因通俗解釋原因
    WSASYSNOTREADY10091底層網絡子系統尚未準備好進行網絡通信系統配置問題,重啟下電腦,檢查ws2_32庫是否存在,或者是否在環境配置目錄下
    WSAVERNOTSUPPORTED10092此特定Windows套接字實現不提供所請求的Windows套接字支持的版本。要使用的版本不支持
    WSAEPROCLIM10067已達到對Windows套接字實現支持的任務數量的限制Windows Sockets實現可能限制同時使用它的應用程序的數量
    WSAEINPROGRESS10036正在阻止Windows Sockets 1.1操作。當前函數運行期間,由于某些原因造成阻塞,會返回在這個錯誤碼,其他操作均禁止
    WSAEFAULT10014lpWSAData參數不是有效指針參數寫錯了

代碼(錯誤處理就簡單點了):

// 開啟網絡庫 WORD wVersionRequird = MAKEWORD(2, 2); // MAKEWORD(主版本,副版本) WSADATA wdScokMsg; switch (WSAStartup(wVersionRequird, &wdScokMsg)) { case WSASYSNOTREADY:printf("重啟電腦試試,或者檢查網絡庫\n");return -1;case WSAVERNOTSUPPORTED:printf("請更新網絡庫\n");return -1;case WSAEPROCLIM:printf("請嘗試關掉不必要的軟件,以為當前網絡運行提供充足的資源\n");return -1;case WSAEINPROGRESS:printf("請重新啟動\n");return -1; }

雖然我們有正確的代碼,但是我們也可以測試一下不正確的版本號會發生什么:

錯誤版本號問題
主版本號為0返回錯誤碼WSAVERNOTSUPPORTED,不支持該版本
有對應的主版本,沒有對應的副版本沒問題,得到該主版本的最大副版本 1.1 2.2并使用
超過最大主版本沒問題,使用系統能提供的最大的版本 2.2

2、校驗版本

這一步也許不是必須的,不過網絡版本還是比較重要的,需要確定。

// 校驗版本 if (2 != HIBYTE(wdScokMsg.wVersion) || 2 != LOBYTE(wdScokMsg.wVersion)) {printf("版本不存在\n");WSACleanup();return -1; }

3、創建socket

SOCKET WSAAPI socket(int af,int type,int protocol );

功能:創建一個SOCKET
參數:

  • af — 地址族 常用地址族宏展開含義
    AF_INET2IPv4
    AF_INET623IPv6
    AF_IRDA26紅外
    AF_BTH32藍牙
  • type — 套接字類型 套接字類型宏展開含義
    SOCK_STREAM1一種套接字類型,提供帶有OOB數據傳輸機制的順序,可靠,雙向,基于連接的字節流。 此套接字類型使用傳輸控制協議(TCP)作為Internet地址系列(AF_INET或AF_INET6)。
    SOCK_DGRAM2一種支持數據報的套接字類型,它是固定(通常很小)最大長度的無連接,不可靠的緩沖區。 此套接字類型使用用戶數據報協議(UDP)作為Internet地址系列(AF_INET或AF_INET6)。
    SOCK_RAW3一種套接字類型,提供允許應用程序操作下一個上層協議頭的原始套接字。 要操作IPv4標頭,必須在套接字上設置IP_HDRINCL套接字選項。 要操作IPv6標頭,必須在套接字上設置IPV6_HDRINCL套接字選項。
    SOCK_RDM4一種套接字類型,提供可靠的消息數據報。 這種類型的一個示例是Windows中的實用通用多播(PGM)多播協議實現,通常稱為可靠多播節目。僅在安裝了可靠多播協議時才支持此類型值。
    SOCK_SEQPACKET5一種套接字類型,提供基于數據報的偽流數據包。
  • protocol — 協議類型 協議類型含義
    IPPROTO_TCP傳輸控制協議(TCP)。 當af參數為AF_INET或AF_INET6且類型參數為SOCK_STREAM時,這是一個可能的值。
    IPPROTO_UDP用戶數據報協議(UDP)。 當af參數為AF_INET或AF_INET6且類型參數為SOCK_DGRAM時,這是一個可能的值。
    IPPROTO_ICMPInternet控制消息協議(ICMP)。 當af參數為AF_UNSPEC,AF_INET或AF_INET6且類型參數為SOCK_RAW或未指定時,這是一個可能的值。
    IPPROTO_IGMPInternet組管理協議(IGMP)。 當af參數為AF_UNSPEC,AF_INET或AF_INET6且類型參數為SOCK_RAW或未指定時,這是一個可能的值。
    IPPROTO_RM用于可靠多播的PGM協議。 當af參數為AF_INET且類型參數為SOCK_RDM時,這是一個可能的值。 在針對Windows Vista及更高版本發布的Windows SDK上,此協議也稱為IPPROTO_PGM。僅在安裝了可靠多播協議時才支持此協議值。
    0調用者不希望指定協議,服務提供商將選擇要使用的協議。

通過上面對參數的描述來看我們三個參數需要配合使用,比如說我們使用tcp通信需要基于字節流,而不是數據包,不能亂用。

返回值:

  • 如果成功,返回socket。
  • 如果失敗,返回INVALID_SOCKET。

代碼:

// 創建服務器socket(監聽套接字) SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == socketServer) {printf("創建socket失敗 error:%d\n", WSAGetLastError());WSACleanup();return -1; }

我們測試一下基于數據包的tcp(SOCKET socketServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_TCP);)會有什么問題:

4、綁定地址和端口

int WSAAPI bind(SOCKET s,const sockaddr *name,int namelen );

功能:給socket綁定具體地址(定位到電腦)與端口號(定位到具體應用)
參數:

  • s — socket
  • name — 地址和端口typedef struct sockaddr { #if (_WIN32_WINNT < 0x0600)u_short sa_family; #elseADDRESS_FAMILY sa_family; // Address family. #endif //(_WIN32_WINNT < 0x0600)CHAR sa_data[14]; // Up to 14 bytes of direct address. } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;// 和sockaddr相同 便于我們賦值 typedef struct sockaddr_in { #if(_WIN32_WINNT < 0x0600)short sin_family; #else //(_WIN32_WINNT < 0x0600)ADDRESS_FAMILY sin_family; // 地址族 #endif //(_WIN32_WINNT < 0x0600)USHORT sin_port; // 端口IN_ADDR sin_addr; // 地址CHAR sin_zero[8]; } SOCKADDR_IN, *PSOCKADDR_IN;
  • namelen — 參數二的大小

其中端口號理論上取值范圍0~65535。實際上介于0~1023,為系統保留占用端口號,(如:21端口分配給FTP(文件傳輸協議)服務、25端口分配給SMTP(簡單郵件傳輸協議)服務、80端口分配給HTTP服務)所以我們使用其他的端口號。
因為同一個端口號不能同時被兩個程序使用,萬一我們想要的端口號被占用了,想知道是什么程序占用的,可以使用cmd查看:

  • 打開運行cmd輸入netstat -ano,查看被使用的所有端口
  • netstat -aon | findstr “12345”,檢查我們要使用的端口號是否被使用了

返回值:

  • 如果成功,返回0。
  • 如果失敗,返回SOCKET_ERROR。

代碼:

// 綁定地址 SOCKADDR_IN sockAddress; sockAddress.sin_family = AF_INET; sockAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // INADDR_ANY --- 任何地址都可以 sockAddress.sin_port = htons(6666); if (SOCKET_ERROR == bind(socketServer, (struct sockaddr*)&sockAddress, sizeof(sockAddress))) {printf("bind 失敗 error:%d\n", WSAGetLastError());closesocket(socketServer);WSACleanup();return -1; }

我們測試一下已經被占用的端口:

5、監聽

int WSAAPI listen(SOCKET s,int backlog );

功能:將套接字置于正在偵聽傳入連接的狀態
參數:

  • s — socket
  • backlog — 掛起的連接隊列的最大長度。如果設置為SOMAXCONN,則負責套接字s的基礎服務提供商將積壓設置為最大合理值。

返回值:

  • 如果成功,返回0。
  • 如果失敗,返回SOCKET_ERROR。

代碼:

// 開始監聽 if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)) {printf("listen 失敗 error:%d\n", WSAGetLastError());closesocket(socketServer);WSACleanup();return -1; }

我們測試一下未bind的情況:

6、接受鏈接

SOCKET WSAAPI accept(SOCKET s,sockaddr *addr,int *addrlen );

作用:允許在套接字上進行傳入連接嘗試。listen監聽客戶端來的鏈接,accept將客戶端的信息綁定到一個socket上,也就是給客戶端創建一個socket,通過返回值返回給我們客戶端的socket。(一次只能創建一個,有幾個客戶端鏈接,就要調用幾次。)
參數:

  • s — socket
  • addr — 客戶端地址和端口
  • addrlen — 參數二的大小

注:如果參數二和參數三都填NULL,那就不是直接得不到客戶端信息。可以通過函數得到客戶端信息getpeername(newSocket, (struct sockaddr*)&sockClient, &nLen);。對應的得到本地服務器信息getsockname(sSocket, (sockaddr*)&addr, &nLen);。

返回值:

  • 如果成功,返回客戶端socket。
  • 如果失敗,返回INVALID_SOCKET。

代碼:

// 接受鏈接 SOCKADDR_IN sockClient; int nLen = sizeof(sockClient); SOCKET socketClient = accept(socketServer, (struct sockaddr*)&sockClient, &nLen); if (INVALID_SOCKET == socketClient) {printf("accept 失敗 error:%d\n", WSAGetLastError());closesocket(socketServer);WSACleanup();return -1; } printf("客戶端連接成功\n");

7、與客戶端收發消息

int WSAAPI recv(SOCKET s,char *buf,int len,int flags );

功能:得到指定目標(參數1)發來的消息
參數:

  • s — socket
  • buf — 消息數據的存儲空間(網絡傳輸得最大單元MTU為1500字節,也就是客戶端發過來得數據,一次最大就是1500字節,當然可以減去報文的40字節,這是協議規定,這個數值也是根據很多情況,總結出來得最優值。)
  • len — buf參數指向的緩沖區的長度(以字節為單位)
  • flags — 數據的讀取方式 值含義
    MSG_PEEK窺視傳入的數據。 數據將復制到緩沖區中,但不會從輸入隊列中刪除。
    MSG_OOB處理帶外(OOB)數據。就是傳輸一段數據,在外帶一個額外的特殊數據。不建議被使用,實在不行send兩次。
    MSG_WAITALL當滿足事件之一(調用方提供的緩沖區已滿、連接已關閉、該請求已被取消或發生錯誤)就讀取數據,并從輸入隊列中刪除。
    0有數據就讀取數據,并從輸入隊列中刪除。

返回值:

  • 如果成功,返回接收到的字節數。
  • 如果已正常關閉連接,返回值為零。
  • 如果失敗,返回SOCKET_ERROR。
int WSAAPI send(SOCKET s,const char *buf,int len,int flags );

功能:向目標發送數據
參數:

  • s — socket
  • buf — 要發送的數據
  • len — buf參數指向的緩沖區的長度(以字節為單位)
  • flags — 數據的發送方式 值含義
    MSG_OOB與上面對應處理帶外(OOB)數據。
    MSG_DONTROUTE指定數據不應受路由限制。 Windows套接字服務提供程序可以選擇忽略此標志。
    0正常發送

返回值:

  • 如果成功,返回已發送的字節數。
  • 如果失敗,返回SOCKET_ERROR。

代碼:

// 與客戶端收發消息 while (1) {// 緩沖區char szRecvBuffer[1500];char szSendBuffer[1500];int result = recv(socketClient, szRecvBuffer, sizeof(szRecvBuffer), 0);if (0 == result) // 客戶端正常關閉{printf("客戶端正常下線\n");break; // 因為這個例子只接受一次客戶端請求所以服務器關閉}else if (SOCKET_ERROR == result) // recv出錯{printf("recv 失敗 error:%d\n", WSAGetLastError());break;}else // 給客戶端發消息{// 接收到客戶端消息 printf("Client Data : %s \n", szRecvBuffer);// 給客戶回信scanf_s("%s", szSendBuffer, 1500);getchar();if (SOCKET_ERROR == send(socketClient, szSendBuffer, strlen(szSendBuffer) + 1, 0)){printf("send 失敗 error:%d\n", WSAGetLastError());break;}} }

客戶端

1、打開網絡庫

與服務器類似

2、校驗版本

與服務器類似

3、創建socket

與服務器類似

4、連接服務器

int WSAAPI connect(SOCKET s,const sockaddr *name,int namelen );

功能:連接服務器
參數:

  • s — socket
  • name — 服務器ip地址端口號結構體
  • namelen — 參數二的大小

返回值:

  • 如果成功,返回0。
  • 如果失敗,返回SOCKET_ERROR。

代碼:

// 連接服務器 SOCKADDR_IN sockAddress; sockAddress.sin_family = AF_INET; sockAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddress.sin_port = htons(6666); if (SOCKET_ERROR == connect(socketClient, (struct sockaddr*)&sockAddress, sizeof(sockAddress))) {printf("connect 失敗 error:%d\n", WSAGetLastError());closesocket(socketClient);WSACleanup();return -1; }

5、與客戶端收發消息

與服務器類似

類比

我們可以把操作socket類比于操作file:

socket文件
建立socket socket聲明File*
連接服務器 connect打開文件fopen
向服務器發送數據 send寫文件fwrite
接收服務器數據 recv讀文件fread
關閉socket closesocket關閉文件fclose

運行結果

源碼鏈接

百度云鏈接:https://pan.baidu.com/s/1xBOiSADlAG2gO1TC6BBO_A
提取碼:sxbd

遇到的問題

頭文件沖突

代碼:

#include <Windows.h> #include <WinSock2.h>int main() {return 0; }

報錯:

解決方法:

  • 交換#include <WinSock2.h>寫在#include <Windows.h>上方
  • 在頭文件上方添加宏#define WIN32_LEAN_AND_MEAN
  • 總結

    以上是生活随笔為你收集整理的windows socket网络编程一:最简单的服务器和客户端搭建的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。