套接字有兩種概念:服務(wù)器和客戶(hù)端
下面是用于創(chuàng)建流式 (steam)TCP/IP 服務(wù)器和客戶(hù)端的常規(guī)模型。
服務(wù)器
- 初始化 Winsock。
- 創(chuàng)建套接字疯坤。
- 綁定套接字。
- 在套接字上監(jiān)聽(tīng)客戶(hù)端拥峦。
- 接受來(lái)自客戶(hù)端的連接贴膘。
- 接收和發(fā)送數(shù)據(jù)。
- 斷開(kāi)連接
客戶(hù)端
- 初始化 Winsock略号。
- 創(chuàng)建套接字刑峡。
- 連接到該服務(wù)器。
- 發(fā)送和接收數(shù)據(jù)玄柠。
- 斷開(kāi)連接
很明顯, 1, 2, 還有 斷開(kāi)連接 步驟完全相同
0.檢測(cè)環(huán)境
用vs, 調(diào)試下面代碼, 判斷環(huán)境是否準(zhǔn)備充分
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
printf("Hello World");
return 0;
}
1. 初始化
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
printf("Hello World");
return 0;
}
2. 服務(wù)器端創(chuàng)建套接字
首先為服務(wù)器創(chuàng)建套接字, 這樣接下來(lái)的客戶(hù)端就可以連接調(diào)試
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
return 0;
}
每個(gè)步驟盡量都要調(diào)試一番, 已檢查是否錯(cuò)誤
3.綁定套接字
若要使服務(wù)器接受客戶(hù)端連接突梦,它必須綁定到服務(wù)器的網(wǎng)絡(luò)地址。
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 綁定套接字 start
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);// 調(diào)用 bind 函數(shù)后羽利,不再需要地址信息 釋放
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 3. 綁定套接字 end
return 0;
}
4.在套接字上監(jiān)聽(tīng)客戶(hù)端
將套接字綁定到系統(tǒng)上的 IP 地址和端口之后宫患,服務(wù)器必須在該 IP 地址和端口上偵聽(tīng)傳入的連接請(qǐng)求
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 綁定套接字 start
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);// 調(diào)用 bind 函數(shù)后,不再需要地址信息 釋放
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 3. 綁定套接字 end
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 start
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
// SOMAXCONN定義了此套接字允許最大連接
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 end
return 0;
}
備注: window10第一次調(diào)試這一步驟會(huì)讓用戶(hù)給予防火墻權(quán)限
5. 接受來(lái)自客戶(hù)端的連接这弧。
當(dāng)套接字偵聽(tīng)連接后娃闲,程序必須處理該套接字上的連接請(qǐng)求
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 綁定套接字 start
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);// 調(diào)用 bind 函數(shù)后,不再需要地址信息 釋放
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 3. 綁定套接字 end
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 start
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
// SOMAXCONN定義了此套接字允許最大連接
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 end
// 5.接受來(lái)自客戶(hù)端的連接 start
SOCKET ClientSocket; //臨時(shí) 套接字 對(duì)象匾浪,以接受來(lái)自客戶(hù)端的連接
// 這里沒(méi)有考慮多線程并發(fā)接受多個(gè)客戶(hù)端 只接受一個(gè)
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 5.接受來(lái)自客戶(hù)端的連接 end
return 0;
}
備注:運(yùn)行這一步時(shí), 控制臺(tái)似乎沒(méi)有顯示任何東西, 其實(shí) 是accept 將邏輯流程卡住 等待 客戶(hù)端連接, 如下圖所示
6.在服務(wù)器上接收和發(fā)送數(shù)據(jù)
服務(wù)器接收的數(shù)據(jù)來(lái)自客戶(hù)端, 發(fā)送也是向客戶(hù)端發(fā)送數(shù)據(jù), 故而需要等下面的客戶(hù)端socket編寫(xiě)完畢才能進(jìn)行最終的功能測(cè)試.
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 綁定套接字 start
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);// 調(diào)用 bind 函數(shù)后皇帮,不再需要地址信息 釋放
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 3. 綁定套接字 end
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 start
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
// SOMAXCONN定義了此套接字允許最大連接
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 end
// 5.接受來(lái)自客戶(hù)端的連接 start
SOCKET ClientSocket; //臨時(shí) 套接字 對(duì)象,以接受來(lái)自客戶(hù)端的連接
// 這里沒(méi)有考慮多線程并發(fā)接受多個(gè)客戶(hù)端 只接受一個(gè)
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 5.接受來(lái)自客戶(hù)端的連接 end
// 6.在服務(wù)器上接收和發(fā)送數(shù)據(jù) start
#define DEFAULT_BUFLEN 512 // 字符緩沖區(qū)長(zhǎng)度
char recvbuf[DEFAULT_BUFLEN]; // 字符緩沖區(qū)數(shù)組
int iSendResult;
int recvbuflen = DEFAULT_BUFLEN;
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// recvbuf參數(shù)表示: 這里為了簡(jiǎn)單將客戶(hù)端發(fā)送過(guò)來(lái)的消息再發(fā)送給客戶(hù)端
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
// 6.在服務(wù)器上接收和發(fā)送數(shù)據(jù) end
return 0;
}
備注: 這一步相當(dāng)于完成了服務(wù)器的書(shū)寫(xiě),但為了保險(xiǎn), 還是要關(guān)閉連接
7.斷開(kāi)連接
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// 創(chuàng)建socket對(duì)象
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 綁定套接字 start
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);// 調(diào)用 bind 函數(shù)后蛋辈,不再需要地址信息 釋放
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 3. 綁定套接字 end
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 start
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
// SOMAXCONN定義了此套接字允許最大連接
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 4.在套接字上監(jiān)聽(tīng)客戶(hù)端 end
// 5.接受來(lái)自客戶(hù)端的連接 start
SOCKET ClientSocket; //臨時(shí) 套接字 對(duì)象属拾,以接受來(lái)自客戶(hù)端的連接
// 這里沒(méi)有考慮多線程并發(fā)接受多個(gè)客戶(hù)端 只接受一個(gè)
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// 5.接受來(lái)自客戶(hù)端的連接 end
// 6.在服務(wù)器上接收和發(fā)送數(shù)據(jù) start
#define DEFAULT_BUFLEN 512 // 字符緩沖區(qū)長(zhǎng)度
char recvbuf[DEFAULT_BUFLEN]; // 字符緩沖區(qū)數(shù)組
int iSendResult;
int recvbuflen = DEFAULT_BUFLEN;
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// recvbuf參數(shù)表示: 這里為了簡(jiǎn)單將客戶(hù)端發(fā)送過(guò)來(lái)的消息再發(fā)送給客戶(hù)端
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
// 6.在服務(wù)器上接收和發(fā)送數(shù)據(jù) end
// 7.斷開(kāi)連接 start
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// 7.斷開(kāi)連接 end
return 0;
}
備注: 這里沒(méi)有寫(xiě)控制臺(tái)輸入判斷 進(jìn)行關(guān)閉服務(wù)器 而是等客戶(hù)端傳輸完數(shù)據(jù)后自動(dòng)執(zhí)行關(guān)閉邏輯
1.初始化
這一步和服務(wù)端一樣, 見(jiàn)最上面
2.客戶(hù)端創(chuàng)建套接字
需要重新用vs創(chuàng)建一個(gè)新項(xiàng)目當(dāng)做客戶(hù)端
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
printf("啟動(dòng)客戶(hù)端");
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_IP "127.0.0.1"http:// 服務(wù)器為本機(jī)
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
return 0;
}
3.客戶(hù)端連接到該服務(wù)器
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
printf("啟動(dòng)客戶(hù)端");
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_IP "127.0.0.1"http:// 服務(wù)器為本機(jī)
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 連接到套接字 start
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
// 3. 連接到套接字 end
freeaddrinfo(result);
return 0;
}
4.客戶(hù)端發(fā)送和接收數(shù)據(jù)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
printf("啟動(dòng)客戶(hù)端");
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_IP "127.0.0.1"http:// 服務(wù)器為本機(jī)
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 連接到套接字 start
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
// 3. 連接到套接字 end
freeaddrinfo(result);
// 4.在客戶(hù)端上發(fā)送和接收數(shù)據(jù) start
#define DEFAULT_BUFLEN 512
int recvbuflen = DEFAULT_BUFLEN;
const char* sendbuf = "Hello World";
char recvbuf[DEFAULT_BUFLEN];
// 發(fā)送
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// 關(guān)閉
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// 接收
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);
// 4.在客戶(hù)端上發(fā)送和接收數(shù)據(jù) end
return 0;
}
5.客戶(hù)端斷開(kāi)連接
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
printf("啟動(dòng)客戶(hù)端");
// 1. 初始化 start
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// 1. 初始化 end
// 2. 創(chuàng)建套接字 start
#define DEFAULT_IP "127.0.0.1"http:// 服務(wù)器為本機(jī)
#define DEFAULT_PORT "9501" // 服務(wù)器監(jiān)聽(tīng)的端口
struct addrinfo*
result = NULL,
* ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; //AF _INET 用于指定 IPv4 地址族
hints.ai_socktype = SOCK_STREAM;// SOCK _STREAM 用于指定流套接字
hints.ai_protocol = IPPROTO_TCP;// IPPROTO _TCP 用于指定 tcp 協(xié)議
hints.ai_flags = AI_PASSIVE;
// 從本機(jī)中獲取ip地址等信息為了sockcet 使用
iResult = getaddrinfo(DEFAULT_IP, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 2. 創(chuàng)建套接字 end
// 3. 連接到套接字 start
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
// 3. 連接到套接字 end
freeaddrinfo(result);
// 4.在客戶(hù)端上發(fā)送和接收數(shù)據(jù) start
#define DEFAULT_BUFLEN 512
int recvbuflen = DEFAULT_BUFLEN;
const char* sendbuf = "Hello World";
char recvbuf[DEFAULT_BUFLEN];
// 發(fā)送
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// 關(guān)閉
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// 接收
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);
// 4.在客戶(hù)端上發(fā)送和接收數(shù)據(jù) end
// 5.斷開(kāi)連接 start
// 這里和服務(wù)器斷開(kāi)連接寫(xiě)在最后不同, 客戶(hù)端斷開(kāi)連接寫(xiě)在 發(fā)送后 和 接收前
// shutdown(ConnectSocket, SD_SEND) SD_SEND表示socket的發(fā)送數(shù)據(jù)端雖然關(guān)閉(為了服務(wù)器釋放客戶(hù)端連接資源), 但是仍然能接收服務(wù)端的數(shù)據(jù)
//iResult = shutdown(ClientSocket, SD_SEND);
//if (iResult == SOCKET_ERROR) {
// printf("shutdown failed: %d\n", WSAGetLastError());
// closesocket(ClientSocket);
// WSACleanup();
// return 1;
//}
closesocket(ConnectSocket);
WSACleanup();
// 5.斷開(kāi)連接 end
return 0;
}
結(jié)果
啟動(dòng)服務(wù)端 和客戶(hù)端 可以看到, 雙方都能接收到
備注: 本文內(nèi)容來(lái)源于 [微軟官方文檔] (https://docs.microsoft.com/zh-cn/windows/win32/winsock/using-winsock)
因?yàn)槲臋n 內(nèi)容繁多 ,特做筆記