socket编程——tcp

news2025/2/25 0:31:18

在我这篇博客:网络——socket编程中介绍了关于socket编程的一些必要的知识,以及介绍了使用套接字在udp协议下如何通信,这篇博客中,我将会介绍如何使用套接字以及tcp协议进行网络通信。

1. 前置准备

在进行编写代码之前,我们要明白一点:udp协议是面向数据报的,而tcp协议是面向字节流的,同时tcp是面向连接的,它要进行数据通信,就必须先连接,这些我们日后再解释。
udp协议和tcp协议所使用的socket文件是全双工的,也就是可以同时对该文件进行读写,而不会有任何影响,这一点在我上面的那篇博客中udp协议的套接字编程也有所体现。
那么我们现在就来开始tcp协议式的套接字编程。

2. 开始编码

a. 整体框架

整体框架还是一如既往:
Server.hpp:
在这里插入图片描述
客户端main函数:
在这里插入图片描述
服务端main函数:
在这里插入图片描述

b. Server.hpp

1). sockfd和bind

我们仍需要一个socket文件来被获取,以及需要bind成为网络进程:
在这里插入图片描述
在这里插入图片描述

2). listen

我们前面说过,要想使用tcp协议进行网络通信,就必须先让客户端与服务端连接,而这个连接申请一般是由客户端发起的,所以我们的服务端应该时刻 “监听” 客户端发过来的连接请求,所以我们要认识一个接口listen:
在这里插入图片描述
在这个接口中,我们第一个参数就是我们现在创建的所使用的_sockfd,第二个参数现在不做解释,我们给它填成5,这个函数也是成功返回0,失败返回-1,错误码被设置:
在这里插入图片描述
至此我们的tcp协议的初始化就完成了,当你使用tcp协议进行通信时,这段代码是始终不变的。

3). accept

接下来就是我们的Start函数了,上面说到,我们需要先建立连接,然后再进行通信,我们现在光知道客户端要连接服务端了(listen),我们还得接收它(accept):
在这里插入图片描述
这个函数的参数的第一个就是我们的_sockfd, 第二个和第三个参数就如同udp协议进行通信时的recvfrom中的那两个字段一样,作为输入输出型参数,主要是输出型参数来接收客户端的套接字信息。
这个函数我们需要重点关注它的返回值:
在这里插入图片描述
这句话的大致意思就是,当这个函数成功的话会给我们返回一个文件描述符。为什么这里要返回一个文件描述符呢?我们可以这样理解:
在这里插入图片描述

而其中,我们listen中的_sockfd就是那个招揽客人的人,这个accept返回的文件描述符就是服务员,而我们进行tcp通信时,客户端和服务端通信服务端使用的文件描述符就是这个accept返回的文件描述符:
在这里插入图片描述
所以我们应该,将成员变量_sockfd更名为_listenfd更合适一点。

4). echo server

现在我们开始编写如和接收客户端的信息,以及如何返回去,我们前面说过tcp是面向字节流的,所以我们的读写接口使用read和write接口也不令人意外了:
在这里插入图片描述
在这里插入图片描述
接下来我们的服务端就完成了。
接下来就是客户端的编写。

c. client

开始的套路不变:
在这里插入图片描述
这里我们再认识一个接口,那就是客户端如何向服务端发起连接请求:
在这里插入图片描述
内容不做解释了:
在这里插入图片描述

效果如下:
在这里插入图片描述
当有一端退出时,另一端目前也会退出:
在这里插入图片描述
还有一点,那就是当对这段代码测试时连续的运行server程序,会出现场这种情况:
在这里插入图片描述
这样的原因是因为程序退出后,并不会立即释放连接。我们只需要在Init函数中写这么一段代码就可以了:
在这里插入图片描述
这也是一种固定写法,原理是什么日后再说。至此我们就可以使用tcp协议进行简单的网络套接字通信了,由此可见tcp协议使用的文件描述符也是全双工的。

d. 引入多进程

上面的代码在面对一个客户时,没有什么问题,但是如果是多个用户要连接服务器呢?
在这里插入图片描述
我们发现只有上面的客户端可以正常进行通信,下面的客户端甚至都没有连接到服务器,当我们上面的客户端退出之后:
在这里插入图片描述
我们发现,下面客户端可以连接服务器了,并且一瞬间把消息返回来了。这其中的原因就是,我们的服务器是单进程的,在给第一个客户端提供服务时,我们的服务器进程就进入一个死循环了:
在这里插入图片描述
在这里插入图片描述

所以我们要将我们的服务器改为多进程的:
在这里插入图片描述

这样一来,当我们accept返回一个sockfd之后,我们就创建子进程,让子进程进行执行对该客户端的服务,然后然父进程回收子进程就可以了,但是这里还有个问题。当我们创建好子进程之后,子进程去执行任务去了,但是父进程需要等待子进程,等子进程结束之后父进程才能成功回收,然后继续接收切他客户端的连接,这样一来,这个代码不还是串行的,不能支持多个用户同时连接服务器嘛,所以我们还需要再改造一下:
在这里插入图片描述
在这里我们写了这样一段代码,当这段代码执行之后,子进程就又创建一个孙子进程,然后两者会继续执行后续的代码,但是此时我们让子进程直接退出,这个时候,父进程会直接回收子进程,而孙子进程由于它的父进程exit了,那么它就会成为孤儿进程被操作系统领养。这样我们就不需要让父进程一直等待了,而是直接对子进程回收之后就可以。
但是,还有问题。这个问题我使用图来表示一下:
在这里插入图片描述
在这个过程中,我们发现当我们的的孙子进程被创建好之后,执行完服务任务之后,关闭sockfd,退出之后,实际上这个sockfd并没有关闭,因为这个sockfd实际的指向还有一个父线程。这个文件结构体并不会被销毁,那么这样的话,当客户端连接的越来越多的话,我们父进程的文件描述符就会越来越多,这一点我们可以通过执行程序来看到:
在这里插入图片描述

但是要知道,一个进程的文件描述符数量是有限的,这样的话,就会出现文件描述符泄露的问题,所以我们需要,在各自的进程中,关闭不需要的文件描述符:
在这里插入图片描述
我们再来看程序运行:
在这里插入图片描述
从此以后我的们的父进程在接收返回来的sockfd之后就永远都是4了。也不会出现文件描述符泄露的问题。
上面的代码中我们的父进程还是需要等待子进程的并且我们接受一个客户端就需要创建两次子进程。这样效率有点低,所以我们再次改造:
在这里插入图片描述
我们直接对子进程退出时会给父进程发出的SIGCHLD信号进行忽略,这样的话,我们的子进程退出之后就不需要让父进程等待了,而是直接被操作系统释放:
在这里插入图片描述
我们发现,我们的代码在服务端被多个客户端连接后无法分辨客户端,所以我们再优化一下代码:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

e. 多线程

我们知道创建进程就意味着,一整套内核资源要被重新创建,这样的效率还是较低下的,所以我们使用多线程,再次修改代码:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
可以看到也可以进行多个客户端同时通信,并且也不需要关闭文件描述符了,因为文件描述符表也是多线程的共享资源。
但是,线程的创建也是有代价的,为什么不提前创建好一批线程呢?所以就有了线程池。

f. 线程池

在引入线程池之前,我要先更改代码中的一个地方:
在这里插入图片描述
在这个类中,我们将IP地址的网络字节序列转化为字符串,但是这个接口实际上不是那么安全:
在这里插入图片描述
我们可以看到,这个函数的返回值是一个char*类型的,这意味着,我们的返回的IP字符串,之前是被存在一个地方的,这个地方不是我们提供的,因为我们没有写,这个地方是C语言提供的,那就意味着,假如这个地方是固定的话,那就会导致多线程下的线程安全问题,所以我们需要换一个接口:
在这里插入图片描述

在这里插入图片描述
这么一改之后,这个函数的字符串的最终存储地址是我们单独在栈上提供的,就不用担心上面的问题了。
现在我们再来向代码中引入线程池:
在这里插入图片描述

这里我设置的线程池的数量是两个。

