C++ Socket實現TCP與UDP網絡編程

前言

socket編程分為TCP和UDP兩個模塊,其中TCP是可靠的、安全的,常用於發送文件等,而UDP是不可靠的、不安全的,常用作視頻通話等。

如下圖:

在這裡插入圖片描述

頭文件與庫:

#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

準備工作:

創建工程後,首先右鍵工程,選擇屬性

在這裡插入圖片描述

然後選擇 C/C++ – 預處理器 – 預處理器定義

在這裡插入圖片描述

將字符串 _WINSOCK_DEPRECATED_NO_WARNINGS 添加到裡面去,點擊應用即可!

TCP

連接過程圖:

在這裡插入圖片描述

創建tcp服務器和客戶端都是按照上圖的步驟來操作的!

1). 服務器

初始化套接字庫
對應圖中socket()

WORD wVersion;
WSADATA wsaData;
int err;

// 設置版本,可以理解為1.1
wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

// 啟動
err = WSAStartup(wVersion, &wsaData);

創建tcp套接字
對應圖中socket()

// AF_INET:ipv4   AF_INET6:ipv6
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

綁定到本機
對應圖中bind()

// 準備綁定信息
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 設置綁定網卡
addrSrv.sin_family = AF_INET;		// 設置綁定網絡模式
addrSrv.sin_port = htons(6000);		// 設置綁定端口
// hton: host to network  x86:小端    網絡傳輸:htons大端

// 綁定到本機
int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

監聽
對應圖中listen()

// 同時能接收10個鏈接,主要看參數二的設置個數
listen(sockSrv, 10);

接收連接請求,返回針對客戶端的套接字
對應圖中accept()

SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);

發送數據
對應圖中write()

sprintf_s(sendBuf, "hello client!\n");
int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);

接收數據
對應圖中read()

recv(sockConn, recvBuf, 100, 0);

關閉套接字
對應圖中close()

closesocket(sockConn);

清理套接字庫

WSACleanup();

具體實現代碼

#include <iostream>
#include <stdio.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")


int main(void) {

	// 1.初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	// 設置版本,可以理解為1.1
	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

	// 啟動
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}
	// 檢查:網絡低位不等於1 || 網絡高位不等於1
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		// 清理套接字庫
		WSACleanup();
		return -1;
	}

	// 2.創建tcp套接字		// AF_INET:ipv4   AF_INET6:ipv6
	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	// 準備綁定信息
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 設置綁定網卡
	addrSrv.sin_family = AF_INET;		// 設置綁定網絡模式
	addrSrv.sin_port = htons(6000);		// 設置綁定端口
	// hton: host to network  x86:小端    網絡傳輸:htons大端

	// 3.綁定到本機
	int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
	if (retVal == SOCKET_ERROR) {
		printf("Failed bind:%d\n", WSAGetLastError());
		return -1;
	}

	// 4.監聽,同時能接收10個鏈接
	if (listen(sockSrv, 10) == SOCKET_ERROR) {
		printf("Listen failed:%d", WSAGetLastError());
		return -1;
	}

	std::cout << "Server start at port: 6000" << std::endl;

	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR);

	char recvBuf[100];
	char sendBuf[100];
	while (1) {
		// 5.接收連接請求,返回針對客戶端的套接字
		SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
		if (sockConn == SOCKET_ERROR) {
			//printf("Accept failed:%d", WSAGetLastError());
			std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
			break;
		}

		//printf("Accept client IP:[%s]\n", inet_ntoa(addrCli.sin_addr));
		std::cout << "Accept client IP: " << inet_ntoa(addrCli.sin_addr) << std::endl;

		// 6.發送數據
		sprintf_s(sendBuf, "hello client!\n");
		int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
		if (iSend == SOCKET_ERROR) {
			std::cout << "send failed!\n";
			break;
		}

		// 7.接收數據
		recv(sockConn, recvBuf, 100, 0);
		std::cout << recvBuf << std::endl;

		// 關閉套接字
		closesocket(sockConn);
	}


	// 8.關閉套接字
	closesocket(sockSrv);

	// 9.清理套接字庫
	WSACleanup();

	return 0;
}

2). 客戶端

初始化套接字庫
對應圖中socket()

WORD wVersion;
WSADATA wsaData;
int err;

// 可以理解為1.1
wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

// 啟動
err = WSAStartup(wVersion, &wsaData);

// 創建TCP套接字
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);

連接服務器
對應圖中connect()

// 連接服務器
int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

發送數據到服務器
對應圖中write()

char sendBuf[] = "你好,服務器,我是客戶端!";
send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);

