网络编程与socket套接字

传输层

PORT协议

port是一种接口,数据通过它在计算机和其他设备(比如打印机,鼠标,键盘或监视器)之间,网络之间和其他直接连接的计算机之间传递

TCP协议

​ 传输控制协议TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC定义

三次握手过程

​ TCP连接的建立时,双方需要经过三次握手,具体过程如下:

​ (1)第一次握手:Client进入SYN_SENT状态,发送一个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。

​ (2)第二次握手:Server端在收到SYN帧之后,会进入SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment

Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。

​ (3)第三次握手:Client在收到Server的第二次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment

Number)值被设置为Server端的SN序列号+1。还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。

​ (4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。

​ 四次挥手:

​ (1)第一次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置SequenceNumber(序列号)和Acknowledgment

Number(确认号)。发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。

​ (2)第二次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文,报文的Acknowledgment

Number(确认号)值为断开请求报文的Sequence Number

(序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。

​ (3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。

​ (4)第四次挥手:主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文,然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。

被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。

UDP协议

​ internet协议集支持一个无连接的传输协议,该协议成为用户数据报协议(UDP)为应用程序提供了一种无需建立连接就可以发送等装的ip数据报的方法就是UDP。

​ UDP协议的报头比TCP协议的报头简单很多。它的报头里面只有一个源端口,还有一个目标端口。这个和TCP里面一样,TCP里面也有一个源端口和目标端口。

​ 然后这个端口肯定也是给应用层去寻找对应的应用程序来进行数据的处理。所以,这个源目端口跟TCP是一样的。

​ 剩下的就是16位UDP长度和16位UDP校验和,这个看下就行,不需要太去纠结。

​ 那么这个里面有没有序号、确认号、标志位等?很明显,它没有。它的报头只有这四个部分,后面是数据部分了。

​ 既然没有序号、确认号、标志位,它是怎么建立连接的呢?没有这些东西是没办法建立连接的,所以UDP协议和TCP协议有很大的不一样。

​ UDP协议不会建立连接。客户端要去服务器那边去访问数据,基于UDP协议的话,是不会先去建立连接的。所以UDP是一个无连接的传输协议。这是和TCP协议最大的区别。

​ TCP的序号和确认号是为了确保数据包没有被丢,确保数据的完整性和正确性。既然UDP协议没有确认号和序号这2个部分,那它肯定是一个不可靠的协议。

​ 我发了一个包过去,你也不会给我回一个确认消息,所以我不知道你有没有收到。既然是不可靠的协议,用UDP去发送数据,可能会出现 丢包

​ 因为我发过去之后,你也没有给我发确认消息。我不知道你有没有收到,那我也不知道要不要给你重传。

​ 像TCP,如果它没有收到对方的确认号,等一段时间后,它会进行一个重传。这就是为什么要确认号的原因啊,没有收到对方的确认号,我就认为你没有收到前面的包。所以我给你重新发一个。

​ 那对于我们的UDP协议来说,你既然不会给我发确认,那你也没有连接可以依靠,所以你就是不可靠的。

​ 缺点:

​ UDP里面出现了丢包、出错,这些都不管,这些都是被允许的。

应用层

常见的策略和协议

FTP协议:(File Transfer Protocol,文件传输协议)

是 TCP/IP 协议组中的协议之一,FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。

HTTP协议:(超文本传输协议)

是联网上应用最为广泛的一种网络协议。

HTTPS 协议:(Secure Hypertext Transfer Protocol安全超文本传输协议)

是一个安全通信通道,它基于HTTP开发用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版,是使用TLS/SSL加密的HTTP协议。

TCP/IP协议:

TCP/IP 是基于 TCP 和 IP 这两个最初的协议之上的不同的通信协议的大的集合。

TCP:传输控制协议,传输效率低,可靠性强

UDP协议:

​ UDP:用户数据报协议,适用于传输可靠性要求不高,数据量小的数据

DHCP协议:(动态主机配置协议):

发现协议中的引导文件名、空终止符、属名或者空,DHCP供应协议中的受限目录路径名 Options –可选参数字段,参考定义选择列表中的选择文件。

POP3协议:(全名为"Post Office Protocol - Version 3",即"邮局协议版本3")

是TCP/IP协议族中的一员,本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。提供了ssl加密的POP3协议被称为POP3S。

DNS协议:(域名解析协议)

DNS是一种用以将域名转换为IP地址的Internet服务

SMTP协议:(简单邮件传送协议)

Client/Server模式,面向连接

SNMP协议:(简单网络管理协议)

SNMP模型的4个组件:被管理结点、管理站、管理信息、管理协议

SNMP代理:运行SNMP管理进程的被管理结点

对象:描述设备的变量

管理信息库(MIB):保存所有对象的数据结构

Telnet协议:(远程登录协议)

客户服务器模式,能适应许多计算机和操作系统的差异,网络虚拟终端NVT的意义

TFTP:(简单文件传送协议)

客户服务器模式,使用UDP数据报,只支持文件传输,不支持交互,TFTP代码占内存小

socket套接字

​ 所谓套接字(socket),就是网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层利用网络协议交换数据的机制。从所处的地位来讲。套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,使应用程序与网络协议栈进行交互的接口。

​ 基于文件类型的套接字家族:AF_UNIX

​ 基于网络类型的套接字家族:AF_INETN

服务器端代码:
import socket # 导入网络编程模块
# 创建一个socket对象
server = socket.socket() # 括号内什么都不写 其实就是默认基于互联网的TCP套接字
# 绑定一个固定的地址
server.bind(('127.0.0.1',8080)) # 127.0.0.1本地回环地址,只允许自己访问自己的机器
# 为什么要两个括号,因为只要元组
# 半连接池
server.listen(5)
# 开业大酬宾,上门送福利,白开水管饱
sock,address = server.accept()
print(sock,address) # sock是双向通道 address是客户端地址
# 数据交互
sock.send(b'hei my girl') # 向客户端发送消息
data = sock.recv(1024) # 接受客户端所返回的数据 1024个bytes
print(data) # 打印出来
# 断开连接
sock.close() # 断开连接
server.close() # 关闭服务器 客户端:
import socket # 导入网络编程模块
# 产生一个网络编程对象
client = socket.socket()
# 连接服务器(拼接服务器的ip和port)
client.connect(('127.0.0.1',8080))
# 进行数据交互
data = client.recv(1024) # 接收服务器的数据
print(data) # 打印数据
client.send(b'hei my father') # 向服务器发送数据
client.close() # 关闭

代码优化

send与recv

​ 客户端与服务端不能同时执行只有有一个执行另一个接受

消息自定义

​ input获取用户所输入的数据即可但是获得的时候必须要进行编码和解码

循环通道

​ 给程序添加循环功能就可以实现两方互相循环沟通

服务端的持续提供服务

​ 服务端不会因为客户端的断链而报错,如果客户端有报错的可能的话那么我们阔以尝试通过异常捕获的方式来预防报错,等待下一个会话

消息不能为空

​ 判断是否为空,如果是则需要重新输入

无限畅聊版,服务端:
import socket # 导入网络编程模块
# 创建一个socket对象
server = socket.socket() # 括号内什么都不写 其实就是默认基于互联网的TCP套接字
# 绑定一个固定的地址
server.bind(('127.0.0.1',8080)) # 127.0.0.1本地回环地址,只允许自己访问自己的机器
# 为什么要两个括号,因为只要元组
# 半连接池
server.listen(5)
# 开业大酬宾,上门送福利,白开水管饱
while True:
sock,address = server.accept()
print(sock,address) # sock是双向通道 address是客户端地址
while True:
try:
# 数据交互
res = input('请输入您需要对话>>>:').strip()
if len(res) == 0:
continue
sock.send(res.encode('utf-8')) # 向客户端发送消息
data = sock.recv(1024) # 接受客户端所返回的数据 1024个bytes
if len(data) ==0:
break
print(data.decode('utf-8')) # 打印出来
except ConnectionResetError:
sock.close()
break
无限畅聊版,客户端:
import socket # 导入网络编程模块
# 产生一个网络编程对象
client = socket.socket()
# 连接服务器(拼接服务器的ip和port)
client.connect(('127.0.0.1',8080))
# 进行数据交互 while True:
res = input('请输入您需要对话>>>:').strip()
if len(res) == 0:
continue
client.send(res.encode('utf-8')) # 向服务器发送数据
data = client.recv(1024) # 接收服务器的数据
if len(data) == 0:
break
print(data.decode('utf-8')) # 打印数据

半连接池

半连接的两种状况

​ 客户端无法返回ack信息

​ 服务器来不及处理客户端的连接请求

server.listen(5)

​ 主要是为了做缓冲,当服务器在响应了客户端的第一次请求后会进入等待状态,会等客户端发送的ack信息,这时候这个连接池就被称为半连接池。

​ 搬来你劫持其实就是一个容器,系统会自动将半连接放入容器中,阔以避免半连接多而保证资源耗光

黏包问题

什么是黏包问题

​ 发送方发送的若干包数据到接收方时粘成一包,从接受缓冲区来看就是后一包数据的头紧接着前一包数据的尾。

黏包问题出现的原因

​ 若连续几次发送的数据都很小tcp就会根据优化算法把这些数据整合成一包后一次性发送,这样接收方就收到了黏包数据。

​ 接收方会先把收到的数据放在系统接受缓冲区,用户进程从该缓冲区读取数据,若下一包数据到达前时一包数据尚未被用户进程取走,则下一包数据进入缓冲区时就会到钱一包数据之后,而用户进程根据预先设定的缓冲区大小从系统中读取数据这样便一辞去了很多包。

解决方法--封包和解包

​ 发送方在发送数据的包前,加入数据长度,将数据包的结构变成[dataLen|data]的结构再进行发送。而接收方通过解析这种结构,从而将多个数据包聚合形成的黏包分解成一个个独立的数据包。

​ struct模块无论数据长度是多少,都可以帮你打包成固定的长度,然后基于该固定长度还可以反向解析出数据真是的长度,但是struct模块在遇到数据量特别大的数字时那么就没有办法打包

思路:

​ 1.先将真实数据的长度制作成固定的长度 4

​ 2.先发送固定长度的报头

​ 3.再发送真实的数据

​ 1.先接受固定长度报头 4

​ 2.再跟还有报头解压出真实长度

​ 3.根据真实长度接收即可

import struct  # 导入模块

# 设置需要查看或者获取需要查看的数据
data = '这不是瞎搞嘛,选课系统都还没整明白' # 设置
print(len(data)) # 获取这个数据的长度 17
res = struct.pack('i',len(data)) # 将原来那个数据打包 第一个值默认使用i
print(len(res)) # 打印打包好后的值是什么长度 4
res = struct.unpack('i',res) # 将打包好的数据进行解压
print(res[0]) # 打印长度 17

网络编程与socket套接字的更多相关文章

  1. linux网络编程-(socket套接字编程UDP传输)

    今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...

  2. 【网络编程】Socket套接字网络编程模型

    一.Linux网络模型 -- Socket套接字编程 图片:Socket 抽象层 Socket编程--不同协议,统一接口 Socket的实质就是一个接口, 利用该接口,用户在使用不同的网络协议时,操作 ...

  3. 02网络编程( socket套接字+TCP粘包 )

    目录 02 网络编程 一.socket套接字编程 二.简易代码模板 2.1 服务端 2.2 客户端 三.通信循环及代码优化 四.黏包现象 五.struct模块 六.简易版本报头 七.上传文件数据 * ...

  4. 31_网络编程(Socket套接字编程)_讲义

    今日内容介绍 1.网络三要素及传输协议 2.实现UDP协议的发送端和接收端 3.实现TCP协议的客户端和服务器 4.TCP上传文件案例 01网络模型 *A:网络模型 TCP/IP协议中的四层分别是应用 ...

  5. python网络编程:socket套接字

    一.socket 二.TCP服务器 三.TCP客户端 四.UDP服务器 五.UDP客户端 六.聊天的客户端 七.聊天的服务器 一.socket """ 学习网络编程 其实 ...

  6. Unix网络编程--卷一:套接字联网API

    UNIX网络编程--卷一:套接字联网API 本书面对的读者是那些希望自己编写的程序能够使用成为套接字(socket)的API进行彼此通信的人. 目录: 0.准备环境 1.简介 2.传输层:TCP.UD ...

  7. VC++学习之网络编程中的套接字

    VC++学习之网络编程中的套接字 套接字,简单的说就是通信双方的一种约定,用套接字中的相关函数来完成通信过程.应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问 ...

  8. [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序]

    [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序] 为何学习socket套接字一定要先学习互联网协议: 1.首先:要想开发一款自己的C/S架构软件,就必须掌握socket ...

  9. Linux网络编程:原始套接字简介

    Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...

随机推荐

  1. vue传值的几种方式

    props:适用于 父组件 ==> 子组件 通信 由父组件传值子组件在props中接收即可: (由父组件给子组件传递 函数类型 的props可实现 子组件 ==> 父组件 传递数据,较为繁 ...

  2. JS:构造函数

    定义:在JavaScript中,用new关键字来调用的函数,称为构造函数,构造函数首字母一般大写. 理解: 构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象. 创建对象, ...

  3. spring源码解析:元注解功能的实现

    前言 众所周知,spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置.平时在使用的时候,点开一些常见的等注解,会发现往往在一 ...

  4. pyenv安装及使用教程

    pyenv安装及使用教程 pyenv 安装 git clone https://github.com/pyenv/pyenv.git ~/.pyenv # 编辑 bashrc vim ~/.bashr ...

  5. 自然常数e的由来以及计算机为什么是二进制

    背景 ​ 昨晚我在看一本书,叫<数学极客>,看到第六章<e:不自然的自然数>,这个数最早开始接触应该是高一的时候,那时候问老师,这个数是怎么来的,老实说,和圆周率一样,是一个常 ...

  6. 我大抵是卷上瘾了,横竖睡不着!竟让一个Bug,搞我两次!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言:一个Bug 没想到一个Bug,竟然搞我两次! 我大抵是卷上瘾了,横竖都睡不着,坐起来 ...

  7. 获取请求体数据 POST

    POST获取请求体 请求体中封装了 POST请求的请求参数 获取流对象 再从流对象中那数据 一种字节流 一种字符流 BufferedReader getReader()获取字符输入流 只能操作字符 S ...

  8. 端口被占用的问题解决 Web server failed to start. Port ×× was already in use

    出现此问题是端口被占用了,只需要关闭正在使用的端口就行 解决思路: 1.在服务器中更改port端口号,改为不冲突,没有被占用的端口. 2.找出被占用的端口,结束被占用的端口 解决结束被占用的端口的方法 ...

  9. 当在命令行输入"pip install xxx"

    当输入"pip install xxx"时发生了什么 不知道你在下载一些包的时候有没有什么疑惑,输入了"pip install xxx" ,系统是如何找到对应的 ...

  10. Mysql数据库的默认引擎

    InnoDB的优势在于提供了良好的事务处理.崩溃修复能力和并发控制.缺点是读写效率较差,占用的数据空间相对较大. ①InnoDB:支持事务处理,支持外键,支持崩溃修复能力和并发控制.如果需要对事务的完 ...