网络与套接字

目录

初识网络

网络的概念

局域网通信 

如何理解报头

IP和mac

udp——代码实现

tcp——代码实现


先有计算机,然后有OS,最后才有了网络,而网络则是为了提高效率的!

局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起!

广域网:只是一个相对的概念,可以看成是一个范围更广的局域网!

网络的位置

软件是可以分层的!,而网络也是软件,所以网络协议栈也是层状结构,就再正常不过了!

网络与套接字_第1张图片

这里的网络,指的是网络协议栈,它是一个软件,贯穿体系结构的tcp/ip,属于OS的一部分

认识协议

例:在2000年的时候,小明在外地上大学,而因为电话费很贵,所以他和他爸做了个约定,来节

省电话费,小明给家里打电话,电话响一声,小明就挂掉,表示小明是平安的;响两声,小明就挂

掉,表示小明没钱了;响三声,表示小明有其它事情要处理,需要他爸接电话沟通,而这样的约

定,就是对协议的感性认识!

网络与套接字_第2张图片

网络协议的概念

所谓的计算机协议,本质就是约定,约定是由编码的程序员自己根据标准文档(只约定好协议,是

不够的,因为计算机操作系统,生产厂商等都有很多,所以就需要有一个人来,来约定共同的标

准,大家遵守),或者自己的喜好定义的协议!

OSI七层模型

OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范

TCP/IP五层(或四层)模型

物理层: 负责光/电信号的传递方式,物理层的能力决定了最大传输速率、传输距离、抗干扰性等,

集线器(Hub)工作在物理层

数据链路层: 负责设备之间的数据帧的传送和识别,交换机(Switch)工作在数据链路层

网络层: 负责地址管理和路由选择,路由器(Router)工作在网路层

传输层: 负责两台主机之间的数据传输

应用层: 负责应用程序间沟通

例:如下图,是一个洗发水快递的输送过程,卖家首先需要将洗发水找快递公司去封装,比如贴快

递单, 包装洗发水等等,然后发货,而从卖家运输到客户,走的是一条什么样的路径(路径规

划)是由网络层决定的,而具体从这一站到下一站,则是由数据链路层决定,比如从浙江到云南,

而如果客户在收到快递后,发现产品破损,联系卖家,卖家重新发一份,传输层所做的就是卖家此

时的工作!

网络与套接字_第3张图片

注意:数据传输给对面,事情还没完,这只是第一步,还有第二步,如何分析和使用数据,这是由

应用层去做的

局域网通信 

如下图,两台主句通信,是需要走完所有层,经过以太网传输数据给对方,而不能在某一层直接跨

越过去,比如:你朋友在宿舍另外一栋,你在这一栋,那你要将他的文件给他,就必须先下楼到一

楼,然后再上楼给他!

如下图,数据在发送的传输过程中,在每一层都会添加报头,而在收到的传输过程中,在每一层都

会去掉报头!而在用户看来,两个人之间是直接通信的!

网络与套接字_第4张图片

如何理解报头

从生活角度理解

所有的快递盒子上,都必定有快递单,而其中最重要的就是它的格式+数据,它就可以称为快递的

报头,因为没有它,就无法得知,该快递如何派发,所以需要报头中的数据,来指导当前层进行某

种协议决策!

在计算机OS中理解报头和数据

每一层添加的就是报头,其它的则是数据,比如上图,在网络层添加的五边形就是报头,另外两个

则是数据,该数据也被称为有效载荷!

报头也是数据,而且是一种结构化的数据,又因为Linux是C语言写的,站在语言角度去理解,就

有了下图中的报头!添加时只需要用memcpy或memove拷贝到缓冲区即可!

网络与套接字_第5张图片

如下图,在传输数据给对方解包的过程中,几乎每一层协议的报头中都要包含两种字段下面两种字

段,这两种也是协议的共性!第1点也被称为分用!

1,当前报文的有效载荷要交付给上层的哪一个协议!比如是交给TCP,还是udp

2,几乎每个报头,明确报头和有效载荷的边界,防止多解或者少解!

网络与套接字_第6张图片

如下图,是一个局域网,B向G发送消息的时候,报文中包含G的mac地址,而光电信号是无法将

其直接发送给G的,所以每一台主机都会收到这个报文,而其它主机在收到时,因为不是它的,就

