当前位置: 首页 > web >正文

linux下的网络编程(2)

一、网络协议--UDP

      UDP位于传输层,全称为 transform  control   Protocol(传输控制协议/流式套接字)

1.1 TCP与UDP的比较

         

UDP的特点:
1. 面向数据包
2. 无连接
3. 尽最大努力交付,不安全不可靠(数据丢包、数据乱序)
4. 机制简单,资源开销小,数据实时性高
5. 可实现一对一、一对多的通信    

TCP的特点:

           1. 面向数据流
2. 有连接(通信之前必须建立连接)
3. 安全可靠的传输机制
4. 机制复杂,网络资源开销大
5. 本质只能实现一对一的通信(使用TCP并发方式可实现一对多通信)

 

1.2 TCP三次握手和四次挥手机制

           TCP三次握手:TCP建立连接时,需要进行三次握手,为了确保收发双方通信之前都已准备就绪。

ACK:响应报文标志;

SYN:请求建立连接标志位;

        三次握手:开始时,发送端向接收端发送SYN请求信号请求接收;接收端发送是哪个ACK应答信号表示接收到请求,同时发送SYN请求信号;发送端发送ACK应答信号,开始传输数据;

 

        TCP四次挥手:TCP断开连接时,需要进行四次挥手,确保断开连接前双方都已通信结束。

     

FIN:断开连接的标志;

        四次挥手:结束阶段,发送端发送FIN结束信号表示结束发送数据,接收端发送ACK应答表示接收到信号,接收端再次发送FIN和ACK信号,表示请求中止接受和应答信号,最后发送端发送ACK应答信号,传输结束。

 

1.3 TCP的编程流程

 

 int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:请求与服务端建立连接
参数:
sockfd:套接字
addr:要连接的服务端的地址信息
addrlen:服务端地址大小
返回值:
成功:0
失败:-1

 ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送网络数据
参数:
sockfd:网络套接字
buf:要发送的数据首地址
len:发送的字节数
flags:0 :按照默认方式发送
返回值:
成功:实际发送的字节数
失败:-1

int listen(int sockfd, int backlog);
功能:监听建立三次握手的客户端
参数:
sockfd:监听套接字
backlog:最大允许监听的客户端个数
返回值:
成功:0
失败:-1

int accept(int socket, struct sockaddr *restrict address,
socklen_t *restrict address_len);
功能:接收建立三次握手的客户端,并产生一个通讯套接字
参数:
socket:监听套接字
address:客户端的地址信息
address_len:客户端地址长的指针
返回值:
成功:通讯套接字
失败:-1


ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:从网络套接字上接收数据
参数:
sockfd:通讯套接字
buf:存放接收数据的首地址
len:期望接收到的字节数
flag : 0:默认方式接收(阻塞)
返回值:
成功:实际接收到的字节数
失败:-1
对方断开连接:0

实例:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h> 
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>int main(void)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.190");int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}ret = listen(sockfd,10);if(ret < 0){perror("listen error");return -1;}int connfd = accept (sockfd,NULL,NULL);if(connfd < 0){perror("accept error");return -1;}char buff[1024] = {0};ssize_t cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){printf("recv error");return -1;}printf("cnt = %ld,buff = %s\n", cnt, buff);cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){printf("recv error");return -1;}printf("cnt = %ld,buff = %s\n", cnt, buff);close(connfd);close(sockfd);return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h> 
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>int main(void)
{int sockfd = socket(AF_INET, SOCK_STREAM,0);if(sockfd < 0){perror("sockst error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.190");int ret = connect(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("connect error");return -1;}ssize_t cnt = send(sockfd,"hello world",11,0);if(cnt < 0){perror("send error");return -1;}printf("cnt = %ld\n",cnt);cnt = send(sockfd,"how are you",11,0);if(cnt < 0){perror("send error");return -1;}printf("cnt = %ld\n",cnt);close(sockfd);return 0;
}

1.4 TCP粘包问题

TCP粘包问题:发送方应用层发送的多包数据,将来在接收方可能一次读到,多包数据产生了粘连。

      原因:
1. 发送方速度较快,TCP底层可能对多包数据进行重新组帧;
2. 接收方数据处理速度较慢,导致多包数据在接收缓冲区缓存,应用层读时,一次将多包数据读出。

     
解决粘包问题的常用方法:

1.  调整发送速率
2.  发送指定大小,将来接收方也接受指定大小。
结构体
注意:
1. 跨平台之间的数据传输时,注意结构体对齐问题。
struct a
{
char a;
int b;
long c;
};
32bits平台《--》64位平台

         3. 应用层位发送的数据增加分隔符,利用分隔符解析
hello world\nhow are you\n

         4. 封装自定义数据帧格式进行发送(协议),严格根据协议进行解析。

AA  C0  00 00 00 F0 00 BB 10 A0  00 00 00 10 校验 BB  AA  C0  00 00 00 F0 00 BB 10 A0  00 00 00 10 校验 BB AA  C0  00 00 00 F0 00 BB 10 A0  00 00 00 10 校验 BB

帧头:AA
帧尾:BB
有效数据长度:C0
有效数据:00 00 00 F0 00 BB 10 A0  00 00 00 10
校验:
8位和校验
16位和校验
CRC校验

1.5  TCP的其他机制

     TCP头部的标志位:

     SYN:请求建立连接标志位
ACK:响应报文标志位
PSH:携带数据标志位,通知接收方该从缓冲区读数据
FIN: 请求断开连接标志位
RST:复位标志位
URG: 紧急数据标志位

  机制:

        1.三次握手,四次挥手;

        2.应答机制:TCP对于每一包数据都会给出相应的应答。发送数据时序列号表示这包数据的起始编号,响应报文中的确认号是接受方收到的最后一个字节编号+1;

        3.超时重传机制:当数据发送出去等待指定时间没有收到响应,此时认为这包数据丢失,则进行重传;

        4.滑动窗口机制:一段缓冲区,缓存TCP已发送未收到响应,准备发送等数据,确保重传时找到数据

提高效率的机制:

延迟应答机制:发送数据时同时等待应答;

流量控制机制:结合TCP头部的窗口大小,动态调整发送速率

捎带应答机制:ACK报文可能和应用层的数据同时发送;

 

二、HTTP协议

 

WWW:万维网

问题:

1.万维网服务器后台如何标记万维网数据:URL;

2.万维网客户端与万维网服务器之间使用什么方式通信(HTTP:超文本传输协议);

        超文本:文字、链接、音频、视频集合一体的文本;

3.万维网客户端如何展示请求的数据(HTML:超文本标记语言);

 

 

URL :统一资源定位符

 

https://www.baidu.com     端口号可省略

 

HTTP:超文本传输协议,位于应用层,基于传输层的TCP协议

        端口:80

        备用端口:8080

 

(1)HTTP通信过程

客户端建立与服务端的TCP链接,再发送HTTP请求报文,服务端发送HTTP回应报文,任意一方均可发送断开TCP连接。

 

长连接:发完回应报文会连续一定时间,不会立刻断开  keep--->alive

短链接:发完立刻断开    close

(2)HTTP的报文格式

GET/ HTTO/1.1\r\n   GET 后面的/是主页

 

 

请求报文:

响应报文:

 

 

三、TCP的并发

        单循环服务器:服务端同一时刻只能处理一个客户端的任务

        并发服务器:服务端同一时刻可以处理多个客户端的任务

        并发模型:

        (1)多进程

                进程资源开销大;安全性高;

实例:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h> 
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>
#include <pthread.h>int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM,0);if(sockfd < 0){perror("sockst error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.190");int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}ret = listen(sockfd,10);if(ret < 0){perror("listen error");return -1;}return sockfd;}int main(void)
{struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);int sockfd = init_tcp_ser();if(sockfd < 0){return -1;}  while(1){int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clilen);if(connfd < 0){perror("accept error");return -1;}pid_t pid = fork();if(pid > 0){}else if(0 == pid){char buff[1024] = {0};while(1){memset(buff,0,sizeof(buff));size_t cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");break;}else if(0 == cnt){printf("[%s : %d] : offline\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));break;}printf("[%s : %d] : %s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);strcat(buff,"----ok");cnt = send(connfd,buff,strlen(buff),0);if(cnt < 0){perror("send error");break;}}close(connfd);}} close(sockfd);return 0;
}

        (2)多线程

                资源开销小;相同资源环境下,并发量比进程大

实例:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h> 
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>
#include <pthread.h>
#include<stdlib.h>struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);int init_tcp_ser()
{int sockfd = socket(AF_INET, SOCK_STREAM,0);if(sockfd < 0){perror("sockst error");return -1;}int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));//避免被占用struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.190");int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}ret = listen(sockfd,10);if(ret < 0){perror("listen error");return -1;}return sockfd;}void *send_msg(void *arg)
{int connfd = *((int *)arg);char buff[1024] = {0};while(1){memset(buff,0,sizeof(buff));size_t cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");break;}else if(0 == cnt){printf("[%s : %d] : offline\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));break;}printf("[%s : %d] : %s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);strcat(buff,"----ok");cnt = send(connfd,buff,strlen(buff),0);if(cnt < 0){perror("send error");break;}}close(connfd);}int main(void)
{pthread_t tid;int sockfd = init_tcp_ser();if(sockfd < 0){return -1;}  while(1){int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clilen);if(connfd < 0){perror("accept error");return -1;}pthread_create(&tid, NULL, send_msg, &connfd);pthread_detach(tid);}close(sockfd);return 0;
}

        (3)线程池

                为了解决多线程或者多进程模型,在服务器运行过程中,频繁创建和销毁线程(进程)bi带来的时间消耗问题;

                基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程模型;

        (4)IO多路复用

                I--->O:fd

                对多个文件描述符的读写可以复用一个进程。

                在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时检测;

                fgets(stdin);

                recv(connfd);

               阻塞IO模式:

                        1.多个任务之间是同步的效果

