【计算机网络】TCP实战
其实有了UDP的基础,TCP不管怎么说学习起来都还是比较舒服的,至少是比直接就学习TCP的感觉好。
这篇文章最多就是介绍一下起手式,如果想带业务的话和UDP那篇是完全一样的,就不进行演示了。
总的来说还是很简单的。
目录
- Echo
- 服务端起手式
- 服务端LOOP
- 客户端起手
- 客户端LOOP
- 验证
- 改进
- 方案一
- 方案二
- 验证
Echo
我们还是从最简单的不带业务的Echo开始。
服务端起手式
服务器起手式
首先要说明的一点是,TCP是面向字节流,有连接。
而UDP创建好套接字后不管连接,直接recvfrom,sendto就可以发送。
在TCP编码中会体现出有连接的特点,面向字节流会在理论中提及,代码中实践。
创建套接字
首先与UDP不同的是,UDP的第二个参数是SOCK_DGRAM。
_listenfd = ::socket(AF_INET, SOCK_STREAM, 0);
关于这两个参数不同含义更详细的放在下图中了。

另外,这次使用listenfd接收,以前我们获得的就是sockfd,直接使用这个描述符进行收发,那么叫listenfd肯定是有特别用意。
进行bind
bind没什么好说的,仍旧是要注意填参问题。
int n = ::bind(_listenfd, (struct sockaddr *)&local, len);
进入listen状态

从这里就可以看到面向连接的痕迹了。
那么什么叫listen状态?
我们举一个小例子:
UDP就是无人售货店,不许用老板看店即可买东西。
但是TCP就是有人售货店,需要老板在才可以买东西,而TCP就是那个有人售货店,listen就是设置为老板模式,这样就可以接客了。
第一个参数就是你得到的listenfd。
第二个参数是连接队列长度,这个值我们暂时先不用管,一般默认设置为4, 8, 16即可,不需要太大。
n = ::listen(_listenfd, gbacklog);
服务端LOOP
accept
来了新的连接我们就要去accept了
换句话说就是在有人售货店中现在如果来客人了老板就需要去接客。
int sockfd = ::accept(_listenfd, (sockaddr *)&peer, &len);
参数是输出型参数,来一个连接就可以获得这个连接的基本信息。
但是要注意填参时一定要正确填参,虽然addrlen是一个输出型参数,但仍然需要正确初始化,不能不初始化。
我就犯了这个错误,导致有时错误(accept或者connect错误,connect在客户端会提到),甚至搞的我以为这是tcp特性…
但是我们注意到返回值是一个文件描述符!?那么他是什么?与listenfd有何区别?
我们举一个例子进行理解:

上图可以形象的帮助理解返回值。
其中饭店 = 服务器
客户 = 新连接
揽客员 = listenfd(socket返回值)
服务员 = sockfd (accept返回值)
listenfd不直接提供服务,sockfd才直接提供服务!
所以在TCP服务端我们一般将socket返回值叫做监听套接字。
没有连接时就会陷入阻塞状态,accept失败返回-1(这里可以理解为揽客员都把路人拉到店里面,但是路人又临时有事离开了,导致拉客失败~)。
下段代码是TCP的框架代码,与UDP有很大的不同。
void Loop()
{_isrunning = true;while (_isrunning){sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = ::accept(_listenfd, (sockaddr *)&peer, &len);if (sockfd < 0){continue;}// version 0 :提供长服务。service(sockfd);}_isrunning = false;
}
与UDP有很大的不同:
每一次循环都对应着一个新连接,我们随后要对这个新连接进行长服务;
而UDP是不管来的是谁,都统一处理。
读写操作
我们在长服务中进行读写操作。
因为我们是面向字节流,而管道、文件也都是流。
且Linux下一切皆文件,管道、文件都是文件,所以都是可以用文件操作read,write进行读写,所以这里也可以使用文件操作!
void service(int sockfd)
{// 长服务while (true){char inbuffer[1024];int n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);if (n > 0){inbuffer[n] = 0;int m = write(sockfd, inbuffer, n);if (m < 0){break;}}else if (n == 0){break;}else{break;}}::close(sockfd);
}
但是这里有一个细节要注意:read的返回值>0是表示读到的字节数(无\0),==0时表示读到文件结尾,在这里表示客户端结束。
就想吃在管道那里,写端管了,读端读出的自然都是0了。
客户端起手
socket
int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
注意客户端仍旧是用socket的返回值进行通信。
connect
同样,客户端还是不用bind,在UDP那是sendto时OS进行绑定,而在这就是connect时帮我们自动绑定。
sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
inet_pton(AF_INET, ip.c_str(), &peer.sin_addr);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
int n = ::connect(sockfd, (sockaddr *)&peer, sizeof(peer));
客户端LOOP
这里没啥好说的,就是普通的write + read。
但是要注意:由于是面向字节流,所以这里的处理是有问题的!具体如何操作请看用户自定义协议与序列化
while (true)
{std::cout << "Please enter#";std::string line;getline(std::cin, line);n = write(sockfd, line.c_str(), line.size());if (n < 0){break;}char inbuffer[1024];n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);if (n > 0){inbuffer[n] = 0;std::cout << inbuffer << std::endl; }
}
完整代码链接
验证