会将其丢弃,只有G才会接收!比如,在教室上课,老师喊张三回答问题,就只有张三会站起来,

其它人会将老师的话忽略掉

因为网络资源被大家所共享,所以如果两台及其以上主机同时发送,就可能发送数据碰撞的问题,

所以每台主机都要有碰撞检测的能力,以及碰撞避免算法!!!

因为网络资源为大家所共享,所以它也是临界资源!局域网中任何一个时刻,都只能有一台主机在

向局域网中发送消息,说明各个主机访问网络资源是"互斥"的!

如果想攻击这个局域网,就可以一直占用网络资源,使其它主机出现"饥饿"问题!

网络与套接字_第7张图片

IP和mac

报文是有两套地址的,一套是IP地址,另一套则是mac地址,IP地址是从哪里来,到哪里去,几乎

一直不变,比如你要从湖南经过多个省份到达北京,起始地址和终止地址始终没变,而mac地址则

是上一站从哪里来,下一站去哪里,一直在变化,起始地址和终止地址一直在变,因为它的上一站

和下一站在变!

如下图,主机A发送消息给主机B,报文中的报头在经过路由器后发生了巨大改变,将原来的报头

解包后,重新封装了令牌环的报头,但它的有效载荷是一定不会变的!要不然这通信就没意义了!

而有了路由器屏蔽下面的操作后,IP上面的也就看不到底层网络的任何差异!

mac地址,在数据链路层,经过路由器,一直在进行解包和封装!!!

网络与套接字_第8张图片

源IP,目的IP:对一个报文来讲,从哪里来到哪里去,而最大的意义则是指导一个报文该如何进行

路径选择,到哪里去:本质就是让我们根据目标,进行路径选择的依据!

数据从A主句到达B主句,不是目的!而是要让数据到目标主机B上的一个进程,让该进程提供数

据处理的服务

计算机本身不产生数据,阐述数据的是人!人是通过特定的客户端,产生的数据!比如你下载一个

app,然后注册,就会起昵称等等

端口号

IP地址(公网IP)唯一的标识互联网中的一台主机

本质上,所有的网络通信,站在普通人的角度,都是人和人之间通信(小白)

技术人员的视角,我们学到的网络通信,本质是:是进程间通信!比如:抖音的app客户端(进程)

<->抖音的服务器(进程)

如下图,IP仅仅是解决了两台物理机器之间互相通信,比如主机A与B,但是我们还要考虑如何保

证双方的用户之间能看到发送的和接收的数据,比如抖音app客户端与抖音服务器,要让这两者都

看到发送和接收的数据,不能发送到其它进程,所以就有了端口号!!!

端口号:唯一的标识一台机器上的唯一的一个进程!

网络与套接字_第9张图片

sockct

IP + PORT = 能够标识互联网中的唯一的一个进程!!!

整个网络可以看作是一个大的OS,所有的网络上网行为,基本都是在这一个大的OS内,进行进程

间通信!!!

ip地址 + port端口号 = socket(套接字)

网络与套接字_第10张图片

进程的PID与PORT的区别 

例:PID就相当于我们的身份证,PORT就相当于我们的学号,虽然身份证号可以唯一确定一个

人,也就可以直接用身份证号来充当学号,但这样做不好,其一,学号里面的数字有许多含义,例

如确定了你是哪个学院的哪个班级等等,其二,不会受身份证号的影响,假设某天国家不使用身份

证号了,如果用身份证作学号,也就可能无法唯一标识学校的某个人了

要进行通信,本质:先找到目标主机;再找到该主机上的服务(进程),总结:互联网世界,是一

个进程间通信的世界!

因为进程具有独立性,进程间通信的前提工作,先得让不同的进程,看到同一份资源,而这里的这

个资源则是网络!

一个进程可以关联多个端口号,但一个端口号不能关联多个进程(因为不符合端口号的定义)

TCP是可靠的,而UDP是不可靠的!但这里的可靠是中性词!!!

网络字节序

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节

因为主机有大端机和小端机之分,所以都按照TCP/IP规定的网络字节序来发送/接收数据,如果发

送主机是小端,就需要先将数据转成大端

网络通信的标准方式有很多种,基于ip的网络通信 AF_INT,原始套接字,域间套接字

udp——代码实现

服务器端

首先需要创建套接字

网络与套接字_第11张图片