在这里插入图片描述
可以看到是可以正常通信的,但是:
在这里插入图片描述
当我再次连接一个客户端时,第三个客户端卡住了,这其实就是因为,我们的Service服务是一个死循环,我们线程池中的线程只有两个线程,而当第三个客户端连接过来之后,仅有的两个线程还在死循环中,所以就会导致第三个连接的客户端不能通信。
我们现在服务端提供的Service服务是一个死循环,是一个长任务,我们上面的线程池代码明显是不支持的,所以我们要改造代码,将Service长服务改为一个短服务,例如只完成一个功能(大小写转换,翻译,Ping服务器等):
首先服务器能提供一些服务:
在这里插入图片描述
在这里插入图片描述
注册服务通过服务端main函数中硬编码。
在这里插入图片描述

然后我们具体的服务应该是先让用户输入一个操作符,然后再接收一段用户想要被操作的内容,这段内容经过操作符对应的处理,将结果发送给用户:
在这里插入图片描述
在这里插入图片描述
接收用户操作符
在这里插入图片描述
接收用户想被操作的内容
在这里插入图片描述
根据操作符处理内容并返回结果
在这里插入图片描述
最后将结果发给用户
在这里插入图片描述
其中我们对于没有的服务以及ping功能是不需要用户再次输入内容的,所以我做了特殊处理。
在服务端mian函数中我们提供服务功能:
在这里插入图片描述
对于客户端我们也肯定要做出修改:
在这里插入图片描述
这其中本地文件内容如下:
在这里插入图片描述
而获取服务端服务列表是为了用户知道有哪些服务:
在这里插入图片描述
在这里插入图片描述
其中服务器中有一个翻译功能,我使用一个类对该功能进行封装:
在这里插入图片描述
在这里插入图片描述
这就是关于tcp和线程池实现的一个简单的短服务功能的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1602234.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新版idea 合并分支方法

前言 以下是最新版的idea2024,如果有人找不到按键可能是因为版本不同。 操作步骤 看右小角我的分支是submit,现在我要将test合并到我的submit分支上 找到test分支,选择update,这一步会拉取相应分支内容等同于pull 选择merge 选…

能源成果3D网络三维展厅越发主流化

在这个数字化飞速发展的时代,我们为您带来了全新的展览形式——线上3D虚拟展厅。借助VR虚拟现实制作和web3d开发技术,我们能够将物品、图片、视频和图文信息等完美融合,通过计算机技术和3D建模,为您呈现一个逼真、生动的数字化展览…

【保姆级】frp内网穿透

场景:本地调试微信生态的回调时,如果全用线上调试,那就太恶心人了,所以我们今天简单说下frp内网穿透 一、安装和配置 frp下载地址: https://github.com/fatedier/frp/releases 简单说一下: 服务器用的是f…

包装类的认识

前言~🥳🎉🎉🎉 hellohello~,大家好💕💕,这里是E绵绵呀✋✋ ,如果觉得这篇文章还不错的话还请点赞❤️❤️收藏💞 💞 关注💥&#x1…

储能的全生命周期成本即平准化度电成本的计算方法及python实践

1. 平准化度电成本(LCOE)是一种衡量电力项目经济性的指标 LCOE(Levelized Cost of Energy,)的概念最早由美国国家可再生能源实验室(NREL)在1995年提出,它是通过将一个项目生命周期内的所有成本…

什么牌子的短袖t恤质量好?2024年高品质短袖推荐!

什么牌子的短袖质量好?最近大部分地区的天气都比较热,大家对短袖的关注度日益提升。然而作为一名十年以上的经验的服装测评师,我必须强调市面上有许多所谓网红品牌,虽然广告营销做得沸沸扬扬,但对短袖的做工、细节方面…

ELK日志分析系统(上)

目录 引言 一、ELK日志分析系统简介 1.1 日志服务器 1.2 ELK日志分析系统的组成 1.3 日志处理步骤 二、Elasticsearch介绍 2.1 概述 2.2 核心概念 三、Logstash介绍 3.1 概述 3.2 主要组件 四、Kibana介绍 4.1 概述 4.2 主要功能 五、ELK的工作原理 六、部署ELK…

