【Hello Network】网络编程套接字(四)

news2024/10/7 7:31:53

作者:@小萌新
专栏:@网络
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:简单介绍下TCP通讯过程

文章目录

  • 简单的TCP英译汉服务器
    • 更改handler方法
  • 地址转换函数
    • 字符串转化整型IP
    • 整数IP转化字符串
  • 绑定失败问题
  • TCP协议通讯流程
    • 通讯流程总览
    • 三次握手
    • 数据传输的过程
    • 四次挥手的过程
  • TCP/UDP

简单的TCP英译汉服务器

我们在上一篇博客中简单实现了一个多执行流的TCP服务器 多执行流服务器

在实现多执行流服务器的时候我们分别完成了多进程和多线程版本

为了进一步优化 我们最后还将线程池引入了我们的服务器当中

此时访问TCP服务器的各个客户端 分别由不同的执行流为其提供服务 因此这些客户端能够同时享受服务器提供的服务

我们当时说过 如果想要修改这个服务器的功能 我们只需要修改其中的handler方法即可 下面我们以简单的英译汉服务器为例 看看更改后我们的TCP服务器能否正常为客户端提供英译汉服务

更改handler方法

英译汉TCP服务器要做的就是 根据客户端发来的英文单词找到其对应的中文意思 然后将该中文意思作为响应数据发给客户端

我们之前是使用回调函数的方式来处理 当线程池当中的线程从任务队列中拿出一个任务后 会调用该任务对应的Run方法处理该任务

class Handler    
{    
    public:    
    Handler() = default;    
    void operator()(int sock)    
    {    
      // map three words    
      unordered_map<string , string> dict;    
      dict.insert(make_pair("hello" , "nihao"));    
      dict.insert(make_pair("world" , "shijie"));    
      dict.insert(make_pair("sort"  , "paixu"));    
    
    
      char buff[1024];    
      string value;    
    
      while(true)    
      {    
        ssize_t size = read(sock , buff , sizeof(buff) - 1);    
    
        if(size > 0)    
        {    
          buff[size] = 0;    
          cout << "sock: " << sock << endl;    
    
          string key = buff;    
          auto it = dict.find(key);    
          if (it != dict.end())    
          {    
            value = it -> second;    
          }    
          else    
          {    
            value = key;    
          }    
    
          write(sock , value.c_str() , value.size());    
                                                                                                                                                                                                                                                                                                                                                                    
        }    
        else if (size == 0)    
        {    
          cout << "read close" << endl;    
          break;    
        }    
        else    
        {    
          cout << "unknown error " << endl;    
          break;    
        }    
    
      }    
    
      close(sock);    
      cout << "service end" << endl;    
    }    
  };  

这里因为汉字在linux系统上显示有点问题 所以我们这里使用汉字拼音来替代了 下面是替代效果

在这里插入图片描述

我们可以发现如果我们发送的是字典里面记录过的单词服务器就会回显给我们它的中文拼音

如果发送的不是字典里面记录过的单词就会回显单词本身

地址转换函数

字符串转化整型IP

inet_addr函数

inet_addr函数的函数原型如下:

in_addr_t inet_addr(const char *cp);

参数说明:

  • cp:待转换的字符串IP。

返回值说明:

  • 如果输入的地址有效,则返回转换后的整数IP;如果输入的地址无效,则返回INADDR_NONE(通常为-1)

整数IP转化字符串

inet_ntoa函数

inet_ntoa函数的函数原型如下:

char *inet_ntoa(struct in_addr in);

参数说明:

  • in:待转换的整数IP。

返回值说明:

  • 返回转换后的字符串IP。

绑定失败问题

绑定端口失败主要是由三个问题引起的

端口号已被其他程序绑定

当我们在测试网络代码时 先将服务端绑定8081端口运行 然后运行客户端 并让客户端连接当前服务器

在这里插入图片描述

这也验证了一个端口只能绑定一个进程的规则

资源未释放干净

当我们在测试网络代码时 先将服务端绑定8081端口运行 然后运行客户端 并让客户端连接当前服务器

此时在有客户端连接服务端的情况下 如果直接将服务端关闭 此时服务端要想再次绑定8081号端口运行 就可能会绑定失败

在这里插入图片描述

无法绑定的端口号

我们自己编写的服务器代码在绑定端口号时 尽量不要绑定1024以下的端口号 一般云服务器只能绑定1024及其往上的端口号 因为1024以下的端口已经约定俗成被其他一些比较成熟的服务所使用了 如果我们绑定1024以下的端口号 那么会绑定失败

TCP协议通讯流程

通讯流程总览

下图是基于TCP协议的客户端/服务器程序的一般流程:

在这里插入图片描述

下面我们结合TCP协议的通信流程 来初步认识一下三次握手和四次挥手 以及建立连接和断开连接与各个网络接口之间的对应关系

三次握手

在这里插入图片描述

初始化服务器

当服务器完成套接字创建、绑定以及监听的初始化动作之后,就可以调用accept函数阻塞等待客户端发起请求连接了

服务器初始化:

  • 调用socket,创建文件描述符。
  • 调用bind,将当前的文件描述符和IP/PORT绑定在一起,如果这个端口已经被其他进程占用了,就会bind失败。
  • 调用listen,声明当前这个文件描述符作为一个服务器的文件描述符,为后面的accept做好准备。
  • 调用accept,并阻塞,等待客户端连接到来。

建立连接

而客户端在完成套接字创建后,就会在合适的时候通过connect函数向服务器发起连接请求,而客户端在connect的时候本质是通过某种方式向服务器三次握手,因此connect的作用实际就是触发三次握手。

建立连接的过程:

  • 调用socket,创建文件描述符。
  • 调用connect,向服务器发起连接请求。
  • connect会发出SYN段并阻塞等待服务器应答(第一次)。
  • 服务器收到客户端的SYN,会应答一个SYN-ACK段表示“同意建立连接”(第二次)。
  • 客户端收到SYN-ACK后会从connect返回,同时应答一个ACK段(第三次)

这个建立连接的过程,通常称为三次握手。

需要注意的是,连接并不是立马建立成功的,由于TCP属于传输层协议,因此在建立连接时双方的操作系统会自主进行三次协商,最后连接才会建立成功。

数据传输的过程

在这里插入图片描述

数据交互

连接一旦建立成功并且被accept获取上来后,此时客户端和服务器就可以进行数据交互了。需要注意的是,连接建立和连接被拿到用户层是两码事,accept函数实际不参与三次握手这个过程,因为三次握手本身就是底层TCP所做的工作。accept要做的只是将底层已经建立好的连接拿到用户层,如果底层没有建立好的连接,那么accept函数就会阻塞住直到有建立好的连接。

而双方在进行数据交互时使用的实际就是read和write,其中write就叫做写数据,read就叫做读数据。write的任务就是把用户数据拷贝到操作系统,而拷贝过去的数据何时发以及发多少,就是由TCP决定的。而read的任务就是把数据从内核读到用户。

数据传输的过程:

  • 建立连接后,TCP协议提供全双工的通信服务,所谓全双工的意思是,在同一条连接中,同一时刻,通信双方可以同时写数据,相对的概念叫做半双工,同一条连接在同一时刻,只能由一方来写数据。
  • 服务器从accept返回后立刻调用read,读socket就像读管道一样,如果没有数据到达就阻塞等待。
  • 这时客户端调用write发送请求给服务器,服务器收到后从read返回,对客户端的请求进行处理,在此期间客户端调用read阻塞等待服务器端应答。
  • 服务器调用write将处理的结果发回给客户端,再次调用read阻塞等待下一条请求。
  • 客户端收到后从read返回,发送下一条请求,如此循环下去。

四次挥手的过程

在这里插入图片描述

端口连接

当双方通信结束之后,需要通过四次挥手的方案使双方断开连接,当客户端调用close关闭连接后,服务器最终也会关闭对应的连接。而其中一次close就对应两次挥手,因此一对close最终对应的就是四次挥手。

断开连接的过程:

  • 如果客户端没有更多的请求了,就调用close关闭连接,客户端会向服务器发送FIN段(第一次)。
  • 此时服务器收到FIN后,会回应一个ACK,同时read会返回0(第二次)。
  • read返回之后,服务器就知道客户端关闭了连接,也调用close关闭连接,这个时候服务器会向客户端发送一个FIN(第三次)。
  • 客户端收到FIN,再返回一个ACK给服务器(第四次)。

这个断开连接的过程,通常称为四次挥手。

注意通讯流程与socket API之间的对应关系

在学习socket API时要注意应用程序和TCP协议是如何交互的:

  • 应用程序调用某个socket函数时TCP协议层完成什么动作,比如调用connect会发出SYN段。
  • 应用程序如何知道TCP协议层的状态变化,比如从某个阻塞的socket函数返回就表明TCP协议收到了某些段,再比如read返回0就表明收到了FIN段。

