C++實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶端與服務(wù)端的通信(筆記附代碼)-創(chuàng)新互聯(lián)

目錄

成都創(chuàng)新互聯(lián)公司是一家專(zhuān)注于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)與策劃設(shè)計(jì),名山網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:名山等地區(qū)。名山做網(wǎng)站價(jià)格咨詢(xún):18982081108

前言

一、Socket的客戶端與服務(wù)端的通訊原理

二、各接口介紹

1.WSAStartup:異步啟動(dòng)套接字命令

2.Socket創(chuàng)建套接字

3.bind:綁定套接字

4.listen:監(jiān)聽(tīng)

5.accept:接受連接請(qǐng)求

6. connet:發(fā)送連接請(qǐng)求

7.send:發(fā)送數(shù)據(jù)

8.recv:接收數(shù)據(jù)函數(shù)?

9.closesocket,?WSACleanup:釋放socket

三、代碼塊的簡(jiǎn)單實(shí)現(xiàn)(可直接使用)

? (1) 服務(wù)器

(2)客戶端

效果展示:

完結(jié)撒花



前言

?首先、在敲代碼之前我們必須先要了解服務(wù)端與客戶端之前的通訊原理,否則我們敲代碼將會(huì)無(wú)從下手、了解原理后也會(huì)對(duì)我們的敲代碼的效率達(dá)到事半功倍的效果!

? 在c++里面有一個(gè)我們可以通過(guò)直接調(diào)用一些api來(lái)實(shí)現(xiàn)、前提是我們要聲明其頭文件并且連接到其提供的動(dòng)態(tài)鏈接庫(kù)!

在Windows系統(tǒng)中的剛好有這么一個(gè)、即Winsock。聲明頭文件為:

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

一、Socket的客戶端與服務(wù)端的通訊原理

如圖所示:圖左(客戶端創(chuàng)建過(guò)程) 圖右(服務(wù)器創(chuàng)建過(guò)程)

交互過(guò)程:圖左(客戶端創(chuàng)建過(guò)程) 圖右(服務(wù)器創(chuàng)建過(guò)程)

二、各接口介紹 1.WSAStartup:異步啟動(dòng)套接字命令
1、函數(shù)原型:

int WSAAPI WSAStartup(
  WORD      wVersionRequested,
  LPWSADATA lpWSAData
);

2、參數(shù):

@WORD wVersionRequested:調(diào)用者可以使用的Windows套接字規(guī)范的最高版本。 高位字節(jié)指定次要版本號(hào);             
                        低位字節(jié)指定主要版本號(hào)。[這個(gè)版本我也不太清楚、但是大部分人用的是2.2、 
                        所以我也用的2.2]
                        但是可借鑒這篇文章: 

@LPWSADATA lpWSAData:指向WSADATA數(shù)據(jù)結(jié)構(gòu)的指針,該數(shù)據(jù)結(jié)構(gòu)將接收Windows套接字實(shí)現(xiàn)的詳細(xì)信息

3、返回值:

返回0執(zhí)行正確
否則失敗

4、代碼如下:
WSADATA wsdata;
if (WSAStartup(MAKEWORD(2, 2), &wsdata))
{
?? ?std::cout<< "init socket failed!"<< std::endl;
?? ?WSACleanup();
?? ?return FALSE;
}
2.Socket創(chuàng)建套接字
1、函數(shù)原型:

    SOCKET socket(int af,int type,int protocl);

    參數(shù):
            @int af:第一個(gè)參數(shù)(af)指定地址族,對(duì)于TCP/IP協(xié)議的套接字他有以下兩個(gè)參數(shù):
                    AF_INET,PF_INET IPV4協(xié)議
                    PF_INET6 IPV6協(xié)議
                    其他還有很多協(xié)議這里不做介紹

            @int type:用于設(shè)置套接字通信的類(lèi)型,有流式套接字(SOCKET_STREAM)和數(shù)據(jù)包套接字                
             (SOCK_DGRAM)
              SOCK_STREAM TCP連接,提供有序化的、可靠的、雙向連接的字節(jié)流。支持帶外數(shù)據(jù)傳輸
              SOCK_DGRAM UDP連接

            @int protocl:用于制定某個(gè)協(xié)議的特定類(lèi)型,即type類(lèi)型中的某個(gè)類(lèi)型,通常一種協(xié)議只有一    
                         種類(lèi)型,該參數(shù)可以直接被設(shè)置為0;如果協(xié)議有多種類(lèi)型,則需要指定協(xié)議類(lèi) 
                         型

