C++網絡編程快速入門(一):TCP網絡通信基本流程以及基礎函數使用

目錄

  • 流程概述
  • 服務器端代碼實現
  • 客戶端代碼實現
  • 函數和結構講解
    • sockaddr_in和sockaddr
    • socket : 創建一個socket連接
    • bind :綁定地址以及端口號問題

流程概述

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

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

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

1、調用socket函數創建客戶端socket
2、調用connect函數嘗試連接服務器
3、連接成功后調用send或recv函數與服務器進行數據交流
4、通信結束后,調用close函數關閉監聽socket

在這里插入圖片描述

服務器端代碼實現

#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>using namespace std;
int main() {// 創建一個監聽socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1) {cout << " create listen socket error " << endl;return -1;}// 初始化服務器地址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;}// 啟動監聽if (listen(listenfd, SOMAXCONN) == -1) {cout << "listen error" << endl;return -1;}while (true) {// 創建一個臨時的客戶端socketstruct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// 接受客戶端連接int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);if (clientfd != -1) {char recvBuf[32] = {0};// 從客戶端接受數據int ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {cout << "recv data from cilent , data:" << recvBuf << endl;// 將接收到的數據原封不動地發給客戶端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);}}// 關閉監聽socketclose(listenfd);return 0;
}

客戶端代碼實現

#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() {// 創建一個socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if (clientfd == -1) {cout << " create client socket error " << endl;return -1;}// 連接服務器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;}// 向服務器發送數據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;}// 從服務器拉取數據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;}// 關閉socketclose(clientfd);return 0;
}

函數和結構講解

sockaddr_in和sockaddr

在講解套接字編程函數之前,有必要對socket編程的兩個不可或缺的結構體進行說明:sockaddrsockaddr_in
結構如下:

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)];};

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

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

socket : 創建一個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; 

向用戶提供一個套接字,即套接口描述文件字,它是一個整數,如同文件描述符一樣,是內核標識一個IO結構的索引。
一般傳入參數是這樣的:

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

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

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

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

在服務器端我們有這樣一段代碼:

// 初始化服務器地址
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;
}

函數參數解釋:

