【Linux Day15 TCP网络通讯】
TCP网络通讯
TCP编程流程

接口介绍
-
socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。创建套接字时要指定使用的服务类型,使用 TCP 协议选择流式服务(SOCK_STREAM)。
-
**bind()方法是用来指定套接字使用的 IP 地址和端口。**IP 地址就是自己主机的地址,测试程序时可以使用回环地址“127.0.0.1”。端口是一个 16 位的整形值,一般 0-1024 为知名端口,如 HTTP 使用的 80 号端口。这类端口一般用户不能随便使用。其次,1024-4096 为保留端口,用户一般也不使用。4096 以上为临时端口,用户可以使用。在Linux 上,1024 以内的端口号,只有 root 用户可以使用。
-
**listen()方法是用来创建监听队列。**监听队列有两种,一个是存放未完成三次握手的连接,一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。

-
accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则accept()返回该连接对应的套接字描述符。如果该队列为空,则 accept 阻塞。
-
connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。该方法执行后,会进行三次握手, 建立连接。
-
send()方法用来向 TCP 连接的对端发送数据。send()执行成功,只能说明将数据成功写入到发送端的发送缓冲区中,并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送缓冲区中的数据长度。
-
recv()方法用来接收 TCP 连接的对端发送来的数据。recv()从本端的接收缓冲区中读取数据,如果接收缓冲区中没有数据,则 recv()方法会阻塞;返回值是实际读到的字节数,如果recv()返回值为 0, 说明对方已经关闭了 TCP 连接。
-
close()方法用来关闭 TCP 连接。此时,会进行四次挥手。
客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);assert(sockfd != -1);struct sockaddr_in saddr;memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (-1 == res){ exit(1);}while (1){char buff[128] = {0};printf("input:\n");fgets(buff, 128, stdin);if (strncmp(buff, "end", 3) == 0){break;}send(sockfd, buff, strlen(buff), 0);memset(buff, 0, 128);recv(sockfd, buff, 127, 0);printf("buff=%s\n", buff);}close(sockfd);exit(0);
}
服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == sockfd){exit(1);}struct sockaddr_in saddr;memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000); // htons 将主机字节序转换为网络字节saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 回环地址int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (-1 == res){exit(1);}res = listen(sockfd, 5);if (-1 == res){exit(1);}struct sockaddr_in caddr;socklen_t len = sizeof(caddr);int n = 0;int c = -1;while (1) // 服务器循环接收客户端连接{char data[128] = {0};if (n == 0){c = accept(sockfd, (struct sockaddr *)&caddr, &len); // 阻塞if (c == -1){printf("accept error ");continue;;}}n = recv(c, data, 127, 0); // 阻塞if (n == 0) //连接关闭{close(c);printf("client close\n");continue;}else if (n < 0) //出错{printf("recv error");continue;}printf("n = %d, buff = %s\n", n, data);send(c, "OK", 2, 0);}close(sockfd); exit(0);
}
运行结果:

引入多线程处理并发
服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>void *run(void *arg)
{int c = (int)arg;while (1){char buff[128] = {0};if (recv(c, buff, 127, 0) <= 0){break;}printf("recv(%d)=%s", c, buff);send(c, "ok", 2, 0);}printf("one client over(%d)\n", c);close(c);
}int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){exit(1);}struct sockaddr_in saddr, caddr;memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (-1 == res){exit(1);}listen(sockfd, 10);while (1){int len = sizeof(caddr);int c = accept(sockfd, (struct sockaddr *)&caddr, &len);if (c < 0){continue;}printf("accept c = %d\n", c);pthread_t id;pthread_create(&id, NULL, run, (void *)c);}close(sockfd);exit(0);
}
运行结果:

引入fork处理并发
服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>void DealClientLink(int c, struct sockaddr_in caddr)
{while (1){char buff[128] = {0};int n = recv(c, buff, 127, 0);if (n <= 0){break;}printf("%s:%d %s", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buff);send(c, "OK", 2, 0);}printf("One Client Close\n");close(c);
}void Signal_Fun(int sign)
{wait(NULL);
}
int main()
{signal(SIGCHLD, Signal_Fun); // 用wait()处理僵死进程int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == sockfd){printf("create sockfd error\n");exit(1);}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));assert(-1 != res);listen(sockfd, 10);while (1){struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd, (struct sockaddr *)&caddr, &len);assert(-1 != c);printf("%s:%d Link Success\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));pid_t pid = fork();if (-1 == pid){exit(1);}if (0 == pid){DealClientLink(c,caddr);exit(0); //必须结束子进程,否则会有多个进程调 accept}else{close(c); //父子进程都需要关闭 c}}close(sockfd);exit(0);
}
运行结果: 
TCP连接状态转变图

