一、Winsock2 概述
- Winsock2(Windows Sockets 2)是微軟提供的 Windows 平臺網絡編程庫
二、初始連接案例
1、Server
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")using namespace std;const int listen_port = 12345;int main() {WSADATA wsaData;int WSAStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (WSAStartupResult != 0) {cerr << "WSAStartup() failed: " << WSAStartupResult << endl;return 1;}SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listenSocket == INVALID_SOCKET) {cerr << "socket() failed: " << WSAGetLastError() << endl;WSACleanup();return 1;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(listen_port);serverAddr.sin_addr.s_addr = INADDR_ANY;int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));if (bindResult == SOCKET_ERROR) {cerr << "bind() failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;}if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {cerr << "listen() failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;}cout << "Server is listening on port " << listen_port << endl;sockaddr_in clientAddr;int clientAddrSize = sizeof(clientAddr);SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrSize);if (clientSocket == INVALID_SOCKET) {cerr << "accept() failed: " << WSAGetLastError() << endl;}char clientIP[INET_ADDRSTRLEN];inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);cout << "Client connected from " << clientIP << ":" << ntohs(clientAddr.sin_port) << endl;return 0;
}
2、Client
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")using namespace std;int main() {WSADATA wsaData;int WSAStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (WSAStartupResult != 0) {cerr << "WSAStartup() failed: " << WSAStartupResult << endl;return 1;}SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock == INVALID_SOCKET) {cerr << "socket() failed: " << WSAGetLastError() << endl;WSACleanup();return 1;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(12345);inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);int ConnectionResult = connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr));if (ConnectionResult == SOCKET_ERROR) {cerr << "connect() failed: " << WSAGetLastError() << endl;closesocket(sock);WSACleanup();return 1;}cout << "Successfully connected to server!" << endl;closesocket(sock);WSACleanup();return 0;
}
3、Test
- 啟動 Server,輸出結果
# ServerServer is listening on port 12345
- 啟動 Client,輸出結果
# ClientSuccessfully connected to server!
# ServerClient connected from 127.0.0.1:63154
三、初始連接案例解讀
1、Server
(1)頭文件與庫引入
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")using namespace std;
-
winsock2.h
是 Winsock 編程的核心頭文件,包含了大部分套接字函數和數據結構的定義 -
ws2tcpip.h
提供 IP 地址轉換等功能 -
iostream
標準輸入輸出 -
#pragma comment
指令用于鏈接ws2_32.lib
庫 -
using namespace
啟用命名空間std
(2)初始化 Winsock 庫
WSADATA wsaData;
int WSAStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (WSAStartupResult != 0) {cerr << "WSAStartup() failed: " << WSAStartupResult << endl;return 1;
}
-
WSADATA
結構用于接收庫的詳細信息 -
WSAStartup()
是 Winsock 程序的第一個調用,用于初始化庫,MAKEWORD(2, 2)
表示初始化 Winsock 2.2 版本 -
必須檢查返回值,失敗時應立即退出
(3)創建監聽套接字
SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket == INVALID_SOCKET) {cerr << "socket() failed: " << WSAGetLastError() << endl;WSACleanup();return 1;
}
-
socket()
用于創建套接字,失敗時返回INVALID_SOCKET
-
AF_INET
表示使用 IPv4 地址族,SOCK_STREAM
表示面向連接的 TCP 套接字,IPPROTO_TCP
表示指定 TCP 協議 -
失敗時調用
WSAGetLastError()
獲取錯誤代碼,然后調用WSACleanup()
釋放資源
(4)設置服務端地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(listen_port);
serverAddr.sin_addr.s_addr = INADDR_ANY;
-
sockaddr_in
結構用于指定服務器地址信息 -
sin_family
表示地址族(IPv4),sin_port
表示端口號(使用htons
將主機字節序轉換為網絡字節序),sin_addr.s_addr
使用的INADDR_ANY
表示綁定到所有可用接口
(5)綁定套接字
int bindResult = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (bindResult == SOCKET_ERROR) {cerr << "bind() failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;
}
-
bind()
將套接字與特定 IP 和端口關聯,如果未發生錯誤,綁定返回 0,否則,返回SOCKET_ERROR
-
失敗時調用
WSAGetLastError()
獲取錯誤代碼,然后調用closesocket()
關閉現有套接字,然后調用WSACleanup()
釋放資源
(6)開始監聽
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {cerr << "listen() failed: " << WSAGetLastError() << endl;closesocket(listenSocket);WSACleanup();return 1;
}cout << "Server is listening on port " << listen_port << endl;
-
listen()
使套接字進入監聽狀態,如果未發生錯誤,返回 0,否則,返回SOCKET_ERROR
-
SOMAXCONN
是系統允許的最大掛起連接數 -
失敗時調用
WSAGetLastError()
獲取錯誤代碼,然后調用closesocket()
關閉現有套接字,然后調用WSACleanup()
釋放資源
(7)接受客戶端連接
sockaddr_in clientAddr;
int clientAddrSize = sizeof(clientAddr);
SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrSize);
if (clientSocket == INVALID_SOCKET) {cerr << "accept() failed: " << WSAGetLastError() << endl;
}
-
clientAddr
結構用于獲取客戶端地址信息 -
accept()
接受傳入的連接請求,阻塞直到有客戶端連接,返回一個新的套接字用于與客戶端通信
(8)顯示客戶端信息
char clientIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);
cout << "Client connected from " << clientIP << ":" << ntohs(clientAddr.sin_port) << endl;
-
inet_ntop()
將二進制 IP 地址轉換為可讀字符串 -
ntohs()
將網絡字節序的端口轉換為主機字節序
2、Client
(1)頭文件與庫引入
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>#pragma comment(lib, "ws2_32.lib")using namespace std;
-
winsock2.h
是 Winsock 編程的核心頭文件,包含了大部分套接字函數和數據結構的定義 -
ws2tcpip.h
提供 IP 地址轉換等功能 -
iostream
標準輸入輸出 -
#pragma comment
指令用于鏈接ws2_32.lib
庫 -
using namespace
啟用命名空間std
(2)初始化 Winsock 庫
WSADATA wsaData;
int WSAStartupResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (WSAStartupResult != 0) {cerr << "WSAStartup() failed: " << WSAStartupResult << endl;return 1;
}
(3)創建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {cerr << "socket() failed: " << WSAGetLastError() << endl;WSACleanup();return 1;
}
-
socket()
用于創建套接字,失敗時返回INVALID_SOCKET
-
AF_INET
表示使用 IPv4 地址族,SOCK_STREAM
表示面向連接的 TCP 套接字,IPPROTO_TCP
表示指定 TCP 協議 -
失敗時調用
WSAGetLastError()
獲取錯誤代碼,然后調用WSACleanup()
釋放資源
(4)設置服務端地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(12345);
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
-
sockaddr_in
結構用于指定服務器地址信息 -
sin_family
表示地址族(IPv4),sin_port
表示端口號(使用htons
將主機字節序轉換為網絡字節序),sin_addr
使用inet_pton
將字符串 IP 轉換為二進制格式
(5)連接服務端
int ConnectionResult = connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (ConnectionResult == SOCKET_ERROR) {cerr << "connect() failed: " << WSAGetLastError() << endl;closesocket(sock);WSACleanup();return 1;
}
-
connect()
發起連接,如果成功,返回 0,否則,返回SOCKET_ERROR
-
失敗時調用
WSAGetLastError()
獲取錯誤代碼,然后調用closesocket()
關閉現有套接字,然后調用WSACleanup()
釋放資源