如下图,是创建套接字的接口,参数domain,是套接字的种类,这里传AF_INET,参数type,是

套接字的类型,这里是udp,所以传SOCK_DGRAM,最后一个是协议种类,这里传0即可!返回

值,这个接口的创建与文件类似,失败就返回-1

网络与套接字_第12张图片

给该服务器绑定端口和ip

服务器的socket信息(ip+port),必须得被客户知道!一般服务器的port,必须是众所周知的

(不仅是被人,也可以是各种软件,app,浏览器等)的,而且轻易不能被改变!

首先设置一个端口,并创建一个结构体,同时需要将计算机中的变量(主机序列)转变为网络字节序

另外,需要解决两个问题,而inet_addr函数可以解决

1.需要将人识别的点分十进制,字符串风格IP地址([0-255].[0-255],[0-255].[0-255]),转化为4字节

整数IP

2.需要考虑大小端

因为云服务器,不允许用户直接bind公网IP,另外,实际正常编写的时候,我们也不会指明IP,如

果用上面那种方式,意味着只有发到该IP主机上面的数据才会交给你的网络进程,但是,一般服务

器可能有多张网卡,配置多个IP,我们需要的不是某个IP上面的数据,而是发送到该主机,该端口

的数据!!!所以采用下图这种方式更好!

然后对绑定失败的情况做一下处理,同时要对参数中的结构体做一下强转

网络与套接字_第13张图片如下图,是绑定端口和ip的接口,参数sockfd是创建套接字的返回值,addr是需要用户指定服务

器的相关socket信息,addrlen是传入结构体的大小,成功返回0,失败返回-1

 网络与套接字_第14张图片

提供服务

如下图,是读取客户端发送的信息

网络与套接字_第15张图片如下图,是读取信息的接口,参数sockfd是创建套接字的返回值,参数buf是缓冲区,参数len是

长度,参数flags是读取方式,设为0即可!下面两个参数,则是和服务端通信的客户端的信息!

返回值返回的是读取了多少个字节,如果读取错误则返回-1

给客户端回复信息

如下图,前面几个参数和返回值与上面的读取信息类似,最后两个参数则是表明给谁发送信息!

客户端

创建套接字

网络与套接字_第16张图片

客户端不需要显示的bind,首先,客户端必须也要有ip和port,但是,客户端不需要显示的bind!

一旦显示bind,就必须明确,client要和哪一个port关联,但client指明的端口号,在client端不一

定会有,可能被占用,被占用导致client无法使用,server要的是port必须明确,而且不变,但

client只要有就行!一般由OS自动给bind(),在client正常发送数据的时候,采用的是随机端口的

方式!

使用服务

数据从哪儿来

你要给谁发

网络与套接字_第17张图片

这里借助了命令行参数

网络与套接字_第18张图片

读取服务器端回复的信息!

网络与套接字_第19张图片

运行结果

如下图,是本地测试的结果,这里用上面的IP地址可能测试不了,所以可以用127.0.0.1来测试,当

然,如果想要两台主机之间通信,也可以去试试

代码调整

我们默认认为通信的数据是双方在互发字符串,所以还需要对上面的代码做一些调整

对服务器端

网络与套接字_第20张图片

对客户端

网络与套接字_第21张图片

注意:在网络通信中,只有报文大小,或者是字节流中字节的个数,没有C/C++字符串这样的概念

如下图,我们不把服务器端的端口给写死了,采取命令行参数的形式来输入端口,更加灵活!

应用场景

给服务器端发送命令,服务器端将执行后的结果发送给客户端

对服务器端

如下图,这个文件是一个管道文件,可以执行命令,底层原理是通过创建子进程来执行命令,执行

后的结果会放在文件中等你去读取!

网络与套接字_第22张图片

对客户端

运行结果 

网络与套接字_第23张图片

TCP——代码实现

对服务器端

前面的代码和udp就只有创建的套接字的类型不一样,其余都一样

网络与套接字_第24张图片

建立连接

tcp是面向连接的,udp是无连接的,所以tcp需要在通信前,需要建连接,才能通信

设置套接字是listen状态,本质是允许用户连接

如下图,参数sockfd是创建套接字的返回值,参数backlog暂时设置为5即可!

 网络与套接字_第25张图片

客户端主动建立连接,因为它需要服务,服务器端被动接受连接,因为它得提供服务