三次握手
- 流程图

-
使用netstat工具查看状态变化(参考图3-8)

四次挥手
-
流程图

-
使用netstat命令查看状态(参考图3-8)

TIME_WAIT的作用
在图3-8中,当客户端连接在收到服务器的结束报文段之后,并没有直接进人CLOSED 状态,而是转移到 TIME_WAIT 状态。在这个状态,客户端连接要等待段长为2MSL(Maximum Segment Life,报文段最大生存时间)的时间,才能完全关闭;MSL是 TCP 报文段在网络中的最大生存时间,标准文档 RFC 1122 的建议值是2 min;
TIME WAIT 状态存在的原因有两点:
-
可靠地终止TCP 连接
当服务器发给客户端的ACK中途丢失,客户端收不到ACK,会重新发送FIN,如果此时服务器已经关闭,无法接收来自客户端的FIN,便会陷入一种“藕断丝连”状态(一方关闭,一方未关闭)。这显然是不合适的,因为TCP 连接是全双工的,双方完成数据交换之后,通信双方都必须断开连接以释放系统资源。
-
保证让迟来的TCP 报文段有足够的时间被识别并丢弃
在 Linux 系统上,一个TCP 端口不能被同时打开多次(两次及以上)。当一个TCP 连接处于 TIME_WAIT 状态时,我们将无法立即使用该连接占用着的端口来建立一个新连接。反过来,如果不存在 TIME WAIT 态,则应用序能够立即建立一个和刚关闭的连接相似的连接(这里说的相似,是指它们具有相同的 IP 地址和端口号)。这个新的、和原来相似的连接被称为原来的连接的化身 (incarmation)。新的化身可能接收到属于原来的连接的、携带应用程序数据的 TCP 报文段(迟到的报文段),这显然是不应该发生的。这就是 TIMEWAIT 状态存在的第二个原因。
TCP协议特点
流式服务
TCP 字节流的特点,发送端执行的写操作次数和接收端执行的读操作次数之间没有任何数量关系,应用程序对数据的发送和接收是没有边界限制的。如下图:

TCP连接的可靠性
- IPV4报文格式:

- TCP报文格式:

- 应答机制

- 超时重传

TCP 传输是可靠的。首先,TCP 协议采用发送应答机制,即发送端发送的每个 TCP 报文段都必须得到接收方的应答,才认为这个 TCP 报文段传输成功。其次,TCP 协议采用超时重传机制,发送端在发送出1个 TCP 报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该报文段。最后,因为 TCP 报文段最终是以 IP数据报发送的,而 数据报到达接收端可能乱序、重复,所以 TCP 协议还会对接收到的 TCP 报文段重排、整理,再交付给应用层。
粘包问题
在流式服务中如上图3-9所示,尽管报文已经按顺序整理好并接受,但是无法分割成正确的信息,就形成了所谓的粘包问题,为了解决此问题,我们可以每次发送时进行标记分割,以便于接收方进行分析和拆分,如下图:

相关文章:
【Linux Day15 TCP网络通讯】
TCP网络通讯 TCP编程流程 接口介绍 socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。创建套接字时要指定使用的服务类型,使用 TCP 协议选择流式服务(SOCK_STREAM)。 **bind()方法是用来指定套接字使…...
力扣:78. 子集
回溯解法思路: 1.跟前面的组合题目有相同的点,主要区别在于:组合题目是遍历到符合条件的组合时加入li1集合中,子集题目是每递归一次就要把结果加入到li1集合中,并遍历但nums数组的最后。其他点和组合问题一样。 clas…...
(29)数组异或操作
文章目录 每日一言题目解题思路方法一方法二 代码方法一方法二 结语 每日一言 泉涸,鱼相与处于陆,相呴以湿,相濡以沫,不如相忘于江湖。 --庄子内篇大宗师 题目 题目链接:数组异或操作 给你两个整数,n 和…...
mac检查CPU温度和风扇速度软件:Macs Fan Control Pro 1.5.17中文版
Macs Fan Control Pro for Mac是一款专业的电脑风扇控制工具,旨在帮助Mac用户有效控制电脑的风扇速度,提高电脑的运行效率和稳定性。 软件下载:Macs Fan Control Pro 1.5.17中文版 该软件支持多种风扇控制模式和预设方案,用户可以…...
数据结构——单链表详解
目录 前言 一.什么是链表 1.概念 编辑 2.分类 二.单链表的实现(不带头单向不循环链表) 2.1初始化 2.2打印 2.3创建新节点 2.4头插、尾插 2.5头删、尾删 2.6查找 2.7在指定位置之前插入 2.8在指定位置之后插入 2.9删除pos位置 2.10删除pos之后的 2.11销毁链表…...
Unity接入GVoice腾讯实时语音
Unity接入GVoice腾讯实时语音 一、介绍二、注册GVoice创建项目语音服务1.创建项目2.申请语音权限3.项目管理查看SDK初始化的一些参数和基本信息4.GVoice检测 三、SDK下载SDK是分为两种类型:独立版集成板 SDK放入Unity工程中 四、语音代码写法五、GVoice踩坑语音权限…...
【Spring基础】从0开始学习Spring(2)
前言 在上篇文章,我已经讲了Spring中最核心的知识点:IoC(控制反转)以及DI(依赖注入)。这篇文章,我将讲一下关于Spring框架中的其它比较琐碎但是又还是挺重要的知识点,因此ÿ…...
cesium mapboxgl+threebox glb 朝向问题
一、3Dbuilder打开glb 二、cesium在pitch和heading都为0的情况下,不设置模型的朝向 三、mapboxglthreebox在pitch和bearing都为0的情况下,不设置模型的朝向 四、对于地图默认视角,cesium设置pitch-90、heading0的时候和mapboxglthreebox设置p…...
LeetCode 打家劫舍
198. 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个…...
单片机的50个电路
单片机 电源 声音模块 收音机 485 蓝牙 光耦 can 光敏电阻 单片机 矩阵 单片机电路 时钟 ADC 接口电路 红外发射 显示模块 红外接收 蜂鸣器驱动 流水灯 usb供电 烧录电路 数码管 EEPROM LCD1602电路 数码管 max485 红外开关 译码器 移位寄存器 步进电机控制 复位电路 下载电路 …...
JVM 性能调优- 五种内存溢出(5)
在介绍之前先简单介绍下 直接内存(Direct Memory)和堆内存(Heap Memory): 关系: 直接内存并不是Java虚拟机的一部分,它是通过Java的NIO库中的ByteBuffer来分配和管理的。直接内存通常由操作系统的本地内存(Native Memory)提供支持。堆内存是Java虚拟机的一部分,用于存…...
【SQL高频基础】1141.查询近30天活跃用户数
题目: 表:Activity ------------------------ | Column Name | Type | ------------------------ | user_id | int | | session_id | int | | activity_date | date | | activity_type | enum | ------------------------…...
基于spring cloud alibaba的微服务平台架构规划
平台基础能力规划(继续完善更新…) 一、统一网关服务(独立服务) 二、统一登录鉴权系统管理(独立服务) 1.统一登录 2.统一鉴权 3.身份管理 用户管理 角色管理 业务系统和菜单管理 部门管理 岗位管理 字典管…...
leetcode(滑动窗口)3.无重复字符的最长字串(C++详细题解)DAY2
文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示…...
Android13 系统源码适配安装可卸载的三方apk应用
Android13 系统源码适配安装可卸载的三方apk应用 文章目录 Android13 系统源码适配安装可卸载的三方apk应用一、前言二、Android 系统运行后默认安装三方apk实现1、Android 系统默认安装三方apk实现主要思路2、Android 系统默认安装三方apk具体实现(1)准…...
flutter使用qr_code_scanner扫描二维码
qr_code_scanner仓库地址:qr_code_scanner | Flutter Package 需要添加android和ios的相机权限和本地相册权限: android中添加权限: 在android\app\build.gradle中修改:minSdkVersion 20 并且在android/app/src/main/AndroidManifest.xml中…...
黑马Java——集合进阶(List、Set、泛型、树)
一、集合的体系结构 1、单列集合(Collection) 二、Collection集合 1、Collection常见方法 1.1代码实现: import java.util.ArrayList; import java.util.Collection;public class A01_CollectionDemo1 {public static void main(String[] a…...
TS项目实战二:网页计算器
使用ts实现网页计算器工具,实现计算器相关功能,使用tsify进行项目编译,引入Browserify实现web界面中直接使用模块加载服务。 源码下载:点击下载 讲解视频 TS实战项目四:计算器项目创建 TS实战项目五:B…...
MySQL的ACID、死锁、MVCC问题
1 ACID ACID代表原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个确保数据安全的事务处理系统,必须满足这些密切相关的标准。 原…...
Docker 可视化工具
1、Portainer 概念介绍 Portainer是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。 Portainer分为开源社区版(CE版)和商用版(BE版/EE版)。 Porta…...
ChatBI 开源产品实战解析:从语义层到Agent,如何选择你的AI数据助手?
1. 为什么企业需要AI数据助手? 想象一下这个场景:市场部的小王需要统计上季度各区域的销售数据,他对着Excel表格里密密麻麻的数字发愁,不得不找IT部门帮忙写SQL查询。三天后拿到数据时,业务窗口期已经错过——这是很多…...
FedMeta: Accelerating Federated Learning with Meta-Learning for Enhanced Privacy and Efficiency
1. FedMeta:当联邦学习遇上元学习 想象一下,你正在训练一个能识别手写数字的AI模型,但数据分散在成千上万个用户的手机里。传统联邦学习就像让每个用户都从头开始训练完整模型,既耗流量又费时间。而FedMeta的聪明之处在于——它让…...
zotero-style:提升文献管理效率的3个核心方案
zotero-style:提升文献管理效率的3个核心方案 【免费下载链接】zotero-style zotero-style - 一个 Zotero 插件,提供了一系列功能来增强 Zotero 的用户体验,如阅读进度可视化和标签管理,适合研究人员和学者。 项目地址: https:/…...
使用PyTorch Lightning优化PETRV2-BEV模型训练流程
使用PyTorch Lightning优化PETRV2-BEV模型训练流程 如果你正在训练像PETRV2这样的BEV感知模型,可能已经体会过那种“一步一坑”的感觉。数据加载复杂、多GPU训练配置繁琐、日志记录混乱、实验难以复现……这些工程上的琐事,常常比模型本身更让人头疼。 …...
别再纠结模型了!用Python+Simulink快速搭建四旋翼无人机仿真(附完整代码)
用PythonSimulink快速搭建四旋翼无人机仿真实战指南 四旋翼无人机开发中最令人头疼的环节,往往不是控制算法设计,而是如何快速搭建一个可靠的仿真环境。我曾见过不少团队在模型选择上耗费数周时间,最终却陷入理论完美主义陷阱——他们反复纠结…...
课堂教学质量综合评分系统
目录 一、项目环境与目录结构 1. 环境要求 2. 推荐目录结构 二、核心类设计:ClassroomScorer 三、关键代码深度解析 1. 基础路径配置 2. 初始化方法:极致灵活的配置 3. 上下文管理器:统一封装 CSV 读取 4. 数据加载:4 类 …...
Repomix用户体验:CLI界面设计与交互的终极指南
Repomix用户体验:CLI界面设计与交互的终极指南 【免费下载链接】repomix 📦 Repomix (formerly Repopack) is a powerful tool that packs your entire repository into a single, AI-friendly file. Perfect for when you need to feed your codebase t…...
SpringBoot+Vue社区老年人帮扶系统源码+论文
代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 分享万套开题报告任务书答辩PPT模板 作者完整代码目录供你选择: 《SpringBoot网站项目》1800套 《SSM网站项目》1500套 《小程序项目》1600套 《APP项目》1500套 《Python网站项目》…...
Connect to Oracle Database with JDBC Driver
1. Overview The Oracle Database is one of the most popular relational databases. In this tutorial, we’ll learn how to connect to an Oracle Database using a JDBC Driver. 2. The Database To get us started, we need a database. If we don’t have access to …...
HarmonyOS6 ArkTS List 设置编辑模式
文章目录一、功能概述二、官方核心知识点1. 编辑模式实现原理2. 列表数据驱动3. 列表项操作三、完整可运行代码四、代码功能详解1. 编辑模式状态控制2. 编辑按钮切换3. 列表项动态显示删除按钮4. 删除列表项5. LazyForEach 高性能渲染五、运行效果总结一、功能概述 List 编辑模…...
