python网络编程 - tcp

时间:2021-07-10 15:47:15

网络编程

低级别的网络服务

高级别的网络服务

socket又称“套接字”,应用程序通过“套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

tcp

传输控制协议(Transfer Control Protocol)
 

tcp优劣势:

1、稳定
2、相对于udp而言,要慢一些(几乎可以忽略不计)
3、web服务器都是使用的tcp
 
udp优劣势
1、不稳定(几乎可以忽略不计,但是总存在隐患)
2、比tcp要快一些
 
tcp的原理就类似生活中的“打电话”,先要双方接通,才能通话
 python网络编程 - tcp
udp的原理就类似生活中的“写信”,信寄出去,就不管了,能不能到达也是不能保证的
 python网络编程 - tcp

TCP三次握手

当需要使用tcp协议的时候(如http,其底层就是tcp协议)
python网络编程 - tcp

第一次握手

python网络编程 - tcp

客户端先发一个“syn”的数据包给服务器。数据包中包括参数:序列号(SEQUENCE  NUM),假设为J。上述为0.

python网络编程 - tcp

第二次握手

python网络编程 - tcp 
服务器收到“syn”的数据包后,给客户端发一个“syn+ack”的数据包。数据包中包括参数,,分别是:序列号(SEQUENCE NUM,假设为K)确认号(ACK NUM,假设为J+1),上述分别是“0”和“1”。
客户端将之前发送的数据包中的J值加1,再将收到的服务器发送过来的数据包中的“J+1”进行比较,如果一样,说明第二次连接成功。
 

第三次握手

python网络编程 - tcp

客户端给服务端发一个“ack”的数据包。

序列号(SEQUENCE NUM,假设为J+1):上述为1
确认号(ACK NUM,假设为K+1):上述为1
服务器将之前发送的数据包中的K值加1,然后将收到的客户端发送过来的数据包中的“K+1”进行比较,如果一样,说明第三次连接成功
 
python网络编程 - tcp
如此,三次握手建立成功。
 

HTTP请求过程

1、客户端向服务器发送“syn”请求包,建立第一次握手
2、服务器向客户端发送“syn+ack”数据包,建立第二次握手
3、客户端想服务器发送“ack”的数据包,建立第三次握手;紧随第三次握手的数据包后面,客户端紧接着向服务器发送“http”的数据包
4、服务器向客户端发送之前的“http”的确认包,并紧随着发送“http”的响应数据包,
5、客户端接收到“http”包后,再向服务器发送“ack”的确认包,告诉服务器数据包收到了
 
tcp协议中,不管是客户端还是服务器,只要收到了数据,就一定会发送一个ack确认包给发送方。这也就导致了tcp比udp稳定的原因。
 

TCP四次挥手

python网络编程 - tcp

TCP的十种状态

python网络编程 - tcp

当一端收到一个FIN后,内核让read返回0来通知应用层另一端已经终止了向本段的数据传送
发送FIN通常是应用层对socket进行关闭的结果
python网络编程 - tcp

TTL

Time  To  Live,IP包被路由器丢弃之前允许通过的最大网段数量。
 
虽然TTL从字面上翻译,是可以存活的时间,但实际上TTL是IP数据包在计算机网络中可以转发的最大跳数。TTL字段由IP数据包的发送者设置,在IP数据包从源到目的的整个转发路径上,每经过一个路由器,路由器都会修改这个TTL字段值,具体的做法是把该TTL的值减1,然后再将IP包转发出去。如果在IP包到达目的IP之前,TTL减少为0,路由器将会丢弃收到的TTL=0的IP包并向IP包的发送者发送 ICMP time exceeded消息。
TTL的主要作用是避免IP包在网络中的无限循环和收发,节省了网络资源,并能使IP包的发送者能收到告警消息。
 
TTL 是由发送主机设置的,以防止数据包不断在IP互联网络上永不终止地循环。转发IP数据包时,要求路由器至少将 TTL 减小 1。
 

2MSL

MSL:Maximum Segment Lifetime,报文最大生存时间,一个数据包在网络上传输所用的最大的时间,称为msl,一般为1~2分钟。
python网络编程 - tcp

TCP的最后一次挥手,怎么能保证服务器端一定会收到呢?

如果在一个msl时间内,服务端没有收到“最后一次挥手”,那么服务端会再次发一个“FIN”数据包给客户端,这一段时间最长又是一个msl,总的加起来就是2msl,在此期间,如果客户端接收到了“FIN”数据包,那么会再发一次“ACK”给服务器;相反如果在2msl时间后,还没有收到服务器的“FIN”数据包的话,说明“最后一次挥手”成功。
注:
1、在此等待的2msl期间内,会占用端口,端口不会被释放。
2、主动关闭的一段并非一定是客户端,也可以是服务端。所以一旦是服务端主动关闭,由于服务端是绑定了端口的,程序就无法立马运行了,因为在2msl期间内,端口还是被占用的。当然,客户端无所谓,反正是动态分配端口。
 