为什么要断开连接?

建立连接本质上是为了保证通信双方都有专属的连接,这样我们就可以加入很多的传输策略,从而保证数据传输的可靠性。但如果双方通信结束后不断开对应的连接,那么系统的资源就会越来越少。

因为服务器是会收到大量连接的,操作系统必须要对这些连接进行管理,在管理连接时我们需要“先描述再组织”。因此当一个连接建立后,在服务端就会为该连接维护对应的数据结构,并且会将这些连接的数据结构组织起来,此时操作系统对连接的管理就变成了对链表的增删查改。

如果一个连接建立后不断开,那么操作系统就需要一直为其维护对应的数据结构,而维护这个数据结构是需要花费时间和空间的,因此当双方通信结束后就应该将这个连接断开,避免系统资源的浪费,这其实就是TCP比UDP更复杂的原因之一,因为TCP需要对连接进行管理。

TCP/UDP

  • 可靠传输 vs 不可靠传输
  • 有连接 vs 无连接
  • 字节流 vs 数据报

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

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

相关文章

贪吃蛇小游戏(C++)

首先我们需要下载EasyX&#xff08;具体的方法在EasyX专栏中有提到&#xff09; easyX下载和绘制简单基本图形_小梁今天敲代码了吗的博客-CSDN博客 贪吃蛇这个游戏我们一定都玩过&#xff0c;玩家使用方向键操控一条“蛇”&#xff0c;蛇会朝着一个方向不断移动&#xff0c;玩…

主成分分析

一、案例与数据 某研究者对企业员工进行调查&#xff0c;并且制定了一份问卷&#xff0c;研究者想要将问卷中的多个量表题进行浓缩以便后续分析&#xff0c;比如休假制度、资金制度、工资水平或者晋升制度等等&#xff0c;其中部分数据如下&#xff1a; 二、分析问题 其实想要…

时序预测 | MATLAB实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆网络时间序列预测

时序预测 | MATLAB实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆网络时间序列预测 目录 时序预测 | MATLAB实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆网络时间…

前端已死还是“娱乐至死”?做个清醒的前端

目录 一、前言 二、为什么会出现“前端已死”的观点&#xff1f; 1、技术变化太快&#xff1a; 2、前端工程化带来的挑战&#xff1a; 3、新技术的崛起&#xff1a; 三、前端该如何提升自己 1、学习新技术&#xff1a; 2、掌握前端工程化&#xff1a; 3、参与社区和开…

远程网关桥接模式实现同一局域网组网管理(Superlink)

远程网关桥接模式配置介绍及示例 功能简述 智联物联远程网关桥接模式&#xff0c;是指电脑侧Superlink远程工具及前端ZP网关通过4G/WAN/WiFi客户端等方式分别接入互联网及后端服务器平台远程服务后&#xff0c;再借助虚拟网卡桥接技术&#xff0c;先由服务器平台下发桥接网络参…

室内定位之5G定位

一、前言 随着5G通信基础设施逐步完善&#xff0c;5G网络的优势一方面在于其具有的更大的带宽和子载波间隔&#xff0c;使得5G室内定位精度提升(3.5GHz频段下定位精度在5米左右&#xff0c;毫米波定位精度可以达到3米或更高)&#xff0c;相对于其他定位技术&#xff0c;已建成…

解决docker启动mysql无法输入中文以及中文不显示或乱码问题

前言 我在使用MySQL时&#xff0c;遇到了两个问题。一是在插入中文数据时&#xff0c;无法输入中文。二是在select的时候&#xff0c;查出来的中文数据是空的&#xff08;因为插入时为空&#xff09;&#xff0c;然后我就使用Navicat连接数据库添加了中文数据&#xff0c;再到…

【MySQL】联合查询子查询以及合并查询的使用

目录 上篇在这里喔~ GROUP BY分组子句与联合查询的使用详解 联合查询步骤 1.自连接 1.查询每位同学的计算机原理和Java的成绩 2.显示所有计算机原理成绩比java成绩高的成绩信息 2.子查询 1.单行子查询 1.查询’许仙‘的同班同学 2.多行子查询 1.查询语文或英语课程的…

综合管廊智慧运维管理平台应用研究

摘要&#xff1a;为提升综合管廊运维管理水平&#xff0c;实现管理的数字化转型&#xff0c;采用综合监测系统、BIMGIS 可视化系统、智能机器人巡检、结构安全监测等技术&#xff0c;搭建实时监控、应急管理、数据分析等多功能为一体的智慧管廊运维管理平 台&#xff0c;为综合…

Python爬虫基础之二

Python爬虫基础包括HTTP协议、HTML、CSS和JavaScript语言基础、requests库的使用、Beautiful Soup库的使用、xpath和正则表达式的使用等。此外&#xff0c;还应该了解反爬虫机制和爬虫的一些常见问题及解决方法。 上一篇文章讲解了有关条件判断语句、循环语句等相关知识&#…

stm32串口中断流程

NVIC简介 内嵌向量中断控制器:Nested Vectored Interrupt Controller (NVIC) 硬件发生中断后,相关电信号会触发内核跳转中断向量表查找中断函数, 中断向量名查于汇编文件startup_stm32xxx.s文件 从本质上说这里就是数字电路的组合形成的一系列行为&#xff0c;当相关中断的寄存…

一键汇总——高效电脑检索方案

文件检索和分类对于电脑内部的文件检索非常重要&#xff0c;因为它们可以帮助用户快速找到需要的文件。在电脑内部&#xff0c;文件通常被分类存储在不同的分区中&#xff0c;这有助于管理员更好地组织和查找文件。对于分区资源整合&#xff0c;以下是一些建议&#xff1a; 了…

大好河山集团董事长黄国林受邀出席2023中国好公司高峰论坛暨产学研合作峰会

大好河山集团董事长黄国林受邀出席2023中国好公司高峰论坛暨产学研合作峰会 本网消息 2023年4月19日&#xff0c;由中国智慧工程研究会、华夏商邦俱乐部、中国流通研究院共同主办&#xff0c;中国品牌发展网联合主办的“2023中国好公司高峰论坛暨产学研合作峰会”在福州福清举…

应急照明系统在民用建筑的设计应用与产品选型

【摘要】应急照明分为备用照明、安全照明及疏散照明。文章介绍了应急照明系统的设计、灯具选择、灯具布置、配电等要求。并结合实例进行疏散照明的计算&#xff0c;以指导应急照明系统的设计与应用。 【关键词】照度&#xff1b;光通量&#xff1b;消防应急灯具&#xff1b;A型…

【Linux】解决切换用户出现bash-4.2$问题创建普通用户并设置密码、授权

【问题描述】 linux中创建了一个wxh用户&#xff0c;然后使用su命令切换用户后&#xff0c;终端提示符显示成“bash-4.2$”而不是[rootlocalhost wxh]#&#xff0c;导致ll等命令无法执行。 [rootlocalhost xhh]# su wxh bash-4.2$ ll bash: ll: 未找到命令 【原因】 没有在hom…

13种权重的计算方法

权重计算方法有很多种&#xff0c;不同的方法有不同的特点和适用情况。AHP层次分析法和熵值法在权重计算中属于比较常用的方法。除此之外&#xff0c;还有一些与权重计算相关的方法&#xff0c;今天一文总结了13种与权重计算相关的方法&#xff0c;大家可以对比选择使用。 一、…

【001-Java基础练习】-适合初学者的练习

用于巩固java基础知识&#xff0c;初学者多练多敲&#xff0c;熟悉代码&#xff0c;熟悉语法就ok。 练习1、从控制台获取Java、ps、HTML三门课程的成绩&#xff0c;计算总分和平均分&#xff08;平均分保留2位小数&#xff0c;要求四舍五入&#xff09;&#xff0c;输出总分和…

OpenAI最新官方ChatGPT聊天插件接口《插件部署上生产》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(五)(附源码)

Plugins in production 插件部署上生产 前言Rate limits 速率限制Updating your plugin 更新您的插件Plugin termsDomain verification and security 域验证和安全性Defining the plugins root domain 定义插件的根域Manifest validation 清单验证Resolving the API spec 解析A…

【小程序】input输入双向数据绑定

小程序中&#xff0c;input标签中的数据为单向绑定&#xff1a; <inputtype"number"bindinput"inputRealmoney"value"{{ amount }}"placeholder"请输入金额" />如上代码&#xff0c;我们绑定了输入框的数据amount&#xff0c;并…

前端后端实现防盗链

防盗链&#xff08;Referer Header&#xff09;是指在网页中嵌入的外部资源&#xff08;如图片、音视频等&#xff09;被非法使用或盗链的现象。为了避免这种情况的发生&#xff0c;我们可以通过前端和后端技术来防止盗链。 前端防盗链 前端防盗链可以通过在页面中添加 JavaSc…