接收服務器的數據
對應圖中read()

char recvBuf[100];
recv(sockCli, recvBuf, sizeof(recvBuf), 0);

關閉套接字並清除套接字庫
對應圖中close()

closesocket(sockCli);
WSACleanup();

具體實現代碼

#include <iostream>
#include <WinSock2.h>


#pragma comment(lib, "ws2_32.lib")


int main(void) {

	// 1.初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	// 可以理解為1.1
	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

	// 啟動
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}
	// 檢查:網絡地位不等於1 || 網絡高位不等於1
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		// 清理套接字庫
		WSACleanup();
		return -1;
	}

	// 創建TCP套接字
	SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	// 服務器地址
	addrSrv.sin_port = htons(6000);		// 端口號
	addrSrv.sin_family = AF_INET;		// 地址類型(ipv4)

	// 2.連接服務器
	int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
	if (err_log == 0) {
		printf("連接服務器成功!\n");
	
	} else {
		printf("連接服務器失敗!\n");
		return -1;
	}


	char recvBuf[100];
	char sendBuf[] = "你好,服務器,我是客戶端!";
	// 3.發送數據到服務器
	send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);

	// 4.接收服務器的數據
	recv(sockCli, recvBuf, sizeof(recvBuf), 0);
	std::cout << recvBuf << std::endl;


	// 5.關閉套接字並清除套接字庫
	closesocket(sockCli);
	WSACleanup();

	system("pause");

	return 0;
}

運行效果:

在這裡插入圖片描述

3). TCP聊天小項目

下面是根據上面的代碼修改的一個聊天小項目(使用到瞭多線程)

隻有一個服務器,服務器一直開啟等待客戶端連接;
客戶都安可以開啟多個,且可以一直連續的與服務器進行發送接收消息;
服務器給客戶端發送數據,得通過1 – 9來區分到底給那個客戶端發送消息,例如給第二個客戶端發送消息:2你好,客戶端
客戶端那邊接收到的數據是:你好,客戶端

服務器代碼

#include <iostream>
#include <WinSock2.h>
#include <stdio.h>
#include <Windows.h>
#include <process.h>
#include <vector>
#include <conio.h>
#include <string.h>
#include <string>

#pragma comment(lib, "ws2_32.lib")


SOCKET sockSrv;
std::vector<SOCKET> vec_sockConn;
std::vector<SOCKADDR_IN> vec_sockaddr_in;
std::vector<int> vec_sockIndex;


// 這個結構體用作線程參數
typedef struct SERVER_CLIENT {
	SOCKET server;
	SOCKADDR_IN client;
	int clientIndex;
}SC;



// 判斷有沒有斷開連接
bool IsSocketClosed(SOCKET clientSocket) {
	bool ret = false;
	HANDLE closeEvent = WSACreateEvent();
	WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);

	DWORD dwRet = WaitForSingleObject(closeEvent, 0);

	if (dwRet == WSA_WAIT_EVENT_0)
		ret = true;
	else if (dwRet == WSA_WAIT_TIMEOUT)
		ret = false;

	WSACloseEvent(closeEvent);
	return ret;
}


// 接收請求
unsigned int WINAPI  ThreadAccept(LPVOID p) {
	static int i = 0;
	while (1) {
		SOCKADDR_IN addrCli;
		int len = sizeof(SOCKADDR);

		// 5.接收連接請求,返回針對客戶端的套接字
		SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
		if (sockConn == SOCKET_ERROR) {
			printf("Accept failed:%d", WSAGetLastError());
		}

		// 存儲當前服務器與客戶端 連接綁定的socket
		vec_sockIndex.emplace_back(i++);
		vec_sockaddr_in.emplace_back(addrCli);
		vec_sockConn.emplace_back(sockConn);

		printf("\033[0;%d;40m客戶端[%d]上線\033[0m\n", 31, i);
	}
	

	return 0;
}

unsigned int WINAPI  _ThreadRecv(LPVOID p) {
	char recvBuf[100];
	memset(recvBuf, 0, 100);

	SC _sc = *(SC *)p;
	
	while (1) {
		Sleep(20);

		if (IsSocketClosed(_sc.server) == true) {
			printf("客戶端 [%d] 斷開連接!\n", _sc.clientIndex + 1);
			break;
		}


		// 接收數據
		recv(_sc.server, recvBuf, 100, 0);
		if (strlen(recvBuf) == 0) {
			continue;
		}
	
		printf("接收到客戶端 [%d] 的消息:%s\n", _sc.clientIndex + 1, recvBuf);
		memset(recvBuf, 0, 100);
	}
	return 0;
}

unsigned int WINAPI  ThreadRecv(LPVOID p) {

	static int index = 0;

	while (1) {
		// 還沒有客戶端與服務器進行連接
		if (vec_sockConn.size() == 0) {
			continue;
		}

		// 接收線程已經開啟和客戶端個數相等
		if (vec_sockConn.size()  == index) {
			continue;
		}

		SC sc;
		sc.server = vec_sockConn.at(index);
		sc.client = vec_sockaddr_in.at(index);
		sc.clientIndex = vec_sockIndex.at(index);

		HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadRecv, (void *)&sc, 0, NULL);

		index++;
		Sleep(20);
	}

	return 0;
}



int main(void) {

	// 1.初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	// 設置版本,可以理解為1.1
	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

	// 啟動
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}
	// 檢查:網絡低位不等於1 || 網絡高位不等於1
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		// 清理套接字庫
		WSACleanup();
		return -1;
	}

	// 2.創建tcp套接字		// AF_INET:ipv4   AF_INET6:ipv6
	sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	// 準備綁定信息
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 設置綁定網卡
	addrSrv.sin_family = AF_INET;		// 設置綁定網絡模式
	addrSrv.sin_port = htons(6000);		// 設置綁定端口
	// hton: host to network  x86:小端    網絡傳輸:htons大端

	// 3.綁定到本機
	int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
	if (retVal == SOCKET_ERROR) {
		printf("Failed bind:%d\n", WSAGetLastError());
		return -1;
	}

	// 4.監聽,同時接收10個鏈接
	if (listen(sockSrv, 10) == SOCKET_ERROR) {
		printf("Listen failed:%d", WSAGetLastError());
		return -1;
	}

	std::cout << "Server start at port: 6000" << std::endl;


	// 線程句柄				// 創建線程
	HANDLE hThread_1 = (HANDLE)_beginthreadex(NULL, 0, ThreadAccept, NULL, 0, NULL);
	HANDLE hThread_2 = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);


	//uiInit();
	//editPrint(0, ">");


	char sendBuf[100];
	while (1) {

		//printf("請輸入發送內容:");

		char c = getchar();				// 輸入發送給誰
		scanf_s("%s", sendBuf, 100);	// 輸入發送的內容

		if (strlen(sendBuf) == 0) {
			printf("輸入內容為空或者超長!\n");
		}

		// 1 至 9
		if (c < '1' || c > '9' || vec_sockConn.size() == 0 || c - '0' >= vec_sockConn.size() + 1) {
			while ((c = getchar()) != '\n');	// 清空輸入緩沖區
			memset(sendBuf, 0, 100);
			printf("輸入內容不符合規則!\n");
			continue;
		}



		// 發送數據
		int index = --c - '0';	// 因為下標是從零開始的,所以c要先自減
		int iSend = send(vec_sockConn.at(index) , sendBuf, strlen(sendBuf) + 1, 0);
		if (iSend == SOCKET_ERROR) {
			std::cout << "send failed!\n";
			break;
		}


		memset(sendBuf, 0, 100);
		while ((c = getchar()) != '\n');	// 清空輸入緩沖區
	}

	// 關閉套接字
	std::vector<SOCKET>::iterator it = vec_sockConn.begin();
	for (; it != vec_sockConn.end(); it++) {
		closesocket((SOCKET)(*it));
	}


	WaitForSingleObject(hThread_1, INFINITE);
	WaitForSingleObject(hThread_2, INFINITE);

	CloseHandle(hThread_1);
	CloseHandle(hThread_2);

	// 7.關閉套接字
	closesocket(sockSrv);

	// 8.清理套接字庫
	WSACleanup();

	return 0;
}

客戶端

#include <iostream>
#include <WinSock2.h>
#include <process.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

SOCKET sockCli;


// 判斷有沒有斷開連接
bool IsSocketClosed(SOCKET clientSocket) {
	bool ret = false;
	HANDLE closeEvent = WSACreateEvent();
	WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);

	DWORD dwRet = WaitForSingleObject(closeEvent, 0);

	if (dwRet == WSA_WAIT_EVENT_0)
		ret = true;
	else if (dwRet == WSA_WAIT_TIMEOUT)
		ret = false;

	WSACloseEvent(closeEvent);
	return ret;
}


unsigned int WINAPI  ThreadRecv(LPVOID p) {
	char recvBuf[100];
	memset(recvBuf, 0, 100);

	while (1) {
		Sleep(20);

		if (IsSocketClosed(sockCli) == true) {
			printf("服務器 斷開連接!\n");
			break;
		}

		// 接收服務器的數據
		recv(sockCli, recvBuf, sizeof(recvBuf), 0);
		if (strlen(recvBuf) == 0) continue;

		std::cout << recvBuf << std::endl;
		memset(recvBuf, 0, 100);
	}


	return 0;
}