2、返回值
  如果沒(méi)有錯(cuò)誤發(fā)生,socket()返回一個(gè)與建立的套接字相關(guān)的描述符。
  否則它返回值INVALID_SOCKET,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到


3、代碼范例:
//定義一個(gè)套接字 作為服務(wù)端
SOCKET s_server;
s_server = socket(PF_INET, SOCK_STREAM, 0);
if (s_server == INVALID_SOCKET)
{
	std::cout<< "create socket fail"<< std::endl;
	WSACleanup();
	return FALSE;
}
3.bind:綁定套接字
1、函數(shù)原型:
    int bind(
          SOCKET   s,
          const sockaddr *addr,
          int      namelen
    );

    @ SOCKET   s:要綁定的套接字
    @const sockaddr *addr:指向要分配給綁定套接字的本地地址的sockaddr結(jié)構(gòu)的指針

    struct sockaddr {
        ushort  sa_family;
        char    sa_data[14];
    };


    struct sockaddr_in {
        short   sin_family;             //指定協(xié)議家族 ipv4 or ipv6
        u_short sin_port;               //指定端口號(hào)
        struct  in_addr sin_addr;       //指定地址 也可以是所有ip 參數(shù)為:INADDR_ANY 
        char    sin_zero[8];            //沒(méi)有特殊意義不用管
    };

    struct in_addr {
          union {
                struct {
                      u_char s_b1;
                      u_char s_b2;
                      u_char s_b3;
                      u_char s_b4;
                } S_un_b;
                struct {
                      u_short s_w1;
                      u_short s_w2;
                } S_un_w;
             u_long S_addr;           //ip地址
          } S_un;
     };


   @int namelen:sockaddr結(jié)構(gòu)的指針的大小


2、返回值
如果沒(méi)有錯(cuò)誤發(fā)生,bind()返回0。
否則返回值SOCKET_ERROR,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到

3、代碼范例:
    //填充服務(wù)端信息
	SOCKADDR_IN server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8224);
    server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    //數(shù)據(jù)綁定服務(wù)器 s_server為服務(wù)端套接字
	if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
	{
		std::cout<< "Binding Socket fail.......!"<< std::endl;
		WSACleanup();
		return FALSE;
	}

4.listen:監(jiān)聽(tīng)

1、函數(shù)原型:
    int PASCAL FAR listen (
                       _In_ SOCKET s,
                       _In_ int backlog);
    @SOCKET s:服務(wù)端的socket,也就是socket函數(shù)創(chuàng)建的
    @int backlog:掛起的連接隊(duì)列的大長(zhǎng)度,由用戶自主選擇。但是我們知道我們電腦處理線程的時(shí)候用一時(shí)間只能處理n個(gè)線程、這里也是一樣的原理,我們創(chuàng)建的服務(wù)器他可能不支持你定義的這個(gè)隊(duì)列長(zhǎng)度的用戶
比如:一個(gè)洗手間 只能同時(shí)供4個(gè)人使用 而這個(gè)時(shí)候來(lái)了8個(gè)人 那么其他4個(gè)人則需要外面等待。                
所以一般填寫(xiě)這個(gè)參數(shù)為SOMAXCONN
作用是讓系統(tǒng)自動(dòng)選擇最合適的個(gè)數(shù)
不同的系統(tǒng)環(huán)境不一樣,所以這個(gè)合適的數(shù)也不一樣


2、返回值
    成功返回0
    失敗返回SOCKET_ERROR
    具體錯(cuò)誤碼:WSAGetLastError()得到