我们当前写的server,是一个周而复始的不间断的等待客户到来,要不断的给用户提供一个建立连

接的功能!

如下图,创建套接字和接收连接的接口的返回值都是文件描述符,但两者有很大区别,比如在一个

旅游区的其中一条商业街上,有许多饭店,张三就是其中一家饭店负责揽客的工作人员,每当他揽

到一次客,他就会喊服务员李四、王五等来给客人提供服务,自己则继续去揽客,所以张三就是前

监听套接字,而李四、王五则是提供IO服务的fd!

网络与套接字_第26张图片如果连接失败,继续去连接,就如同张三招揽的客人不来吃,那他也无法勉强,只能继续去招揽客

人!所以直接continue

网络与套接字_第27张图片

提供服务

如下图,因为tcp是面向字节流的,就如同文件一般,可以进行正常的读写 

网络与套接字_第28张图片

对客户端

前面的和udp只在创建的套接字的类型不一样,其余的都一样!

网络与套接字_第29张图片为发起连接准备参数

如下图,类似于memset,将结构体变量的所有字节置为0!

网络与套接字_第30张图片

发起连接

如下图,是发起连接的接口,成功返回0

 网络与套接字_第31张图片

业务请求

网络与套接字_第32张图片

运行结果

网络与套接字_第33张图片

上面的是单进程版本的,没什么用,因为只能建立一个连接,其它进程想连接时会被阻挡在死循环

外面,所以需要做一个多进程版本的!

把下图中的代码在死循环外面,在Linux中父进程忽略子进程的SIGCHLD信号,子进程会自动退

出释放资源

如下图,在死循环里面创建子进程,同时将提供服务的代码都放在函数ServiceIO中

网络与套接字_第34张图片   

如下图,如果想知道是谁连接到服务器,也可以打印它ip和port

将端口号从网络数据转化为主机数据!

将ip地址从一个四字节整形数据转化为字符串数据! 

网络与套接字_第35张图片 

version 2

如果不关闭不需要的文件描述符,会造成文件描述符泄漏!而因为子进程会继承父进程打开的fd,

以无论父子进程中的哪一个,强烈建议关闭不需要的fd!对于listen_sock,子进程也需要在服

务器提供服务前将其关闭,否则子进程可能会对其进行误读!

网络与套接字_第36张图片

version 2.1

父进程需要等待,因为子进程创建进程后退出,需要有人来清理它的资源,但不再是阻塞等待,而

是非阻塞等待,子进程创建出的进程,也就是孙子进程,成为了孤儿进程,孙子进程去提供务,

退出后由操作系统去清理它的资源!

网络与套接字_第37张图片

version 3

多线程版本,主线程打开的fd,新线程能看到,因为是共享的,所以listen_sock不能关!

缺点:创建线程,进程无上限;当客户链接来了,我们才给客户创建线程,就如同饭店来客人了,

你才去招厨师,服务员,效率太低了!

网络与套接字_第38张图片

version 4

为了解决version 3的两个缺点,可以采用线程池的方式,同时将服务代码放在task任务类的一个

员函数中

网络与套接字_第39张图片

总结

创建socket的过程,socket(),本质是打开文件——仅仅有系统相关的内容

bind(),struct,sockaddr_in -> ip,port,本质是ip+port和文件信息进行关联

listen(),本质是设置该socket文件的状态,允许别人来连接我

accept(),获取新链接到应用层,是以fd为代表的——链接:当有很多个链接连上我们的服务器

时,OS会存在大量的连接,就需要去管理,管理方式还是先描述,再组织。所谓的"连接",再OS

层面,本质上就是一个描述连接的结构体

read/write,本质就是进行网络通信,但对用户来说,相当于我们再进行正常的文件读写

close(fd),关闭文件,系统层面,释放曾经申请的文件资源、连接资源等;网络层面,通知对

方,我的连接已经关闭了!

connect(),本质是发起链接,在系统层面,就是构建一个请求报文发送过去;在网络层面,发起

tcp链接的三次握手!

close(),client和server,本质在网络层面就是在进行四次挥手!

如下图,我们前面写的代码都用到了一些系统调用接口,而这样做的目的是在从零开始编写应用

层,不是在使用应用层!

网络与套接字_第40张图片

你可能感兴趣的:(网络,网络协议,服务器,linux)