/* 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)

如果應用程序不關心bind綁定的IP,則可以使用這個宏,底層的協議棧服務會自動選擇一個合適的IP地址,這樣在多個網卡機器上選擇IP地址會變得簡單
如果只想在本機上進行訪問,bind函數地址可以使用本地回環地址
如果只想被局域網的內部機器訪問,那么bind函數地址可以使用局域網地址
如果希望被公網訪問,那么bind函數地址可以使用INADDR_ANY or 0.0.0.0

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

圖1 不綁定
圖2 綁定

其他相關函數可以到往期文章中查看:
socket編程常見函數使用方法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376818.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376818.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376818.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php 字符串處理

addcslashes — 為字符串里面的部分字符添加反斜線轉義字符addslashes — 用指定的方式對字符串里面的字符進行轉義bin2hex — 將二進制數據轉換成十六進制表示chop — rtrim() 的別名函數chr — 返回一個字符的ASCII碼chunk_split — 按一定的字符長度將字符串分割成小塊conve…

使用前端框架Foundation 4來幫助簡化響應式設計開發

日期&#xff1a;2013-3-12 來源&#xff1a;GBin1.com Foundation是一套使用廣泛的前端開發套件&#xff0c;可以幫助你快速的網站。最近ZURB發布了一個新版本的Foundation 4前端框架&#xff0c;能夠有效的幫助你快速的開發響應式的網站。 和另外一個套知名的前端框架BootSt…

C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序

目錄select參數解釋select使用規范select使用缺點基本流程實例代碼通信效果演示往期文章select參數解釋 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android轉載一:Android文件命名規范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模塊.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[轉]XBRL應用軟件分類

1) 分類標準編輯軟件(Taxonomy editor)&#xff1a; 分類標準是XBRL技術的應用基礎&#xff0c;每一個采用XBRL技術的國家都必須先按各國的GAAP制訂XBRL分類標準&#xff0c;上市公司才能據以編制實例文件。由于一套XBRL 2.0或2.1版分類標準必須包含至少一份XML Schema文…

C++網絡編程快速入門(三):阻塞與非阻塞式調用網絡通信函數

目錄阻塞與非阻塞定義send與recvconnect一些問題為什么要將監聽socket設置為非阻塞阻塞與非阻塞定義 阻塞模式指的是當前某個函數執行效果未達預期&#xff0c;該函數會阻塞當前的執行線程&#xff0c;程序執行流在超時時間到達或者執行成功后恢復原有流程。非阻塞模式相反&am…

css3實現頭像旋轉360度

css樣式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的順序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址復用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket網絡編程中&#xff0c;大規模并發TCP或UDP連接時&#xff0c;經常會用到端口復用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是彈出problem occurred窗口

有的時候是因為jsp頁面中的java腳本有誤&#xff0c;比如說<% String name"";>就會出現錯誤&#xff0c;因為結束標簽少了一個百分號&#xff05;。轉載于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

C++網絡編程快速入門(四):EPOLL模型使用

目錄基本使用方法step1:創建epollfdstep2:將fd綁定到epollfdstep3:調用epoll_wait檢測事件epoll_wait與poll、select區別所在水平觸發與邊緣觸發基本使用方法 step1:創建epollfd 創建一個epollfd&#xff0c;若epoll_create調用成功&#xff0c;則返回一個非負值的epollfd&am…

Mysql中代替like模糊查詢的一種方法

使用Mysql的函數instr,可代替傳統的like方式查詢,并且速度更快。 instr函數&#xff0c;第一個參數是字段&#xff0c;第二個參數是要查詢的串&#xff0c;返回串的位置&#xff0c;第一個是1&#xff0c;如果沒找到就是0. 例如&#xff1a; select username from prefix_user …

兩種大小端判斷的方式

網絡通信是按照字節流進行數據交換的&#xff0c;主機根據不同的CPU型號可能是大段存儲&#xff0c;也可能是小端存儲。而網絡字節序在TCP/IP協議中已經規定好了&#xff0c;采用大端的排序方式。 所以網絡通信中一般將需要傳輸的整數型值轉換成網絡字節序。 從本機字節序轉換成…

把數據庫復制成腳本(包含遠程以及數據庫數據)

1.啟動VS 2.服務器資源管理器 3.連接需要的數據庫 4.右鍵數據庫 選擇publist to provider.... 5.剩下的 選擇數據庫 選擇存放地址 下一步 這方法應該是用在把08的數據還原到05上面 明天用這個方法去盜取哈公司的數據庫 看行不行轉載于:https://www.cnblogs.com/Rock-Lee/a…

代理模式用來初始化的延遲下載

package 設計模式; //代理模式實現延遲加載來減小啟動時間 //數據庫查詢接口 interface IDBQery{ public String request(); }class DBQuery implements IDBQery {//創建一個DBQery非常耗時的&#xff0c;這里面我可以在需要DBQuery的時候在創建public DBQuery(){try {Thread.s…

Linux網絡故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目錄ifconfig-s&#xff0c;顯示網卡信息的精簡列表-a、up、down將IP地址綁定到某個網卡&#xff0c;以及解綁操作pingtelnetnetstatlsofnc模擬一個服務器程序和客戶端程序進行通信發送文件curltcpdump參數連接一個正常的監聽端口ifconfig 該命令用來查看當前系統的網卡和IP地…

My Oracle Support Metalink站點最近將放棄flash界面轉而使用ADF HTML

根據oracle官方博客的報道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT開發team會在最近將support.oracle.com站點從原來的flash界面遷移到基于ADF HTML的用戶界面上。 實際上在2012年的 January 27&#xff0c; MOS開發team就…

BF算法

BF(Brute Force)算法是普通的模式匹配算法&#xff0c;BF算法的思想就是將目標串S的第一個字符與模式串T的第一個字符進行匹配&#xff0c;若相等&#xff0c;則繼續比較S的第二個字符和 T的第二個字符&#xff1b;若不相等&#xff0c;則比較S的第二個字符和T的第一個字符&…

心跳檢測以及應用層心跳包機制設計

博主聯系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950&#xff08;嵌入式方向&#xff09; QQ交流群&#xff1a;856398158&#xff08;后端方向&#xff09; 目錄心跳檢測應用場景死連接情況保活傳遞有效業務數據心跳包…

【APUE】孤兒進程與僵死進程

基本概念&#xff1a; 在unix/linux中&#xff0c;正常情況下&#xff0c;子進程是通過父進程創建的&#xff0c;子進程在創建新的進程。子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什么時候結束。 當一個 進程完成它的工作終止之后&#xff0c…