3、代碼示例:
//監(jiān)聽(tīng)
if (listen(s_server, 1) == SOCKET_ERROR)
{
	std::cout<< "Listening Socket fail........!"<< std::endl;
	WSACleanup();
	return FALSE;
}
5.accept:接受連接請(qǐng)求
1、函數(shù)原型:
        SOCKET WSAAPI    accept (

            SOCKET s, 
            struct sockaddr FAR*  addr, 
            int  FAR* addrlen
          );
        @SOCKET s:該套接字在用作accept()函數(shù)的參數(shù)前必須先調(diào)用過(guò)listen()函數(shù),此時(shí)它正處于監(jiān)聽(tīng)連接的狀態(tài)。
        @struct sockaddr FAR*  addr:一個(gè)可選的指向緩沖區(qū)的指針,用來(lái)接收連接實(shí)體的地址,在通訊層使用。addr的確切格式由套接字創(chuàng)建時(shí)建立的地址族決定
        @int FAR* addrlen:一個(gè)可選的指向整數(shù)的指針,它調(diào)用時(shí)含有地址addr指向的空間的大小,返回時(shí)含有返回的地址的確切長(zhǎng)度(字節(jié)數(shù))。

2、返回值
如果沒(méi)有錯(cuò)誤發(fā)生,accept()返回一個(gè)SOCKET類(lèi)型的值,表示接收到的套接字的描述符。
否則返回值INVALID_SOCKET,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到。

2、代碼范例:
//接受連接請(qǐng)求
sockaddr_in accept_addr;         //用來(lái)記錄請(qǐng)求連接的套接字信息
int len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len ); 
if (s_accept == SOCKET_ERROR) {
		std::cout<< "Error: accept failed !"<< std::endl;
		WSACleanup();
		break;
	}
6. connet:發(fā)送連接請(qǐng)求
1、函數(shù)原型:
 int WSAAPI connect (

              SOCKET  s,                 
              const struct sockaddr FAR* name,  
              int  namelen   //socket address結(jié)構(gòu)的字節(jié)數(shù) 
 );

    @SOCKET  s:發(fā)出連接請(qǐng)求的套接字的描述符
    @const struct sockaddr FAR* name:對(duì)等方的套接字的地址、就是你創(chuàng)建客戶端的套接字時(shí)綁定ip和                
                                      端口號(hào)的套接字地址
    @namelen: 套接字類(lèi)型大小


2、返回值
如果沒(méi)有錯(cuò)誤發(fā)生,connect()返回0。
否則返回值SOCKET_ERROR,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到。