二维码电子画册制作教程,教你如何做出高端作品!

当今社会,二维码已经成为了信息传递的重要方式之一,其在电子商务、广告营销、活动推广等领域广泛应用。而如何将二维码巧妙地融入电子画册中,制作出高端、具有吸引力的作品,成为了许多设计师和营销人员关注的焦点 但是很多人却不知…

HTTP协议详解:从URL到HTTP报文的全貌

⭐小白苦学IT的博客主页⭐ ⭐初学者必看:Linux操作系统入门⭐ ⭐代码仓库:Linux代码仓库⭐ ❤关注我一起讨论和学习Linux系统 前言 随着互联网的飞速发展,HTTP协议作为支撑网络应用的核心基石,扮演着举足轻重的角色。无论是日常的…

LDR6028,充电听歌两不误!

当前市场上的手机普遍取消了3.5mm耳机接口,仅保留Type-C接口。然而,对于音质和零延迟有追求的用户,仍倾向于选择3.5mm有线耳机,特别是在玩游戏时,音画同步至关重要。因此,Type-C转3.5mm接口线应运而生&…

Oracle11.2.0.1,(CVE-2012-1675)漏洞解决方案

1.进入容器停止监听 docker exec -it -u 0 oracle11g bash su - oracle lsnrctl stop listener2.找到监听配置文件位置,修改监听文件 echo $ORACLE_HOMEvi network/admin/listener.ora #在文件底部添加 SECURE_REGISTER_LISTENER (IPC) #启动监听 lsnrctl start …

《量化投资以Python为工具》目录

《量化投资以Python为工具》 获取链接:《量化投资以Python为工具》 更多技术书籍:技术书籍分享,前端、后端、大数据、AI、人工智能... ​ ​ ​ ​

【实战】Dubbo应用可观测性升级指南与踩坑记录

应用从dubbo-3.1.*升级到dubbo-*:3.2.*最新稳定版本,提升dubbo应用的可观测性和度量数据准确性。 1. dubbo版本发布说明(可不关注) dubbo版本发布 https://github.com/apache/dubbo/releases 【升级兼容性】3.1 升级到 3.2 2. 应用修改点 注意:Sprin…

大模型用到的位置编码汇总(面试)

不同于RNN、CNN等模型,对于Transformer模型来说,位置编码的加入是必不可少的,因为纯粹的Attention模块是无法捕捉输入顺序的,即无法区分不同位置的Token。为此我们大体有两个选择:想办法将位置信息融入到输入中&#x…

第一部分-基础入门-学习导航

专题地址:MacOS一站式程序开发系列专题 第一部分:基础入门学习导航 OSX-01-Mac OS应用开发概述:简单介绍下MacOS生态、Xcode使用以及使用Xcode创建app的方法OSX-02-Mac OS应用开发系列课程大纲和章节内容设计:介绍下此系列专题的文章内容组织形式以及此系列专题的覆盖内容…

OpenHarmony轻量系统开发【1】初始OpenHarmony

1.1系统类型 OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互…

使用Ollama加载微软开源WizardLM2-7B模型,并进行“弱智吧”问题测试

开发团队在发布模型之前忘记对其进行毒性测试,微软删除了WizardLM2所有相关模型,HuggingFace的WizardLM仓库已经清空。 不过不用担心,WizardLM2模型的许可是Apache 2.0,微软无权要求他人删除复制的仓库。目前在HuggingFace上仍然…

C++语言·类和对象

1. 类的引入 C语言结构体中只能定义变量,但在C中,结构体内不仅可以定义变量,也可以定义函数,同时C中struct的名称就可以代表类型,不用像C那样为了方便还要typedef一下。 在C中我们管定义的结构体类型叫做类(student)&a…

Spring (四) 之配置及配置文件的操作

文章目录 1、Spring 基于注解的配置基于注解的配置引入依赖包配置实体类数据访问层业务层业务层实现测试 2、Bean和Component和Configuration的区别1 Bean:2 Component:3 Configuration:总结: 区别Component和Configuration区别 3、Spring读取properties配置文件准备…

Springboot框架——4.整合jdbc

1.pom.xml中导入依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupI…