【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…...
微信聊天记录永久保存与智能分析:WeChatMsg完全使用指南
微信聊天记录永久保存与智能分析:WeChatMsg完全使用指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeC…...
FlowState Lab模型微调教程:使用自定义数据集训练专属波动模型
FlowState Lab模型微调教程:使用自定义数据集训练专属波动模型 1. 学习目标与前置准备 想为特定领域打造专属的波动预测模型吗?本文将带你完成从数据准备到模型评估的全流程。学完本教程,你将能够: 准备符合要求的时序/空间序列…...
PCB数据处理利器:从安装到实战的全方位指南
PCB数据处理利器:从安装到实战的全方位指南 【免费下载链接】pcb-tools Tools to work with PCB data (Gerber, Excellon, NC files) using Python. 项目地址: https://gitcode.com/gh_mirrors/pc/pcb-tools 1. 项目价值解析 PCB Tools作为一款专注于印制电…...
别再只用交叉熵了!深入对比YOLOv8中Focal Loss与CIoU Loss的改进效果与适用场景
深入解析YOLOv8损失函数优化:Focal Loss与CIoU Loss的实战对比与场景适配 当你在深夜调试YOLOv8模型时,是否遇到过这样的困境:明明增加了训练数据,小目标检测的准确率却始终上不去?或是发现模型对密集排列的物体总是漏…...
PyTorch 2.8镜像一文详解:CUDA 12.4兼容性、cuDNN版本匹配与驱动升级要点
PyTorch 2.8镜像一文详解:CUDA 12.4兼容性、cuDNN版本匹配与驱动升级要点 1. 镜像概述与核心特性 PyTorch 2.8深度学习镜像是一个专为高性能计算设计的优化环境,基于RTX 4090D 24GB显卡和CUDA 12.4深度调优。这个镜像解决了深度学习开发者经常遇到的环…...
Java后端开发——真实面试汇总(持续更新)
一.浙江大学研究院一面(面试Time:1小时30分钟)1. 面试官自我介绍,同时我开始自我介绍2. 平时接触到哪些数据结构?3. ArrayList和LinkedList的主要区别是什么?4. 数组和链表的主要区别是什么?5.…...
OpenClaw安全加固:Qwen3.5-9B操作权限的4层防护
OpenClaw安全加固:Qwen3.5-9B操作权限的4层防护 1. 为什么需要安全加固? 上周我在用OpenClaw自动处理一份包含客户联系方式的Excel表格时,突然意识到一个问题:如果AI助手误操作删除了关键文件怎么办?更可怕的是&…...
Qwen3.5-4B-Claude-Opus惊艳效果:编译原理词法分析器状态转换图生成
Qwen3.5-4B-Claude-Opus惊艳效果:编译原理词法分析器状态转换图生成 1. 模型能力展示:从代码到状态转换图 Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF模型在编译原理领域展现了令人惊艳的代码理解与可视化能力。当输入词法分析器代码时&…...
人血小板裂解液(hPL)与细胞治疗生产工具解析:Sexton产品应用综述【曼博生物官方代理Sexton】
摘要:人血小板裂解液(hPL)作为无动物源培养补充剂,正在逐步替代FBS应用于细胞与基因治疗(CGT)领域。本文结合相关产品体系,对hPL及细胞冻存与灌装系统进行系统梳理。 关键词:人血小板…...
华为光猫配置解密工具技术架构解析与实现机制
华为光猫配置解密工具技术架构解析与实现机制 【免费下载链接】HuaWei-Optical-Network-Terminal-Decoder 项目地址: https://gitcode.com/gh_mirrors/hu/HuaWei-Optical-Network-Terminal-Decoder 在网络设备运维领域,华为光猫配置文件的安全加密机制为设备…...
