C++?TCP網(wǎng)絡編程詳細講解
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);
//關閉套接字
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關閉所有套接字
#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)關閉
{
//關閉與客戶端連接的套接字
close(cfd);
break;
}
//應答客戶端
send(cfd, buf, len, 0);
}
}
}
//關閉監(jiān)聽套接字
close(sockfd);
return 0;
}
到此這篇關于C++ TCP網(wǎng)絡編程詳細講解的文章就介紹到這了,更多相關C++ TCP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用?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-11
C++11 lambda表達式在回調(diào)函數(shù)中的使用方式
這篇文章主要介紹了C++11 lambda表達式在回調(diào)函數(shù)中的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11