但是当我们多开一个客户端就会发现:一个服务端只能服务一个客户端。
原因在于我们当前是串行,必须等当前客户端退出才能accept下一个客户端,并没有处理并发客户端的能力。
所以改进就来了
改进
方案一
多进程方案。
我们知道子进程是会继承父进程的代码和数据的,同样的文件描述符表也会继承,因此我们就可以利用这个特点让子进程去执行代码和数据。

首先我们fork之后就要将不需要的文件描述符关闭
对于子进程来说,是怕误操作;
对于父进程来说,是为了防止文件描述符泄露,因为进程具有独立性,当子进程继承了后,就与父进程相互不影响了。若是父进程不关闭,一直申请fd却步关闭那么就造成了资源泄露。
但是注意,由于父进程要进行等待,所以此时仍然是串行。
想要解决有两种方法
- 由于子进程结束时会发送SIGCHLD信号,于是进行信号忽略
signal(SIGCHLD, SIG_IGN)。另外这也是最佳方案。 - 下段代码所示:我们进行再次fork,并让子进程退出,所以此时孙子进程成为了孤儿进程,孤儿进程的父进程是OS,结束归OS管,完美的利用了OS的特性。
pid_t pid = fork();
if (pid == 0)
{if (fork() > 0) exit(0);// 子进程关闭不需要的fd,防止误操作。::close(_listenfd);service(sockfd, inetaddr);exit(0);
}
waitpid(pid, nullptr, 0);
// 父进程关闭不需要的fd,防止内存泄漏。
::close(sockfd);
方案二
多线程
我们创建一个新线程,让新线程去执行service即可。
验证
此时同时启动两个客户端也可以完美的进行并发了。

相关文章:
【计算机网络】TCP实战
其实有了UDP的基础,TCP不管怎么说学习起来都还是比较舒服的,至少是比直接就学习TCP的感觉好。 这篇文章最多就是介绍一下起手式,如果想带业务的话和UDP那篇是完全一样的,就不进行演示了。 总的来说还是很简单的。 目录 Echo服务端…...
使用Python制作贪吃蛇小游戏
引言 贪吃蛇游戏是一款经典的电子游戏,玩家通过控制一条不断增长的蛇在格子内移动,并吃掉随机出现的食物来获得分数。随着分数的增加,蛇的身体也会越来越长,游戏的难度也随之提升。在本文中,我们将详细介绍如何使用Py…...
线程的退出
方式1 pthread_exit Void pthread_exit (void *retval) 功能: 结束调用的线程 参数: retval //退出状态值 //需要传的是,退出状态值的地址 注意: 1.pthread_exit 本身表示结束线程 如果用在main函数中 表示结束主线程…...
【AI 绘画】Q版人物定制生成
AI 绘画-PulID手办定制 1. 效果展示 本次测试主要结果展示如下: 牛仔风 古风 2. 基本原理 PuLID是一种类似于 ip-adapter 的恢复面部特征的方法。它同时使用 insightface 嵌入和 CLIP 嵌入,类似于 ip-adapter faceid plus 模型所做的。但是,在将图像传递给 CLIP 之前,还…...
Python爬虫——爬取某网站的视频
爬取视频 本次爬取,还是运用的是requests方法 首先进入bilibili官网中,选取你想要爬取的视频,进入视频播放页面,按F12,将网络中的名称栏向上拉找到第一个并点击,可以在标头中,找到后续我们想要…...
Android逆向题解攻防世界-easy-apk
Jeb反编译apk 题目比较简单,就是一个改了码表的base64编码。 protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setContentView(0x7F04001B); // layout:activity_main((Button)this.findViewById(0x7F0B0076)).set…...
Linux系统使用Typecho搭建个人网站并一键发布公网远程管理本地站点
文章目录 前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大…...
机器学习速成第三集——无监督学习之聚类(理论部分)!
目录 聚类的定义和原理 常见的聚类算法 应用场景 总结 无监督学习中聚类算法的最新进展是什么? K-Means聚类算法在处理大规模数据集时的性能优化方法有哪些? 并行计算模型: 多核处理器优化: 分层抽样: 特征缩…...
【机器学习】CNN的基本架构模块
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 CNN的基本架构模块1. 引言2. 卷积层2.1 基本原理2.2 卷积层的特性2.3 卷积层的超…...
第八节AWK报告生成器(2)
3,1,2 printf 语法: printf("format\n", [arguments])格式说明 format是一个用来描述输出格式的字符串,format格式的指示符都以%开头,后跟一个字符,如下: format含义%c显示字符的asicll%d,%i十进制整数%e,%E科学计数法显示数值…...
Linux 进程间通信之管道
个人主页:仍有未知等待探索-CSDN博客 专题分栏: Linux 目录 一、通信 1、进程为什么要通信? 1.数据的类型 2.父进程和子进程算通信吗? 2、进程如何通信? 3、进程通信的常见方式? 二、管道 1、概念…...
IDEA 无法启动,点击之后没有任何提示或者界面
当你尝试通过双击或以管理员身份启动程序时,均未能成功,且未收到任何提示信息或界面反馈,这确实令人困扰。为了诊断问题并获取有用的错误信息,你可以按照以下步骤操作: 1. 启用并查看错误信息 首先,你需要…...
ctf 堆栈结构
CTF(Capture The Flag)竞赛中,理解堆栈结构对于解决涉及二进制分析、逆向工程和利用开发的挑战至关重要。堆栈是在程序执行过程中用于临时存储数据和管理函数调用的关键数据结构。以下是堆栈结构的基本概念及其在CTF竞赛中的应用:…...
sqlserver的openquery配置
1.命令Demo ---openquery--开启Ad Hoc Distributed Queries组件,在sql查询编辑器中执行如下语句exec sp_configure show advanced options,1reconfigureexec sp_configure Ad Hoc Distributed Queries,1reconfigure--关闭Ad Hoc Distributed Queries组件࿰…...
Spring boot logback日志框架加载初始化源码
##LoggingApplicationListener监听 Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApplicationStartingEvent((ApplicationStartingEvent) event);}else if (event instanceof ApplicationEnvironment…...
qt-11基本对话框(消息框)
基本对话框--消息框 msgboxdlg.hmsgboxdlg.cppmain.cpp运行图QustionMsgInFormationMsgWarningMsgCriticalMsgAboutMsgAboutAtMsg自定义 msgboxdlg.h #ifndef MSGBOXDLG_H #define MSGBOXDLG_H#include <QDialog> #include <QLabel> #include <QPushButton>…...
Windows11下wsl闪退的解决
wsl闪退 1. 原因分析 解释:WSL(Windows Subsystem for Linux)闪退通常指的是在Windows操作系统中运行的Linux环境突然关闭。这可能是由于多种原因造成的,包括系统资源不足、WSL配置问题、兼容性问题或者是Linux内核的问题。&…...
通过调整JVM的默认内存配置来解决内存溢出(OutOfMemoryError)或栈溢出(StackOverflowError)等错误
文章目录 引言I 调整JVM的默认堆内存配置java命令启动jar包Tomcat服务器部署java应用引言 问题: org.springframework.web.util.estedServletException: Handlerdispatch failed: nested exception isjava.lang.0utOfMemoryError: Java heap space原因分析: 查询查询平台所…...
RCE---eval长度限制绕过技巧
目录 题目源码 方法一:命令执行的利用 方法二:file_put_contents(本地文件包含的利用) 方法三:usort(…$_GET); 题目源码 <?php $param $_REQUEST[param]; if(strlen($param)<17 && stripos($par…...
C++11标准模板(STL)- 算法库 - 类似 std::accumulate,但不依序执行 -(std::reduce)
算法库 算法库提供大量用途的函数(例如查找、排序、计数、操作),它们在元素范围上操作。注意范围定义为 [first, last) ,其中 last 指代要查询或修改的最后元素的后一个元素。 类似 std::accumulate,但不依序执行 std…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
