1. 程式人生 > >C++:實現socket通訊(TCP/IP)例項

C++:實現socket通訊(TCP/IP)例項

       首先宣告,博主之前從來沒有寫過通訊方面的東西,這次之所以寫這個是因為專案需要,因此本文主要介紹一個使用C++語言及Socket來實現TCP/IP通訊的例項,希望可以幫助入門者。

一、什麼是TCP/IP?

        TCP提供基於IP環境下的資料可靠性傳輸,事先需要進行三次握手來確保資料傳輸的可靠性。詳細的博主不再贅述,感興趣的朋友可以去search一下。

二、什麼是socket?    

        socket顧名思義就是套接字的意思,用於描述地址和埠,是一個通訊鏈的控制代碼。應用程式通過socket向網路發出請求或者回應。

        socket程式設計有三種,流式套接字(SOCK_STREAM),資料報套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),前兩者較常用。基於TCP的socket程式設計是流式套接字。

三、client/server即C/S模式:

       TCP/IP通訊中,主要是進行C/S互動。廢話不多說,下面看看具體互動內容:

       服務端:建立socket,申明自身的port和IP,並繫結到socket,使用listen監聽,然後不斷用accept去檢視是否有連線。如果有,捕獲socket,並通過recv獲取訊息的內容,通訊完成後呼叫closeSocket關閉這個對應accept到的socket。如果不需要等待任何客戶端連線,那麼用closeSocket直接關閉自身的socket。

        客戶端:建立socket,通過埠號和地址確定目標伺服器,使用Connect連線到伺服器,send傳送訊息,等待處理,通訊完成後呼叫closeSocket關閉socket。

四、程式設計步驟

1、server端

(1)載入套接字型檔,建立套接字(WSAStartup()/socket());

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

void initialization();

int main()
{
  //建立套接字
  s_server = socket(AF_INET, SOCK_STREAM, 0);

 

}


  void initialization() {
	//初始化套接字型檔
	WORD w_req = MAKEWORD(2, 2);//版本號
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字型檔失敗!" << endl;
	}
	else {
		cout << "初始化套接字型檔成功!" << endl;
	}
	//檢測版本號
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字型檔版本號不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字型檔版本正確!" << endl;
	}
	//填充服務端地址資訊
}

 (2)繫結套接字到一個IP地址和一個埠上(bind());

server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(5010);

   (3)將套接字設定為監聽模式等待連線請求(listen());

//設定套接字為監聽狀態
if (listen(s_server, SOMAXCONN) < 0) 
{
	cout << "設定監聽狀態失敗!" << endl;
	WSACleanup();
}
else 
{
	cout << "設定監聽狀態成功!" << endl;
}
cout << "服務端正在監聽連線,請稍候...." << endl;

   (4)請求到來後,接受連線請求,返回一個新的對應於此次連線的套接字(accept());

//接受連線請求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) 
{
  cout << "連線失敗!" << endl;
  WSACleanup();
  return 0;
}
  cout << "連線建立,準備接受資料" << endl;

    (5)用返回的套接字和客戶端進行通訊(send()/recv());

//接收資料
while (1)
 {
	recv_len = recv(s_accept, recv_buf, 100, 0);
	if (recv_len < 0)
        {
	  cout << "接受失敗!" << endl;
	  break;
	}
	else 
        {
	  cout << "客戶端資訊:" << recv_buf << endl;
	}
	cout << "請輸入回覆資訊:";
	cin >> send_buf;
	send_len = send(s_accept, send_buf, 100, 0);
	if (send_len < 0) 
        {
	  cout << "傳送失敗!" << endl;
	  break;
	}
}

    (6)返回,等待另一個連線請求;

  (7)關閉套接字,關閉載入的套接字型檔(closesocket()/WSACleanup());

//關閉套接字
closesocket(s_server);
closesocket(s_accept);
//釋放DLL資源
WSACleanup();
return 0;

     2、Client端

    (1)載入套接字型檔,建立套接字(WSAStartup()/socket);

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

void initialization();

int main()
{
//建立套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);

}

void initialization() {
	//初始化套接字型檔
	WORD w_req = MAKEWORD(2, 2);//版本號
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字型檔失敗!" << endl;
	}
	else {
		cout << "初始化套接字型檔成功!" << endl;
	}
	//檢測版本號
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字型檔版本號不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字型檔版本正確!" << endl;
	}
	//填充服務端地址資訊

}

   (2)向伺服器發出連線請求(connect());

if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "伺服器連線失敗!" << endl;
		WSACleanup();
	}
	else {
		cout << "伺服器連線成功!" << endl;
	}

  (3)和伺服器進行通訊(send()/recv());

//傳送,接收資料
	while (1) {
		cout << "請輸入傳送資訊:";
		cin >> send_buf;
		send_len = send(s_server, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "傳送失敗!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失敗!" << endl;
			break;
		}
		else {
			cout << "服務端資訊:" << recv_buf << endl;
		}

	}

   (4)關閉套接字,關閉載入的套接字型檔(closesocket()/WSACleanup())

//關閉套接字
	closesocket(s_server);
	//釋放DLL資源
	WSACleanup();
	

五、Windows下基於VS2017實現的socket簡單例項(TCP/IP)

(1)server端程式碼

#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
	//定義長度變數
	int send_len = 0;
	int recv_len = 0;
	int len = 0;
	//定義傳送緩衝區和接受緩衝區
	char send_buf[100];
	char recv_buf[100];
	//定義服務端套接字,接受請求套接字
	SOCKET s_server;
	SOCKET s_accept;
	//服務端地址客戶端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	initialization();
	//填充服務端資訊
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5010);
	//建立套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "套接字繫結失敗!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字繫結成功!" << endl;
	}
	//設定套接字為監聽狀態
	if (listen(s_server, SOMAXCONN) < 0) {
		cout << "設定監聽狀態失敗!" << endl;
		WSACleanup();
	}
	else {
		cout << "設定監聽狀態成功!" << endl;
	}
	cout << "服務端正在監聽連線,請稍候...." << endl;
	//接受連線請求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) {
		cout << "連線失敗!" << endl;
		WSACleanup();
		return 0;
	}
	cout << "連線建立,準備接受資料" << endl;
	//接收資料
	while (1) {
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失敗!" << endl;
			break;
		}
		else {
			cout << "客戶端資訊:" << recv_buf << endl;
		}
		cout << "請輸入回覆資訊:";
		cin >> send_buf;
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "傳送失敗!" << endl;
			break;
		}
	}
	//關閉套接字
	closesocket(s_server);
	closesocket(s_accept);
	//釋放DLL資源
	WSACleanup();
	return 0;
}
void initialization() {
	//初始化套接字型檔
	WORD w_req = MAKEWORD(2, 2);//版本號
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字型檔失敗!" << endl;
	}
	else {
		cout << "初始化套接字型檔成功!" << endl;
	}
	//檢測版本號
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字型檔版本號不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字型檔版本正確!" << endl;
	}
	//填充服務端地址資訊

}

(2)client端:

#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
	//定義長度變數
	int send_len = 0;
	int recv_len = 0;
	//定義傳送緩衝區和接受緩衝區
	char send_buf[100];
	char recv_buf[100];
	//定義服務端套接字,接受請求套接字
	SOCKET s_server;
	//服務端地址客戶端地址
	SOCKADDR_IN server_addr;
	initialization();
	//填充服務端資訊
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(1234);
	//建立套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "伺服器連線失敗!" << endl;
		WSACleanup();
	}
	else {
		cout << "伺服器連線成功!" << endl;
	}

	//傳送,接收資料
	while (1) {
		cout << "請輸入傳送資訊:";
		cin >> send_buf;
		send_len = send(s_server, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "傳送失敗!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失敗!" << endl;
			break;
		}
		else {
			cout << "服務端資訊:" << recv_buf << endl;
		}

	}
	//關閉套接字
	closesocket(s_server);
	//釋放DLL資源
	WSACleanup();
	return 0;
}
void initialization() {
	//初始化套接字型檔
	WORD w_req = MAKEWORD(2, 2);//版本號
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字型檔失敗!" << endl;
	}
	else {
		cout << "初始化套接字型檔成功!" << endl;
	}
	//檢測版本號
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字型檔版本號不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字型檔版本正確!" << endl;
	}
	//填充服務端地址資訊

}

注:對於入門級別學習的同學一些使用指導,想要讓這倆程式跑起來,如果只有一臺電腦,那麼只需要在一臺電腦上VS中建立兩個不同的控制檯應用程式,然後把server和client程式碼分別copy到這倆新建專案的主程式中,直接執行即可。

六、執行結果顯示

(1)server端

(2)client端