网络专栏之网络API原理
TCP连接建立连接前的准备工作1.socket()socket()是应用程序与协议栈交互的抽象接口。如何实现的由两个部分组成一个是fd的分配有fd用户才能使用一个是tcp控制块TCBUDP连接没有TCB只有一个简单的UDP控制块记录IP和端口的分配当调用socket()创建 TCP socket 时内核会为这个 socket 创建一个 TCB 结构体隐藏在内核中用户程序无法直接访问。其中fd的分配是基于bitmap算法即0 1 2 ...n每一位都有0或1为值分配fd前会从0开始找找到某一位值为0则分配。TCB是管理状态连接的数据结构一个socket绑定唯一一个TCB。细致一点的话通过fd到进程私有的fd表中找到内核中的struct socket结构体再能找到TCB所有 socket 操作都是通过 fd 找到 TCB再由 TCB 完成实际的 TCP 协议逻辑。fd的作用和内核协议栈沟通的凭证不存储任何状态只作为一个索引唯一标识一个打开的socketTCB的作用属于内核态存储一个TCP连接的所有信息存储三次握手四次挥手的所有状态TCP状态机、连接的IPport负责TCP连接的核心操作三次握手、重传、拥塞控制2.bind() //客户端不用bind(fd,ip,port)。执行过程是通过fd找到对应的TCB然后把本地ip和本地port记录进去。因为建立连接会有一个五元组对应源ip,源port,目的ip,目的port,协议。主要是绑定本地portbind端口后方便listen监听端口。3.listen() //客户端不用listen(fd,number) listen的作用1.根据fd找到TCB 设置TCB的status为LISTEN否则后续无法三次握手建立连接2.为TCB初始化 半连接队列SYN queue 和 全连接队列accept_queue半连接队列的作用暂存未完成三次握手的连接收到SYN但未收到ACK全连接队列的作用暂存已完成握手但未被accept()取走的连接number的意义version1:表示半连接队列的长度。1980年使用偏多侧重安全防御避免syn泛洪version2表示全连接队列加半连接队列的长度。即未分配fd的TCB数量version3:表示全连接队列的长度。现代使用偏多因为可以扩大服务器连接性能syn泛红可以交给防火墙等监测设备。连接相关函数4.connect() //服务端不用connect(fd,addr,len(addr)) //这个addr是bind后的只能客户端调用调用后开始与服务端三次握手。服务端没有明确的开始三次握手的函数但是是由listen监听到第一次握手后将该连接加入到协议栈分配的 半连接队列SYN queue。且由accept在第三次握手后从全连接队列accept queue取走连接。而数据包半连接队列到全连接队列的过程是从第三次握手的数据包中提炼出 “五元”从而去半连接队列找到匹配的结点move到全连接队列中。5.accept() //客户端不用clientfd accept(fd,addr,len(addr)) //这个addr是空的三次握手完毕后accept为服务端和客户端创建一个独立的通信通道。而返回的clientfd也只是在服务端用来标识连接的客户端的id客户端有自己建立连接前创建的sockfd。accept任务分配一个clientfd将这个fd与对应的TCB做一个映射。传输数据6.send() 和 recv()上面提到的三次握手建立连接是在两台机器的内核协议栈中进行的对应用层代码来说是透明的只能通过监测协议栈中是否有数据到了来调用send或者recv函数处理。send()的具体工作就是不论客户端还是服务器将用户区的数据copy到内核协议栈的wmem中具体怎么发送到对方的内核协议栈的rmem由协议栈自动操纵。中间就是计算机网络的知识累计确认失序接收快速重传流量控制拥塞控制如果用户方发送了fin申请断开连接会在recv的buffer中写入EOF字节读到这个字节会返回0。断开连接7.close()close(fd)调用close后会释放文件描述符fd及其关联的内核资源如 Socket、TCB、缓冲区等会触发TCP的四次挥手但也是在两台主句的内核协议栈间交互代码层是透明的。正常四次挥手特殊情况1在fin_wait1状态下先收到了fin再发出ack那会进入一种新状态closing再收到ack进入time_wait特殊情况2两边同时close()小作业双机通信两个人都是服务器和客户端。代码展示另外一台的虚拟机修改ip即可#include stdio.h #include string.h #include stdlib.h #include sys/types.h #include sys/socket.h #include sys/epoll.h #include errno.h #include netinet/tcp.h #include arpa/inet.h #include netdb.h #include fcntl.h #include unistd.h #include pthread.h #define PORT 8888 int epfd; void *input_thread(void *arg) { int fd *(int *) arg; char buffer[1024]; int cnt; while (1) { memset(buffer, 0, 1024); if (fgets(buffer, sizeof(buffer), stdin) ! NULL) { // 去掉换行符 buffer[strcspn(buffer, \n)] \0; if (fd 0) { cnt send(fd, buffer, strlen(buffer), 0); // 利用client的conncetion发送 printf(Sent: %d bytes\n, cnt); } } } } int Add_fd_to_event(int fd, int event_type) { struct epoll_event ev; ev.data.fd fd; ev.events event_type; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev); } int initclient(int port) { int sockfd socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; memset(addr, 0, sizeof(struct sockaddr_in)); addr.sin_family AF_INET; addr.sin_addr.s_addr inet_addr(192.168.88.20); addr.sin_port htons(port); if (connect(sockfd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) 0) { perror(connect); } return sockfd; } int initserver(int port) { int sockfd socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serveraddr; serveraddr.sin_family AF_INET; serveraddr.sin_addr.s_addr htonl(INADDR_ANY); //0.0.0.0 serveraddr.sin_port htons(port); //1023 if (bind(sockfd, (struct sockaddr *)serveraddr, sizeof(struct sockaddr)) -1) { perror(bind); } listen(sockfd, 10); return sockfd; } void new_connection_come(int fd) { struct sockaddr_in clientaddr; socklen_t len sizeof(clientaddr); int clientfd accept(fd, (struct sockaddr *)clientaddr, len); if (clientfd 0) { printf(new connection disappear\n); return; } printf(accept clientfd : %d\n, clientfd); Add_fd_to_event(clientfd, EPOLLIN); } void show_what_I_received(int fd) { char buffer[1024]; memset(buffer, 0, sizeof(buffer)); int cnt recv(fd, buffer, 1024, 0); if (cnt 0) { printf(recv clientfd: %d disconnect\n, fd); close(fd); epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); return; } else if (cnt 0) { close(fd); epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); return; } printf(I have received : %s\n, buffer); } void send_message(int fd) { int cnt; char buffer[1024]; scanf(%s, buffer); cnt send(fd, buffer, strlen(buffer), 0); printf(I have sent %d bytes\n, cnt); } int main() { int sockfd2 initserver(PORT); sleep(5); int sockfd1 initclient(PORT); epfd epoll_create(1); Add_fd_to_event(sockfd1, EPOLLOUT); Add_fd_to_event(sockfd2, EPOLLIN); pthread_t tid; pthread_create(tid, NULL, input_thread, sockfd1); struct epoll_event evs[1024]; while (1) { memset(evs, 0, 1024); int nready epoll_wait(epfd, evs, 1024, -1); int i 0; for (i 0; i nready; i) { int fd evs[i].data.fd; if (evs[i].events EPOLLIN) { if (fd sockfd2) { new_connection_come(fd); } else { show_what_I_received(fd); } } } } }问题处理运行结果A主机B主机知识点参考https://github.com/0voice

