C++?TCP網(wǎng)絡(luò)編程詳細講解
TCP介紹與編程流程
1、面向連接的流式協(xié)議;可靠、出錯重傳、且每收到一個數(shù)據(jù)都要給出相應的確認
2、通信之前需要建立鏈接
3、服務器被動鏈接,客戶端是主動鏈接
TCP C/S架構(gòu)
TCP客戶端編程流程
- socket 創(chuàng)建套接字
- connect 連接服務器
- send 發(fā)送請求
- recv 接收應答
- close
1. 創(chuàng)建TCP套接字
socket創(chuàng)建的套接字:沒有端口、主動連接別人
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
注:TCP是SOCK_STREAM, UDP為SOCK_DGRAM
2. connect連接服務器
如果sockfd沒有被bind綁定,第一次調(diào)用connect系統(tǒng)自動分配隨機端口,后續(xù)使用該端口
int connect(int socket, const struct sockaddr *address, socklen_t address_len) /* socket: 套接字 address: 連接的服務器地址結(jié)構(gòu) address_len: 地址結(jié)構(gòu)長度 */
注:如果客戶端和服務器通信,必須使用connect事先建立連接
3. send發(fā)送請求
ssize_t send(int socket, const void *buffer, size_t length, int flags) /* socket: 客戶端套接字 buffer:發(fā)送的消息 length:消息長度 flags: 0 返回值: 成功:返回發(fā)送的字節(jié)數(shù) 失?。悍祷?1 */
4. recv接收應答(默認帶阻塞)
ssize_t recv(int socket, void *buffer, size_t length, int flags); /* socket: 客戶端套接字 buffer: 接收的消息 length:能接收的最大長度 flags:0 返回值: 成功:成功接收的字節(jié)數(shù) 失敗:-1 */
5. close
#include <unistd.h> int close(int fildes);
6. 客戶端編程流程代碼
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> int main(int argc, char const *argv[]) { //創(chuàng)建tcp套接字 SOCK_STREAN int sockfd = socket(AF_INET, SOCK_STREAM, 0); //connect連接服務器(知道服務器地址信息) struct sockaddr_in ser_addr; bzero(&ser_addr, sizeof(ser_addr)); ser_addr.sin_family = AF_INET; ser_addr.sin_port = htons(8000); ser_addr.sin_addr.s_addr = inet_addr("10.9.21.211"); connect(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)); //客戶端發(fā)送請求 send(sockfd, "hello tcp", strlen("hello tcp"), 0 ); //客戶端接收服務器的應答 unsigned char buf[1500] = ""; int len = recv(sockfd, buf, sizeof(buf), 0); printf("服務器的應答:%s\n", buf); //關(guān)閉套接字 close(sockfd); return 0; }
TCP服務端編程流程
- socket 創(chuàng)建套接字
- bind 綁定固定的port、ip地址信息
- listen 監(jiān)聽套接字 創(chuàng)建連接隊列
- accept
- send 發(fā)送請求
- recv 接收應答
- close
1. 創(chuàng)建TCP套接字
socket創(chuàng)建的套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
注:TCP是SOCK_STREAM, UDP為SOCK_DGRAM
2. bind給服務器綁定固定的port與IP地址信息
struct sockaddr_in my_addr; bzero(&my_addr, sizeof(my_addr)): my_addr.sin_family = AF_INET; my_addr.sin_port = htons(8000); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)):
3. listen監(jiān)聽并創(chuàng)建隊列
listen監(jiān)聽:等待客戶端的連接到來,經(jīng)過三次握手(底層自動),將客戶端放入入連接隊列
#include <sys/socket.h> int listen(int socket, int backlog); /* 功能: 1. 將監(jiān)聽套接字由主動變被動 2. 為該套接字創(chuàng)建連接隊列 參數(shù): socket:變被動的套接字 backlog:連接隊列的大小 返回值: 成功:0 失敗:-1 */
4. accept提取客戶端的連接(阻塞)
#include <sys/socket.h> int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen); /* 功能: 從已連接隊列中取出一個已經(jīng)建立的連接,如果沒有任何連接可用,則進入睡眠等待(阻塞) 參數(shù): sockfd: socket監(jiān)聽套接字 cliaddr: 用于存放客戶端套接字地址結(jié)構(gòu) addrlen:套接字地址結(jié)構(gòu)體長度的地址 返回值: 已連接套接字 */
注:返回的是一個已連接套接字,服務器只能通過已連接套接字和客戶端進行數(shù)據(jù)通信
5. send 發(fā)送消息到客戶端
ssize_t send(int socket, const void *buffer, size_t length, int flags) /* socket: 客戶端套接字 buffer:發(fā)送的消息 length:消息長度 flags: 0 返回值: 成功:返回發(fā)送的字節(jié)數(shù) 失?。悍祷?1 */
注:如果ssize_t>0,表示發(fā)送成功(實際發(fā)送的字節(jié)數(shù))
如果ssize_t為-1,表示發(fā)送是失敗
tcp不允許send發(fā)送0長度報文(服務端接收0長度報文則為客戶端斷開連接)
udp允許sendto發(fā)送0長度報文
6. recv 接收客戶端的消息
ssize_t recv(int socket, void *buffer, size_t length, int flags); /* socket: 客戶端套接字 buffer: 接收的消息 length:能接收的最大長度 flags:0 返回值: 成功:成功接收的字節(jié)數(shù) 失?。?1 */
注:如果ssize_t為0,表示客戶端已經(jīng)斷開連接
如果ssize_t>0,表示recv收到的實際字節(jié)數(shù)
如果ssize_t為-1,表示recv讀取數(shù)據(jù)出錯
7. close關(guān)閉所有套接字
#include <unistd.h> int close(套接字);
注:close后會導致對方recv收到0長度報文
8. TCP服務端編程流程代碼
#include <stdio.h> #include <sys/socket.h> //socket #include <netinet/in.h> //struct sockaddr_in #include <string.h> //memset #include <arpa/inet.h> //htos #include <unistd.h> //close int main() { //創(chuàng)建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { perror("cockt"); return 0; } //bind綁定固定的port、ip地址信息 struct sockaddr_in my_addr; bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(9000); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)); if(ret == -1) { perror("bind"); return 0; } //監(jiān)聽套接字 創(chuàng)捷連接隊列 ret = listen(sockfd, 10); if(ret == -1) { perror("listen"); return 0; } //提取客戶端的連接 while(1) { //一次只能提取一個客戶端 struct sockaddr_in cli_addr; socklen_t cli_len = sizeof(cli_addr); int cfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len); if(cfd < 0 ) //提取失敗 { perrer("accept\n"); break; } else { char ip[16] = ""; unsigned short port = 0; inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr, ip, 16); port = ntohs(cli_addr.sin_port); //打印客戶端的信息 printf("客戶端:%s %d connected\n", ip, port); while(1) { //獲取客戶端的請求 unsigned char buf[1500] = ""; int len = recv(cfd, buf, sizeof(buf), 0); if(len == 0) //客戶端已經(jīng)關(guān)閉 { //關(guān)閉與客戶端連接的套接字 close(cfd); break; } //應答客戶端 send(cfd, buf, len, 0); } } } //關(guān)閉監(jiān)聽套接字 close(sockfd); return 0; }
到此這篇關(guān)于C++ TCP網(wǎng)絡(luò)編程詳細講解的文章就介紹到這了,更多相關(guān)C++ TCP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用?Visual?Studio?2022?開發(fā)?Linux?C++?應用程序的過程詳解
Visual?Studio?2022?引入了用于?Linux?C++?開發(fā)的本機?WSL2?工具集,可以構(gòu)建和調(diào)試?Linux?C++?代碼,并提供了非常好的?Linux?文件系統(tǒng)性能、GUI?支持和完整的系統(tǒng)調(diào)用兼容性,這篇文章主要介紹了使用Visual?Studio?2022?開發(fā)?Linux?C++?應用程序,需要的朋友可以參考下2021-11-11C++11 lambda表達式在回調(diào)函數(shù)中的使用方式
這篇文章主要介紹了C++11 lambda表達式在回調(diào)函數(shù)中的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11