长连接和短链接

TCP在真正的读写操作之前,server与client之间必须建立一个连接,
当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,
连接的建立通过三次握手,释放则需要四次握手,
所以说每个连接的建立都是需要资源消耗和时间消耗的。

TCP通信的整个过程

python网络编程 - tcp

1. TCP短连接

模拟一种TCP短连接的情况:
l client 向 server 发起连接请求
l server 接到请求,双方建立连接
l client 向 server 发送消息
l server 回应 client
l 一次读写完成,此时双方任何一个都可以发起 close 操作
在第 步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。
从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作!

2. TCP长连接

再模拟一种长连接的情况:
l client 向 server 发起连接
l server 接到请求,双方建立连接
l client 向 server 发送消息
l server 回应 client
l 一次读写完成,连接不关闭
l 后续读写操作...
l 长时间操作之后client发起关闭请求

3. TCP长/短连接操作过程

3.1 短连接的操作步骤是:

建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接
python网络编程 - tcp

3.2 长连接的操作步骤是:

建立连接——数据传输...(保持连接)...数据传输——关闭连接
python网络编程 - tcp

4. TCP长/短连接的优点和缺点

l 长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。
l client与server之间的连接如果一直不关闭的话,会存在一个问题:
随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,
如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损(如LOL中的挂机,一段时间后就会断开连接);
如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,
这样可以完全避免某个蛋疼的客户端连累后端服务。
l 短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。
l 但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

5. TCP长/短连接的应用场景

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,
再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,
再次处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,
而且频繁的socket 创建也是对资源的浪费。
 
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,
如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,
那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

python代码实现

服务端

流程

1、socket创建一个套接字
2、bind绑定ip和port
3、listen 使套接字变为可以被动链接(默认创建的套接字是主动去链接别人的)
4、accept 等待客户端的链接(accept和客户端的connect是一对一的关系,服务器的accept只响应客户端的connect)
5、send/recv 发送和接收数据(recv和send是一对多的关系,服务器的recv响应客户端的send,也响应客户端socket的close;反之亦然)。有一个好玩的事情,当某一端send空数据的时候,另一端recv并没有响应,而当close的时候,recv却是能响应的,不过数据为空,猜测是send不能发送空数据
 

代码

# coding:utf-8

import socket
import config
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)-15s %(levelname)s %(filename)s %(lineno)d %(message)s",) def main():
# 创建套接字
# family:套接字家族,AF_UNIX或者AF_INET(默认)
# type:套接字类型,面向连接的还是面向非连接的,SOCK_STREAM(默认)或者SOCK_DGRAM
# protocol:一般不填默认为0
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 重用ip和port,防止报错
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定ip和port
sk.bind((config.host, config.port)) # 使套接字变为被动连接,最多可接收给定参数的客户端的连接(默认套接字是主动去连接别人的)
sk.listen(5) nsk, addr = sk.accept()
logging.info("client connected! socket={}, addr={}".format(nsk, addr)) data = nsk.recv(1024)
if len(data) == 0:
# 客户端关闭了连接
nsk.close()
else:
nsk.send("thank you".encode("utf-8")) data2 = nsk.recv(1024)
print(data2) if __name__ == '__main__':
main()

客户端

代码实现的客户端

# coding:utf-8

import socket
import config def main():
# 创建socket
sk = socket.socket()
print("client connected! socket={}".format(sk)) # 连接服务器
sk.connect((config.host, config.port)) # 发送数据到服务器
sk.send(b"") data = sk.recv(1024)
if len(data) == 0:
# 服务器端主动断开连接
sk.close()
else:
print(data)
sk.close() if __name__ == '__main__':
main()

浏览器客户端

使用postman模拟浏览器请求(get/post/put/delete都可以),修改服务器代码如下:

def main():
# 创建套接字
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 重用ip和port,防止报错
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定ip和port
sk.bind((config.host, config.port)) # 使套接字变为被动连接,最多可接收给定参数的客户端的连接(默认套接字是主动去连接别人的)
sk.listen(5) nsk, addr = sk.accept()
logging.info("client connected! socket={}, addr={}".format(nsk, addr)) data = nsk.recv(1024)
if len(data) == 0:
# 客户端关闭了连接
nsk.close()
else:
nsk.send("thank you".encode("utf-8")) data2 = nsk.recv(1024)
print(data2)

调试发现,一次浏览器的请求,其实做了四个操作,分别是:

  • 创建套接字:sk = socket.socket()
  • 连接服务器:sk.connect((ip, port))
  • 发送消息:socket.send(请求头)。请求头如:b'POST / HTTP/1.1\r\nUser-Agent: PostmanRuntime/7.17.1\r\nAccept: */*\r\nCache-Control: no-cache\r\nPostman-Token: 016ca998-9f45-4ba5-949b-07a51ea0f3e9\r\nHost: 127.0.0.1:5002\r\nAccept-Encoding: gzip, deflate\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n'
  • 关闭连接:sk.close()。通过调试发现data2的数据为空字符串,说明客户端关闭了连接

python网络编程 - tcp的更多相关文章

  1. python 网络编程 TCP/IP socket UDP

    TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...

  2. python 网络编程 -- Tcp协议

    Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 客户端 大多数连接都是可靠 ...

  3. python网络编程-TCP协议中的三次握手和四次挥手(图解)

    建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资 ...

  4. python 网络编程 tcp和udp 协议

    1. 网络通信协议 osi七层,tcp\ip五层 tcp\ip五层 arp协议:通过IP地址找到mac地址 2.tcp和udp的区别 tcp协议:面向连接,消息可靠,相对udp来讲,传输速度慢,消息是 ...

  5. python网络编程--TCP连接的三次握手(三报文握手)与四次挥手

    一.TCP连接 运输连接有三个阶段: 连接建立.数据传送和连接释放. 在TCP连接建立过程中要解决以下三个问题: 1,要使每一方能够确知对方的存在. 2.要允许双方协商一些参数(如最大窗口之,是否使用 ...

  6. [Python 网络编程] TCP Client (四)

    TCP Client 客户端编程步骤: 创建socket对象 连接到服务端的ip和port,connect()方法 传输数据 使用send.recv方法发送.接收数据 关闭连接,释放资源 最简单的客户 ...

  7. [Python 网络编程] TCP编程/群聊服务端 (二)

    群聊服务端 需求分析: 1. 群聊服务端需支持启动和停止(清理资源); 2. 可以接收客户端的连接; 接收客户端发来的数据 3. 可以将每条信息分发到所有客户端 1) 先搭架子: #TCP Serve ...

  8. [Python 网络编程] TCP、简单socket模拟ssh (一)

    OSI七层模型(Open System Interconnection,开放式系统互联) 应用层 网络进程访问应用层: 为应用程序进程(例如:电子邮件.文件传输和终端仿真)提供网络服务: 提供用户身份 ...

  9. python网络编程--TCP客户端的开发

    #导入socket模块 2 import socket 3 #参数说明 4 """ 5 socket类的介绍 6 创建客户端socket对象 7 socket.socke ...

随机推荐

  1. CommandPattern

    /** * 命令模式 * @author TMAC-J * 将调用者和接受者分离 * 可以将一组命令组合在一起,适合很多命令的时候 */ public class CommandPattern { i ...

  2. jquery ajax的error错误信息

    项目开发中ajax的异常处理起来算是比较头疼的,因为是异步请求,所以即使ajax异常程序依然会继续执行,导致找ajax的异常比较麻烦. 今天处理ajax异常时搜到一篇文章,提到error可以返回aja ...

  3. Linux常见命令

    du -sh 查看当前文件夹大小 tail -f /var/log/nginx/access.log 查看日志 vsFTPd Linux上面的ftp df -lh 查看磁盘 df -i 查看inode ...

  4. Javascript起源...

    Javascript的设计思路是这样的: (1)借鉴C语言的基本语法: (2)借鉴Java语言的数据类型和内存管理: (3)借鉴Scheme语言,将函数提升到"第一等公民"(fir ...

  5. 第四十一篇、Masonry利用Block实现链式编程

    一直都觉得使用Masonry的时候语法特别优雅,很早的时候就想尝试下怎么实现, 一直都没弄明白,直到最近看见一篇叫block实现链式编程的 1.方法的返回类型是代码块 >代码块的返回类型是该类的 ...

  6. c# const与readonly 关键字的比较

    C#中,const 与readonly是两个比较有用的关键字.const 与 readonly 定义的数据成员在初始化都不能再改变. 比如定义了 public class MathUtitlity   ...

  7. Alamofire源码解读系列(十)之序列化(ResponseSerialization)

    本篇主要讲解Alamofire中如何把服务器返回的数据序列化 前言 和前边的文章不同, 在这一篇中,我想从程序的设计层次上解读ResponseSerialization这个文件.更直观的去探讨该功能是 ...

  8. 【kafka】生产者速度测试

    非常有用的参考博客:http://blog.csdn.net/qq_33160722/article/details/52903380 pykafka文档:http://pykafka.readthe ...

  9. luogu1072 [NOIp2009]Hankson的趣味题 (数学+STL::set)

    一个JSB做法 由$\frac{x*b0}{gcd(x,b0)}=b1$,可得$\frac{x}{gcd(x,b0)}=\frac{b1}{b0}$ 设$b2=\frac{b1}{b0}$ 所以对$b ...

  10. JavaScript 第十章总结:first class functions

    前言 这一章的内容是 advanced knowledge and use of functions. 讲了关于 function 的使用的一些特殊的方面. function expression 的 ...