3、代碼范例:

    //填充服務(wù)端信息
	//SOCKADDR_IN server_addr;
	//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(8224);
	//創(chuàng)建套接字
	//SOCKET client= socket(AF_INET, SOCK_STREAM, 0);
	if (connect(client, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		std::cout<< "Error: connect server failed !"<< std::endl;
		WSACleanup();
		return -1;
	}
7.send:發(fā)送數(shù)據(jù)
1、函數(shù)原型:
 int WSAAPI send(

                   SOCKET  s,    
                   const char FAR *  buf,   
                   int  len,
                   int  flags
                );
                    
    @SOCKET  s: 已連接的套接字描述符
    @ const char FAR *  buf:指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針
    @int  len:緩沖區(qū)buf中數(shù)據(jù)長(zhǎng)度
    @ int  flags:一般為0,為阻塞發(fā)送 即發(fā)送不成功會(huì)一直阻塞,直到被某個(gè)信號(hào)終端終止,或者直到發(fā)                                
                         送成功為止。
                         指定MSG_NOSIGNAL,表示當(dāng)連接被關(guān)閉時(shí)不會(huì)產(chǎn)生SIGPIPE信號(hào)
                         指定MSG_DONTWAIT 表示非阻塞發(fā)


2、返回值

如果沒(méi)有錯(cuò)誤發(fā)生,send()返回總共發(fā)送的字節(jié)數(shù)(注意,這可能比len指示的長(zhǎng)度?。?否則它返回SOCKET_ERROR,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到。

2、代碼范例:
    char temp[1024] = { 0 };
    snprintf(temp, sizeof(temp), "%s", detectInfo);
	int sendLen = send(socket, (char*)temp, sizeof(temp), 0);
	if (sendLen< 0) {
		std::cout<< "Error: send info to server failed !"<< std::endl;
		return -1;
	}
8.recv:接收數(shù)據(jù)函數(shù)?
1、函數(shù)原型:
    int PASCAL FAR recv (
                SOCKET s,
                writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
                int len,
                int flags);
            
     @SOCKET s:已連接的套接字描述符。
     @char FAR * buf:指向接收輸入數(shù)據(jù)緩沖區(qū)的指針
     @int len:buf參數(shù)所指緩沖區(qū)的長(zhǎng)度
     @int flags:一般為0

2、返回值
如果沒(méi)有錯(cuò)誤發(fā)生,recv()返回收到的字節(jié)數(shù)。
如果連接被關(guān)閉,返回0。
否則它返回SOCKET_ERROR,錯(cuò)誤碼可通過(guò)調(diào)用WSAGetLastError()函數(shù)得到。

3、代碼示例:
    char recv_buf[8192] = { 0 };
	int recv_len = recv(socket, recv_buf, sizeof(recv_buf), 0);
	if (recv_len< 0) {
		std::cout<< "Error: receive info from server failed !"<< std::endl;
		return -1;
	}
9.closesocket,?WSACleanup:釋放socket
使用完后續(xù)如果不再使用 記住一定要釋放相關(guān)資源 否則你懂的。
//關(guān)閉套接字 參數(shù):需要關(guān)閉的套接字描述符
closesocket(s_server);
//釋放DLL資源
WSACleanup();
三、代碼塊的簡(jiǎn)單實(shí)現(xiàn)(可直接使用) ? ? ? ? (1) 服務(wù)器
// ServerTest.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開(kāi)始并結(jié)束。
//

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

int main()
{
    std::cout<< "Hello World!\n";
	WSADATA wsdata;
	std::cout<< "start up now ...."<< std::endl;
	if (WSAStartup(MAKEWORD(2, 2), &wsdata))
	{
		std::cout<< "init socket failed!"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	std::cout<< "before create socket ...."<< std::endl;
	SOCKET s_server, s_accept;
	s_server = socket(PF_INET, SOCK_STREAM, 0);
	if (s_server == INVALID_SOCKET)
	{
		std::cout<< "create socket fail"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	std::cout<< "create socket success...."<< std::endl;
	//填充服務(wù)端信息
	SOCKADDR_IN server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(8226);
    //當(dāng)然這里也可以將 127.0.0.1改成你想要的ip或者 INADDR_ANY 為向所有ip發(fā)送信息
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	//數(shù)據(jù)綁定服務(wù)器 s_server為服務(wù)端套接字
	std::cout<< "before bind socket ...."<< std::endl;
	if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
	{
		std::cout<< "Binding Socket fail.......!"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	std::cout<< "bind socket success...."<< std::endl;
	std::cout<< "before listen socket ...."<< std::endl;
	if (listen(s_server, 1) == SOCKET_ERROR)
	{
		std::cout<< "Listening Socket fail........!"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	std::cout<< "listen socket success...."<< std::endl;
	sockaddr_in accept_addr;         //用來(lái)記錄請(qǐng)求連接的套接字信息
	int len = sizeof(SOCKADDR);
	char recv_buf[8192] = { 0 };
	char send_buf[1024] = { 0 };
	while (true)
	{
		//接受連接請(qǐng)求
		std::cout<< "wait accept...."<< std::endl;
		s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
		if (s_accept == SOCKET_ERROR) {
			std::cout<< "Error: accept failed !"<< std::endl;
			WSACleanup();
			break;
		}
		std::cout<< "建立連接成功...."<< std::endl;
		while (true)
		{
			std::cout<< "等待客戶端數(shù)據(jù)中...."<< std::endl;
			int recv_len = recv(s_accept, recv_buf, sizeof(recv_buf), 0);
			if (recv_len< 0) {
				std::cout<< "Error: receive info from server failed !"<< std::endl;
				return -1;
			}
			std::cout<< "已收到數(shù)據(jù),客戶端的數(shù)據(jù)是:"<< recv_buf<< std::endl;
			std::cout<< "給客戶端返回?cái)?shù)據(jù)中..."<< std::endl;
			snprintf(send_buf, sizeof(send_buf), "%s", "我給你返回信息了");
			int sendLen = send(s_accept, (char*)send_buf, sizeof(send_buf), 0);
			if (sendLen< 0) {
				std::cout<< "Error: send info to server failed !"<< std::endl;
				return -1;
			}
			std::cout<< "給客戶端返回?cái)?shù)據(jù)完畢"<< std::endl;
		}
		
	}
}
(2)客戶端
// ClientTest.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開(kāi)始并結(jié)束。
//
#include#include#pragma comment(lib,"ws2_32.lib")
int main()
{
    std::cout<< "Hello World!\n";
	WSADATA wsdata;
	if (WSAStartup(MAKEWORD(2, 2), &wsdata))
	{
		std::cout<< "init socket failed!"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	//檢測(cè)版本號(hào)
	if (LOBYTE(wsdata.wVersion) != 2 || HIBYTE(wsdata.wHighVersion) != 2) {
		std::cout<< "Error: wsadata.wVersion != 2 ."<< std::endl;
		WSACleanup();
		return -1;
	}
	SOCKET client;
	client = socket(PF_INET, SOCK_STREAM, 0);
	if (client == INVALID_SOCKET)
	{
		std::cout<< "create socket fail"<< std::endl;
		WSACleanup();
		return FALSE;
	}
	//填充服務(wù)端信息
	SOCKADDR_IN server_addr;
	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(8226);
	//發(fā)送連接請(qǐng)求 請(qǐng)求連接服務(wù)器
	if (connect(client, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		std::cout<< "Error: connect server failed !"<< std::endl;
		WSACleanup();
		return -1;
	}
	std::cout<< "成功連接到服務(wù)器"<< std::endl;
	//發(fā)送數(shù)據(jù)
	char temp[1024] = { 0 };
	std::cout<< "開(kāi)始發(fā)送數(shù)據(jù)....."<< std::endl;
	snprintf(temp, sizeof(temp), "%s", "我是客戶端 我發(fā)送信息給你了服務(wù)端");
	int sendLen = send(client, (char*)temp, sizeof(temp), 0);
	if (sendLen< 0) {
		std::cout<< "Error: send info to server failed !"<< std::endl;
		return -1;
	}
	std::cout<< "發(fā)送數(shù)據(jù)成功、等待服務(wù)器響應(yīng)....."<< std::endl;
	char recv_buf[8192] = { 0 };
	int recv_len = recv(client, recv_buf, sizeof(recv_buf), 0);
	if (recv_len< 0) {
		std::cout<< "Error: receive info from server failed !"<< std::endl;
		return -1;
	}
	std::cout<< "收到了服務(wù)器返回的信息 內(nèi)容是:"<< recv_buf<< std::endl;

	system("pause");
}

效果展示:

完結(jié)撒花

當(dāng)然、以上都是一些比較簡(jiǎn)單的實(shí)現(xiàn)、更加深層次我的也慢慢學(xué)習(xí)咯~比如像這個(gè)客戶端和服務(wù)端發(fā)送數(shù)據(jù)、我們完全可以再發(fā)送數(shù)據(jù)和接收數(shù)據(jù)之前對(duì)數(shù)據(jù)進(jìn)行一些處理、也就是說(shuō)我們可以先加一個(gè)判斷、先判斷這個(gè)發(fā)過(guò)過(guò)來(lái)的數(shù)據(jù)是否符合我想接收的要求、如果不是我選擇不往下接續(xù)接收信息。

以上只是個(gè)人學(xué)習(xí)的一些基礎(chǔ)、有哪里不對(duì)的話 歡迎糾正哈

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

網(wǎng)站題目:C++實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶端與服務(wù)端的通信(筆記附代碼)-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://muchs.cn/article16/dhgggg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、面包屑導(dǎo)航、做網(wǎng)站定制網(wǎng)站、虛擬主機(jī)、網(wǎng)站改版

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司