基於TCP的socket通信過程及例子


Socket也叫套接字,用來實現網絡通訊,通過調用系統提供的API,可以和遠程的機子傳輸數據。Socket有很多種協議,而這篇文章主要討論TCP部分的內容,也就是說后面說的內容主要是指TCP Socket。

Socket 的一般調用過程:

服務端:socket(), bind(),listen(),accept(),send(),recv(),close()

客戶端:socket(),connect(),send(),recv(),close()

阻塞socket(同步socket)

進程或線程執行到某些socket函數時必須等待該socket事件的發生,如果該事件沒有發生,socket函數不能立即返回,進程或線程被阻塞。

特點:使用簡單,適合一對一的應答場合,在服務端很少使用,或配合多線程使用

函數 返回值說明 阻塞情況
accept() 返回新的連接socket句柄。 緩沖區隊列沒有新的等待連接
connect() 返回-1說明連接失敗,其他正常。 連接過程阻塞。
recv() 返回值小於1代表接收失敗,其他代表接收數據的長度。 發送緩沖區有數據等待發送完成,或接收緩沖區沒數據時阻塞。
send() 返回-1代表發送失敗,其他為發送數據的長度 發送緩沖區沒有足夠空間保存此次發送數據時阻塞


非阻塞socket(異步socket)

進程或線程執行socket函數時不必非要等待該socket事件的發生,一旦執行立即返回。根據返回值的不同可以判斷函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生則返回一個代碼來告知事件未發生,而進程或線程可以不被阻塞,繼續執行。

特點:函數執行立即返回,不會阻斷進程,性能比阻塞高,適合在主線程直接調用,不會造成主線程卡頓現象

因為socket默認是阻塞的,所以要設置非阻塞模式:

 

#ifdef WIN32 
	DWORD nMode = 1; 
	ioctlsocket(m_sock, FIONBIO, &nMode);
#else
	int r = fcntl(fd, F_GETFL, 0));
	fcntl(fd, F_SETFL, r|O_NONBLOCK);
#endif

TCP與UDP 的區別


協議說明 socket創建
TCP 傳輸控制協議,可靠的連接服務。雙方先建立連接再傳輸數據。提供超時重發,數據檢驗,流量控制等機制,保證數據發送無誤。 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
UDP 用戶數據報協議,不可靠的連接服務。沒有建立連接就可以發送數據,沒有超時重發機制,傳輸速度很快。 socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)

Socket例子

下面以一個簡單例子來說明服務端與客戶端的交互過程

服務端 server.cpp

 

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

#define PORT 8080

int main()
{
	//初始化winsock服務
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);

	//創建socket
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	struct sockaddr_in svraddr;
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = htons(PORT);
	svraddr.sin_addr.s_addr = htonl(INADDR_ANY);

	//綁定socket
	bind(sock, (struct sockaddr*)&svraddr, sizeof(svraddr));

	//監聽socket
	listen(sock, 5);

	while(1)
	{
		struct sockaddr_in addr;
		int len = sizeof(SOCKADDR);

		char buf[1024] = {0};

		//接受客戶端連接
		SOCKET client = accept(sock, (struct sockaddr*)&addr, &len);

		char* ip = inet_ntoa(addr.sin_addr);
		printf("accept client: %s\r\n", ip);

		//接收客戶端數據
		if(recv(client, buf, 1024, 0) >0)
		{
			printf("recv client: %s\r\n", buf);

			//向客戶端發送數據
			send(client, "hello, client", strlen("hello, client"), 0);
		}
		closesocket(client);
	}

	//關閉socket
	closesocket(sock);

	//關閉winsock服務
	WSACleanup();

	return 0;
}

客戶端 client.cpp

 

 

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

#define REMOTE_IP "127.0.0.1"
#define REMOTE_PORT 8080

int main()
{
	//初始化winsock服務
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2), &wsaData);

	//創建socket
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	struct sockaddr_in svraddr;
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = htons(REMOTE_PORT);
	svraddr.sin_addr.s_addr = inet_addr(REMOTE_IP);

	//連接socket
	if( connect(sock, (struct sockaddr*)&svraddr, sizeof(svraddr)) != -1)
	{
		//發送數據給服務端
		if(send(sock, "hello, server", strlen("hello, server"), 0) != -1)
		{
			//接收服務端數據
			char buf[1024] = {0};
			if(recv(sock, buf, 1024, 0) >0)
			{
				printf("recv server: %s\r\n", buf);
			}
			
		}
	}
	else
	{
		printf("can not connect server\r\n");
	}

	//關閉socket
	closesocket(sock);

	//關閉winsock服務
	WSACleanup();
	getchar();
	return 0;
}

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM