linux C/C++ TCP网络通信实战

网友投稿 307 2022-08-28

linux C/C++ TCP网络通信实战

怎么样在Linux系统上通过TCP协议网络发送数据,首先先了解客户端和服务器工作的核心逻辑,如下图所示

各功能函数的介绍

socket

int socket(int domain, int type, int protocol)其中第一个参数:AF_LOCAL:表示的是本地地址,对应的是 Unix 套接字,这种情况一般用于本地 socket 通信,很多情况下也可以写成 AF_UNIX、AF_FILE;AF_INET:因特网使用的 IPv4 地址;AF_INET6:因特网使用的 IPv6 地址其中第二个参数:SOCK_STREAM: 表示的是字节流,对应 TCP;SOCK_DGRAM: 表示的是数据报,对应 UDP;SOCK_RAW: 表示的是原始套接字。其中第三个参数 :      protocol 原本是用来指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成。protocol 目前一般写成 0 即可。其返回套接字fd示例:int clientfd = socket(AF_INET,SOCK_STREAM,0);

/* 描述IPV4的套接字地址格式 */struct sockaddr_in{ sa_family_t sin_family; /* 16-bit */ in_port_t sin_port; /* 端口号 16-bit*/ struct in_addr sin_addr; /* Internet address. 32-bit */ /* 这里仅仅用作占位符,不做实际用处 */ unsigned char sin_zero[8];};//其中in_addr的类型为typedef struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un;} in_addr;//所以通常会有struct sockaddr_in name;name.sin_addr.s_addr = htonl (INADDR_ANY); // IPV4通配地址

bind

bind(int fd, void * addr, socklen_t len)

示例: struct sockaddr_in bindaddr; bindaddr.sin_family = AF_INET; //IPV4 bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定端口 bindaddr.sin_port = htons(SERVER_PORT); //通配地址 INADDR_ANY 表示通配地址,如果你只想该服务器程序仅本机上可以访问,那么你 bind 函数中的地址就可以使用127.0.0.1; 如果你的服务只想被局域网内部机器访问,bind 函数的地址可以使用192.168.1.104;如果 希望这个服务可以被公网访问,你就可以使用地址0.0.0.0或 INADDR_ANY bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr))

listen

int listen (int socketfd, int backlog)第一个参数 socketdf 为套接字描述符,第二个参数 backlog,在 Linux 中表示已完成 (ESTABLISHED) 且未 accept 的队列大小,(全连接队列的大小)这个参数的大小决定了可以接收的并发数目。这个参数越大,并发数目理论上也会越大。但是参数过大也会占用过多的系统资源

accept

int accept(int listensockfd, struct sockaddr *cliaddr, socklen_t *addrlen)返回一个已连接套接字,此时TCP 三次握手,操作系统内核就为这个客户生成一个已连接套接字,让应用服务器使用这个已连接套接字和客户进行通信处理,最后通信完,关闭的是这个已连接套接字,监听的套接字依旧在监听第一个参数 listensockfd 是套接字,可以叫它为 listen 套接字,因为这就是前面通过 bind,listen 一系列操作而得到的套接字

//示例: struct sockaddr_in clientaddr; socklen_t clientaddrlen = sizeof(clientaddr); //accept函数第三个参数需要的类型 //接受客户端的连接 int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen );

connect

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)第一个参数 sockfd 是连接套接字第二个、第三个参数 servaddr 和 addrlen 分别代表指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的 IP 地址和端口号 如果是 TCP 套接字,那么调用 connect 函数将激发 TCP 的三次握手过程,而且仅在连接建立成功或出错时才返回,其中出错返回可能有以下几种情况:三次握手无法建立,客户端发出的 SYN 包没有任何响应,于是返回 TIMEOUT 错误。这种情况比较常见的原因是对应的服务端 IP 写错。客户端收到了 RST(复位)回答,这时候客户端会立即返回 CONNECTION REFUSED 错误。这种情况比较常见于客户端发送连接请求时的请求端口写错,因为 RST 是 TCP 在发生错误时发送的一种 TCP 分节。产生 RST 的三个条件是:目的地为某端口的 SYN 到达,然而该端口上没有正在监听的服务器(如前所述);TCP 想取消一个已有连接;TCP 接收到一个根本不存在的连接上的分节。客户发出的 SYN 包在网络上引起了"destination unreachable",即目的不可达的错误。这种情况比较常见的原因是客户端和服务器端路由不通。

read/write recv/send

#include ssize_t read (int socketfd, void *buffer, size_t size)ssize_t write (int socketfd, const void *buffer, size_t size) ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);send和recv的前3个参数等同于read和write;flags参数值为0或:

TCP通信实战代码:

客户端代码:

默认向服务器端发送  hello world!

in_addr_t inet_addr(const char *cp);//inet_addr函数是将字符串转换网络主机地址(点分十进制)为网络字节序二进制值

#include#include#include#include#include#include#define SERVER_ADDRESS "127.0.0.1"#define SERVER_PORT 3000#define SEND_DATA "hello world!"#define recv_Buf_Size 32int main(int argc,char *argv[]){ //创建一个socket int clientfd=socket(AF_INET,SOCK_STREAM,0); if(clientfd == -1) { std::cout<<"create client fd error. "<< std::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) { std::cout << "connect socket error" << std::endl; return -1; } //向服务器端发送数据 int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0); if(ret != strlen(SEND_DATA)) { std::cout << "send data error." << std::endl; return -1; } std::cout << "send data successfully, data :" <0) { std::cout << "recv data successfully, data: " << recvBuf <

服务器端代码:

#include#include#include#include#include#include#include#define recvBuf_Size 32#define SERVER_PORT 3000int main(int argc,char *argv[]){ //创建一个监听socket int listenfd=socket(AF_INET,SOCK_STREAM,0); if(listenfd == -1) { std::cout<<"create client fd error. "<< std::endl; return -1; } //初始化服务器地址 struct sockaddr_in bindaddr; bindaddr.sin_family = AF_INET; bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); bindaddr.sin_port = htons(SERVER_PORT); if(bind(listenfd,(struct sockaddr *)&bindaddr,sizeof(bindaddr)) == -1) { std::cout << "bind listen socket error" << std::endl; return -1; } //启动监听 if(listen(listenfd,SOMAXCONN) == -1)//SOMAXCONN 表示监听队列数默认为128 { std::cout << "listen error." << std::endl; return -1; } //记录所有客户端连接的容器 std::vector clientfds; while(true) { struct sockaddr_in clientaddr; socklen_t clientaddrlen = sizeof(clientaddr); //accept函数第三个参数需要的类型 //接受客户端的连接 int clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen ); if(clientfd != -1 ) { char recvBuf[recvBuf_Size] ={0}; //从客户端接受数据 int ret = recv(clientfd, recvBuf, recvBuf_Size, 0); if(ret > 0) { std::cout <<"recv data from client, data:" <

测试结果显示:

客户端:

服务器端:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:元宇宙营销:品牌不得不面对的“四个转变”!
下一篇:极路由最新刷机教程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~