相关新闻

好用帖子集合

好用帖子集合

labelImg下载与安装 labellmg中文版安装(最最简单版--适合小白),labellmg使用教程及labelimg闪退问题解决-CSDN博客 yolov8数据集配置 【目标检测】2024最新-用YOLOv8训练自己的数据集(保姆级教学)_yolov8 训练-CSDN博…

2026/7/4 1:43:18 阅读更多 →
Springboot+vue停车场管理系统的设计与实现

Springboot+vue停车场管理系统的设计与实现

文章目录前言源码获取详细视频演示具体实现截图后端框架SpringBoot前端框架Vue持久层框架MyBaits成功系统案例:代码参考数据库前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续…

2026/5/17 12:39:45 阅读更多 →
SpringBoot+Vue校园二手交易系统的设计与实现

SpringBoot+Vue校园二手交易系统的设计与实现

文章目录前言源码获取详细视频演示具体实现截图后端框架SpringBoot前端框架Vue持久层框架MyBaits成功系统案例:代码参考数据库前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续…

2026/5/17 12:39:46 阅读更多 →

最新新闻

VisualCppRedist AIO:一站式解决Windows软件兼容性问题的终极工具

VisualCppRedist AIO:一站式解决Windows软件兼容性问题的终极工具

VisualCppRedist AIO:一站式解决Windows软件兼容性问题的终极工具 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过软件无法启动、游…

2026/7/4 1:41:21 阅读更多 →
UE5多线程编程与FQueuedThreadPool实战指南

UE5多线程编程与FQueuedThreadPool实战指南

1. UE5多线程编程基础与FQueuedThreadPool概述在UE5游戏开发中,多线程编程是提升性能的关键技术之一。虚幻引擎提供了完善的多线程框架,其中FQueuedThreadPool作为核心线程池实现,为开发者管理并发任务提供了便利。与直接创建线程相比&#x…

2026/7/4 1:39:20 阅读更多 →
Unity Addressables内存管理优化实战指南

Unity Addressables内存管理优化实战指南

1. 内存管理在Addressables中的核心地位在Unity项目中使用Addressables资源管理系统时,内存管理是决定项目性能和稳定性的关键因素。不同于传统的Resources加载方式,Addressables采用异步加载和引用计数机制,这给内存管理带来了新的挑战和优化…

2026/7/4 1:37:19 阅读更多 →
FBX导入Unreal缺失平滑组问题的解决方案

FBX导入Unreal缺失平滑组问题的解决方案

1. 问题背景与现象解析最近在将FBX格式的3D模型导入Unreal Engine时,遇到了一个典型警告:"[ue SkeletalMesh] 在FBX文件中未找到这个网格体Mesh_001的平滑组信息"。这个看似简单的提示背后,实际上涉及到3D建模流程中几个关键的技术…

2026/7/4 1:37:19 阅读更多 →
Ubuntu下UE5与AirSim集成开发指南

Ubuntu下UE5与AirSim集成开发指南

1. 项目概述:Ubuntu系统下的UE5与Project AirSim集成方案在Linux生态中部署虚幻引擎5(UE5)与微软开源仿真平台Project AirSim的组合,为自动驾驶、无人机开发等领域提供了高性能的仿真测试环境。不同于Windows平台的"开箱即用…

2026/7/4 1:35:19 阅读更多 →
libgdx游戏UI元素定位与调试实战技巧

libgdx游戏UI元素定位与调试实战技巧

1. libgdx界面元素定位调试实战指南在libgdx游戏开发中,UI元素的精确定位是个看似简单却容易踩坑的环节。我刚接触libgdx时,曾花了两天时间就为了把一个按钮摆到理想位置。经过多个项目实战,我总结出三种不同维度的调试方案,从依赖…

2026/7/4 1:35:19 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