阻塞IO实例:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h> 
#include<arpa/inet.h>
#include<string.h>
#include<sys/wait.h>
#include <pthread.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/select.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, const char *argv[])
{char buff[1024] = {0};int sockfd = socket(AF_INET, SOCK_STREAM,0);if(sockfd < 0){perror("socket error");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.190");int ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));if(ret < 0){perror("bind error");return -1;}ret = listen(sockfd,100);if(ret < 0){perror("listen error");return -1;}fd_set rdfds;fd_set rdfdstmp;FD_ZERO(&rdfds);FD_SET(sockfd,&rdfds);int maxfd = sockfd;struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);while(1){rdfdstmp = rdfds;int cnt = select(maxfd + 1, &rdfdstmp, NULL, NULL, NULL);if (cnt < 0){perror("select error");return -1;}if(FD_ISSET(sockfd,&rdfdstmp)){int connfd = accept(sockfd, (struct sockaddr *)&cliaddr,&clilen);if(connfd < 0){perror("accept error");return -1;}FD_SET(connfd,&rdfds);maxfd = maxfd > connfd ? maxfd : connfd;}for(int i = sockfd + 1;i <= maxfd; i++){if(FD_ISSET(i,&rdfdstmp)){memset(buff,0,sizeof(buff));ssize_t cnt = recv(i,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");FD_CLR(i,&rdfds);close(i);continue;}else if(0 == cnt){FD_CLR(i,&rdfds);close(i);continue;}printf("%s\n",buff);strcat(buff,"----ok");cnt = send(i,buff,strlen(buff),0);if(cnt < 0){	perror("send error");FD_CLR(i,&rdfds);close(i);continue;}}}}close(sockfd);return 0;
}

 

 

1)select实现IO多路复用:

        1.创建文件描述符集合;    fd_set

        2.添加关注的文件描述符到集合;   FD_SET()

        3.使用select传递集合表给内核,内核开始检测事件;   select()

        4.当内核检测到事件时,应用层select将解除阻塞,并获得相关的事件结果;

        5.根据select返回的结果做不同的处理;

 

       void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

 

 int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

功能:传递文件描述符给内核并等待获取事件结果;

参数:

        nfds:关注的最大文件描述符+1;

        readfds:读事件关注的文件描述符集合;

        writefds:写事件的文件描述符集合;

        timeout:设置select检测时的超时时间;

                        NULL:不设置超时时间(select一直阻塞等待)

返回值:

        成功:返回内核监测的到达事件的个数;

        失败:-1

        0:超时时间到达但没有事件发生,返回0;

 

 

http://www.xdnf.cn/news/19022.html

相关文章:

  • 智能体协作体系核心逻辑:Prompt、Agent、Function Calling 与 MCP 解析
  • AV1到达开始和约束时间
  • 分治法——二分答案
  • XFile v2 系统架构文档
  • Ansible 核心模块与实操练习
  • 第十三章项目资源管理--13.3 规划资源管理
  • Apifox 8 月更新|新增测试用例、支持自定义请求示例代码、提升导入/导出 OpenAPI/Swagger 数据的兼容性
  • 手写MyBatis第37弹: 深入MyBatis MapperProxy:揭秘SQL命令类型与动态方法调用的完美适配
  • AI赋能前端性能优化:核心技术与实战策略
  • Swift 解法详解 LeetCode 364:嵌套列表加权和 II
  • 713 乘积小于k的子数组
  • git学习 分支管理(branching)合并分支
  • golang13 单元测试
  • Office 2024 长期支持版(Mac中文)Word、Execl、PPT
  • Node.js 多版本管理工具 nvm 的安装与使用教程(含镜像加速与常见坑)
  • 共识算法如何保障网络安全
  • Java全栈开发面试实战:从基础到微服务的深度探索
  • k8s集群Prometheus部署
  • 1 vs 10000:如何用AI智能体与自动化系统,重构传统销售客户管理上限?
  • Wi-Fi数据包发送机制:从物理层到MAC层的深度解析
  • 记录使用ruoyi-flowable开发部署中出现的问题以及解决方法(二)
  • 贴片式TE卡 +北京君正+Rk瑞芯微的应用
  • 直线拟合方法全景解析:最小二乘、正交回归与 RANSAC
  • Transformer实战(15)——使用PyTorch微调Transformer语言模型
  • 了解迁移学习吗?大模型中是怎么运用迁移学习的?
  • 达梦数据库配置文件-COMPATIBLE_MODE
  • 数据结构青铜到王者第七话---队列(Queue)
  • 《websocketpp使用指北》
  • ModuleNotFoundError: No module named ‘dbgpt_app‘
  • Python音频分析与线性回归:探索声音中的数学之美