日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用

發(fā)布時間:2023/12/1 c/c++ 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 流程概述
  • 服務(wù)器端代碼實現(xiàn)
  • 客戶端代碼實現(xiàn)
  • 函數(shù)和結(jié)構(gòu)講解
    • sockaddr_in和sockaddr
    • socket : 創(chuàng)建一個socket連接
    • bind :綁定地址以及端口號問題

流程概述

客戶端與服務(wù)器之間的網(wǎng)絡(luò)通信基本原理如下所示,復(fù)雜一點的架構(gòu)可能會添加消息中間件。
對于服務(wù)端,通信流程如下:

1、調(diào)用socket函數(shù)創(chuàng)建監(jiān)聽socket 2、調(diào)用bind函數(shù)將socket綁定到某個IP和端口號組成的二元組上 3、調(diào)用listen函數(shù)開啟監(jiān)聽 4、當有客戶端連接請求時,調(diào)用accept函數(shù)接受連接,產(chǎn)生一個新的socket(與客戶端通信的socket) 5、基于新產(chǎn)生的socket調(diào)用send或recv函數(shù)開始與客戶端進行數(shù)據(jù)交流 6、通信結(jié)束后,調(diào)用close函數(shù)關(guān)閉socket

對于客戶端,通信流程如下:

1、調(diào)用socket函數(shù)創(chuàng)建客戶端socket 2、調(diào)用connect函數(shù)嘗試連接服務(wù)器 3、連接成功后調(diào)用send或recv函數(shù)與服務(wù)器進行數(shù)據(jù)交流 4、通信結(jié)束后,調(diào)用close函數(shù)關(guān)閉監(jiān)聽socket

服務(wù)器端代碼實現(xiàn)

#include <iostream> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h>using namespace std; int main() {// 創(chuàng)建一個監(jiān)聽socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1) {cout << " create listen socket error " << endl;return -1;}// 初始化服務(wù)器地址struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;return -1;}// 啟動監(jiān)聽if (listen(listenfd, SOMAXCONN) == -1) {cout << "listen error" << endl;return -1;}while (true) {// 創(chuàng)建一個臨時的客戶端socketstruct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// 接受客戶端連接int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);if (clientfd != -1) {char recvBuf[32] = {0};// 從客戶端接受數(shù)據(jù)int ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {cout << "recv data from cilent , data:" << recvBuf << endl;// 將接收到的數(shù)據(jù)原封不動地發(fā)給客戶端ret = send(clientfd, recvBuf, strlen(recvBuf), 0);if (ret != strlen(recvBuf)) {cout << "send data error" << endl;} else {cout << "send data to client successfully, data " << recvBuf <<endl;}} else {cout << "recv data error" <<endl;}close(clientfd);}}// 關(guān)閉監(jiān)聽socketclose(listenfd);return 0; }

客戶端代碼實現(xiàn)

#include <iostream> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h>#define SERVER_ADDRESS "127.0.0.1" #define SERVER_PORT 3000 #define SEND_DATA "helloworld"using namespace std;int main() {// 創(chuàng)建一個socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if (clientfd == -1) {cout << " create client socket error " << endl;return -1;}// 連接服務(wù)器struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);serveraddr.sin_port = htons(SERVER_PORT);if (connect(clientfd, (struct sockaddr *)& serveraddr, sizeof(serveraddr)) == -1) {cout << "connect socket error" << endl;return -1;}// 向服務(wù)器發(fā)送數(shù)據(jù)int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);if (ret != strlen(SEND_DATA)) {cout << "send data error" << endl;return -1;} else {cout << "send data to client successfully, data " << SEND_DATA <<endl;}// 從服務(wù)器拉取數(shù)據(jù)char recvBuf[32] = {0};ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {cout << "recv data to client successfully, data " << recvBuf <<endl;} else {cout << "recv data to client error" << endl;}// 關(guān)閉socketclose(clientfd);return 0; }

函數(shù)和結(jié)構(gòu)講解

sockaddr_in和sockaddr

在講解套接字編程函數(shù)之前,有必要對socket編程的兩個不可或缺的結(jié)構(gòu)體進行說明:sockaddr 和 sockaddr_in
結(jié)構(gòu)如下:

struct sockaddr{__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */char sa_data[14]; /* Address data. */};/* Structure describing an Internet socket address. */ struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port; /* Port number. */struct in_addr sin_addr; /* Internet address. *//* Pad to size of `struct sockaddr'. */unsigned char sin_zero[sizeof (struct sockaddr)- __SOCKADDR_COMMON_SIZE- sizeof (in_port_t)- sizeof (struct in_addr)];};

由于歷史的原因,套接字函數(shù)中(如connect,bind等)使用的參數(shù)類型大多是sockaddr類型的。而如今進行套接字編程的時候大都使用sockaddr_in進行套接字地址填充.因此,這就要求對這些函數(shù)進行調(diào)用的時候都必須要講套接字地址結(jié)構(gòu)指針進行類型強制轉(zhuǎn)換,例如:

struct sockaddr_in serv;bind(sockfd,(struct sockaddr *)&serv,sizeof(serv));

socket : 創(chuàng)建一個socket連接

/* Create a new socket of type TYPE in domain DOMAIN, usingprotocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.Returns a file descriptor for the new socket, or -1 for errors. */ extern int socket (int __domain, int __type, int __protocol) __THROW;

向用戶提供一個套接字,即套接口描述文件字,它是一個整數(shù),如同文件描述符一樣,是內(nèi)核標識一個IO結(jié)構(gòu)的索引。
一般傳入?yún)?shù)是這樣的:

int clientfd = socket(AF_INET, SOCK_STREAM, 0);

__domain:這個參數(shù)指定一個協(xié)議簇,也往往被稱為協(xié)議域。系統(tǒng)存在許多可以的協(xié)議簇,常見有AF_INET──指定為IPv4協(xié)議,AF_INET6──指定為IPv6,AF_LOCAL──指定為UNIX 協(xié)議域。這里指網(wǎng)絡(luò)層的協(xié)議
__type:這個參數(shù)指定一個套接口的類型,套接口可能的類型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它們分別表明字節(jié)流、數(shù)據(jù)報、有序分組、原始套接口。
__protocol:指定相應(yīng)的傳輸協(xié)議,也就是諸如TCP或UDP協(xié)議等等,系統(tǒng)針對每一個協(xié)議簇與類型提供了一個默認的協(xié)議,我們通過把protocol設(shè)置為0來使用這個默認的值。這里指傳輸層的協(xié)議
返回值:socket函數(shù)返回一個套接字,即套接口描述字。如果出現(xiàn)錯誤,它返回-1,并設(shè)置errno為相應(yīng)的值,用戶應(yīng)該檢測以判斷出現(xiàn)什么錯

返回值:成功則返回0,失敗返回非0

bind :綁定地址以及端口號問題

在服務(wù)器端我們有這樣一段代碼:

// 初始化服務(wù)器地址 struct sockaddr_in bindaddr; bindaddr.sin_family = AF_INET; bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); bindaddr.sin_port = htons(3000); if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;return -1; }

函數(shù)參數(shù)解釋:

/* Give the socket FD the local address ADDR (which is LEN bytes long). */ extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)

bind的地址我們使用了宏INADDR_ANY,這個宏定義如下:

/* Address to accept any incoming messages. */ #define INADDR_ANY ((in_addr_t) 0x00000000)

如果應(yīng)用程序不關(guān)心bind綁定的IP,則可以使用這個宏,底層的協(xié)議棧服務(wù)會自動選擇一個合適的IP地址,這樣在多個網(wǎng)卡機器上選擇IP地址會變得簡單。
如果只想在本機上進行訪問,bind函數(shù)地址可以使用本地回環(huán)地址
如果只想被局域網(wǎng)的內(nèi)部機器訪問,那么bind函數(shù)地址可以使用局域網(wǎng)地址
如果希望被公網(wǎng)訪問,那么bind函數(shù)地址可以使用INADDR_ANY or 0.0.0.0

網(wǎng)絡(luò)通信的基本邏輯是客戶端連接服務(wù)器,即從客戶端的地址:端口 連接到 服務(wù)器的地址:端口上。
一般來說,服務(wù)器的端口號是固定的,而客戶端的端口號是連接發(fā)起時操作系統(tǒng)隨機分配的,并且不會分配被占用的端口。端口號是一個short類型的值,其范圍為0~65535.
如果將bind函數(shù)中的端口號設(shè)置為0,那么操作系統(tǒng)會隨機為程序分配一個可用的監(jiān)聽端口。一般來說,服務(wù)程序不會這么做,因為服務(wù)程序是要對外服務(wù)的,必須讓客戶端知道確切的IP地址和端口號。
在特殊的應(yīng)用中,我們也可以在客戶端程序以指定的端口號連接服務(wù)器,與普通的流程相比就是在創(chuàng)建socket與發(fā)起connect之間多了一次bind操作:

圖1 不綁定 圖2 綁定

其他相關(guān)函數(shù)可以到往期文章中查看:
socket編程常見函數(shù)使用方法

總結(jié)

以上是生活随笔為你收集整理的C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。