int main(void) {

	// 1.初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	// 可以理解為1.1
	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 將a左移8位變成高位與b合並起來

	// 啟動
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}
	// 檢查:網絡地位不等於1 || 網絡高位不等於1
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		// 清理套接字庫
		WSACleanup();
		return -1;
	}

	// 創建TCP套接字
	sockCli = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	// 服務器地址
	addrSrv.sin_port = htons(6000);		// 端口號
	addrSrv.sin_family = AF_INET;		// 地址類型(ipv4)

	// 連接服務器
	int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
	if (err_log == 0) {
		printf("連接服務器成功!\n");

	} else {
		printf("連接服務器失敗!\n");
		return -1;
	}

	// 線程句柄				// 創建線程
	HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);


	char sendBuf[100];
	while (1) {

		//printf("請輸入發送內容:");
		scanf_s("%s", sendBuf, 100);

		// 發送數據到服務器
		send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);

		memset(sendBuf, 0, 100);
		char c;
		while ((c = getchar()) != '\n');
	}


	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);


	// 關閉套接字並清除套接字庫
	closesocket(sockCli);
	WSACleanup();

	system("pause");

	return 0;
}

運行效果:

在這裡插入圖片描述

UDP

UDP就比較簡單瞭,步驟比tcp要少一些。

連接過程圖:

在這裡插入圖片描述

1). 服務器

初始化套接字庫

WORD wVersion;
WSADATA wsaData;
int err;

wVersion = MAKEWORD(1, 1);

創建套接字

SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);

綁定

// SOCKADDR_IN addrSrv; 省略瞭定義和賦值
bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

接收數據

char recvBuf[100];
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);

發送數據

char sendBuf[] = "hello Client,I'm Server!\n";
sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);

關閉

closesocket(sockSrv);
WSACleanup();

具體實現代碼

#include <WinSock2.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")

int main(void) {
	// 初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	wVersion = MAKEWORD(1, 1);
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}

	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return -1;
	}

	// 創建套接字
	SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6001);

	// 綁定到本機6001端口
	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

	// 接收請求,處理請求
	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR);

	char sendBuf[] = "hello Client,I'm Server!\n";
	char recvBuf[100];

	std::cout << "start UDP server with port 6001" << std::endl;
	while (1) {
		// 接收數據
		recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
		std::cout << "Recv:" << recvBuf << std::endl;

		// 發送數據
		sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);
		std::cout << "Send:" << sendBuf << std::endl;
	}

	closesocket(sockSrv);
	WSACleanup();

	return 0;
}

2). 客戶端

初始化套接字庫

WORD wVersion;
WSADATA wsaData;
int err;

wVersion = MAKEWORD(1, 1);

創建UDP套接字

SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSrv;

接收數據

char recvBuf[100];
recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);

發送數據

char sendBuf[] = "hello Client,I'm Server!\n";
sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);

關閉

closesocket(sockSrv);
WSACleanup();

具體實現代碼

#include <WinSock2.h>
#include <iostream>

#pragma comment(lib, "ws2_32.lib")


int main(void) {

	// 初始化套接字庫
	WORD wVersion;
	WSADATA wsaData;
	int err;

	wVersion = MAKEWORD(1, 1);
	err = WSAStartup(wVersion, &wsaData);
	if (err != 0) {
		return err;
	}

	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return -1;
	}

	// 創建UDP套接字
	SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6001);

	SOCKADDR_IN addrCli;
	int len = sizeof(SOCKADDR);

	char sendBuf[] = "hello, I'm Client!\n";
	char recvBuf[100];

	std::cout << "send to Server: " << sendBuf << std::endl;
	sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);

	recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
	std::cout << "recv from: " << recvBuf << std::endl;


	closesocket(sockCli);
	WSACleanup();

	system("pause");
	return 0;
}

運行效果:

在這裡插入圖片描述

總結

socket的具體細節用法我不太清楚,現階段也隻是熟悉TCP的一些簡單操作,UDP的話也還不是太懂,不懂的是不知道在具體項目中該如何進行使用它們。
那個TCP的小項目也隻是自己琢磨搞出來的,不知掉具體項目會不會這樣去寫!

到此這篇關於C++ Socket實現TCP與UDP網絡編程的文章就介紹到這瞭,更多相關C++ Socket實現TCP與UDP內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: