python学习网络编程
python网络编程
一、软件开发架构
两种架构都是从应用层面进行划分:
C/S(client/server)架构 客户端/服务器 模式
B/S(Browser/server)架构 浏览器/服务器 模式
优势:统一了应用程序的接口
注:浏览器本身也是客户端,所有B/S是C/S从属关系
二、网络基础
mac :网卡物理地址、12位16进制组成、唯一性 【保证请求是某台机器上发起的】
ip:互联网地址协议,分4段,32位的二进制,以4段十进制组成(ipv4)【方便查找机器】
端口:操作系统为每一个应用程序分配一个端口号(端口号范围为:0~65535,其中0~1023系统内置绑定一些常用服务如20,21为ftp,22为ssh等,不允许占用)
ip + 端口号: 唯一确定某一个电脑上的某一个程序
arp协议:地址解析协议(tcp/ip协议),将ip地址解析为mac地址
网段: 通过ip地址与子网掩码进行与运算确认是否为同一网络
路由器:路由表记录可以到达的网络目的地址范围和如何到达的路由信息,帮助选择最优即最快到达目的地路劲,不同网段之间的通信,网关
协议:由多人或正规组织制定的一种规则,约定,计算机网络协议是计算机双方共同遵循的一组约定。
tcp协议:传输层、字节流传输、安全可靠通信方式(发送请求会确认对方是否接到),面向连接 ,如传文件
udp协议:传输层、字节流传输、不安全、不可靠的通信方式(只管发送,不管是否接到),快,如qq,视频、音频播放
tcp三次握手:(必须是客户端发出)建立全双工通信
1、客户端向服务端发送请求连接
2、服务端接收回复客户端两个标识,一个syn表示服务端接收到请求,一个ack表示服务端在做连接准备工作
3、客户端收到服务端的回复,客户端准备连接的所有资源,开始进行连接发送给服务器一个ack表示客户端的连接准备工作已经完成。(此时表示客户端和服务端可以相互连接了)
哪句代码体现了三次握手?
回答: 服务器端的accept,客户端connect
tcp四次挥手:(双方都可以发出结束连接)
1、AB会话完成后,A向B发送断开连接请求,
2、B收到A信息后回复,收到断开请求,准备断开的事宜;
3、A收到B断开连接信息,等待B发送确认断开连接
4、B准备完成后,给A发送消息告知,没有数据要继续发送了,可以断开连接了
5、A收到B信息后,准备断开连接,回收资源,回复确认断开连接,等待2mls,A断开连接
哪句代码体现了四次挥手?
回答: close()
1 三次握手: 2 A:“喂,你听得到吗?”A->SYN_SEND 3 4 B:“我听得到呀,你听得到我吗?”应答与请求同时发出 B->SYN_RCVD | A->ESTABLISHED 5 6 A:“我能听到你,今天balabala……”B->ESTABLISHED 7 8 四次挥手: 9 A:“喂,我不说了。”A->FIN_WAIT1 10 11 B:“我知道了。等下,上一句还没说完。Balabala…..”B->CLOSE_WAIT | A->FIN_WAIT2 12 13 B:”好了,说完了,我也不说了。”B->LAST_ACK 14 15 A:”我知道了。”A->TIME_WAIT | B->CLOSED 16 17 A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSED https://blog.csdn.net/qzcsu/article/details/72861891
OSI 七层协议 Tcp/ip协议层 协议
应用层 应用层 .py ,http协议,ftp协议,smtp,POP3,imap --------》和程序
会话层 socket
表示层
传输层 传输层 tcp/udp、四层路由器 -------》和端口
网络层 网络层 ip协议、路由器、三层交换机 ------》和ip
数据链路层 数据链路层 arp协议、网卡、交换机 -------》和mac
物理层 物理层 网线 -------》和0101打交道的
为什么有粘包?
因为当发送端缓存区长度大于网络最大传输长度,就会执行tcp拆包机制,将数据包拆成多个小包发送,
接收端会执行tcp合包机制(nagle算法)将多次间隔较小且数据量小的数据合成一个包接收,这样接收端就没法分辨了,从而造成粘包现象,即数据流的通信是无消息保护边界的。
解决粘包的根本问题?
在接收数据之前,先得到要接收的数据长度。
为什么只有tcp有粘包现象,而udp没有?
因为tcp是面向数据流、面向连接的通信,发送和接收消息不能为空,udp是无连接的,面向数据包的通信,提供高效传输,不采用包的合并优化算法。
三、socket套接字
定义:位于tcp/ip协议分层的 传输层与应用层之间、Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
python通过socket模块进行程序之间的通信。
socket分两种编码流程:tcp 和 udp
Tcp编码流程:
前提:tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
1 import socket 2 '''server端''' 3 sock = socket.socket(type=socket.SOCK_STREAM) # 创建socket,(买了个手机) 4 sock.bind(('127.0.0.1', 9090)) # 绑定端口,(装上电话卡) 5 sock.listen() # 创建监听,(告诉亲友电话号码) 6 conn, add = sock.accept() # 等待客户端连接,(等待来电) 7 msg_r = conn.recv(2048) # 接收客户端信息 (聊天) 8 msg_s = conn.send('say some thing to client') # 向客户端发送信息 9 conn.close() # 断开客户端连接,通话结束 10 sock.close() # 断开套接字连接,关机 11 12 '''client端''' 13 sk = socket.socket() 14 sk.connect(('server IP', 'server_port')) 15 sk.send('say some thing to server') 16 msg_r = sk.recv(2048) # 接收服务端来信 17 sk.close()
服务端:
1、创建套接字socket
2、绑定端口bind
3、监听客户端请求listen
4、接受客户端连接accept
5、接收/发送信息recv/send
6、关闭客户端连接conn.close
7、关闭套接字close socket
客户端:
1、创建套接字
2、连接服务器connect
3、发送/接收信息send/recv
4、关闭套接字
代码实现:
Server:
Client:
Udp编码流程:
注意:udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接
服务器端:
1、创建套接字
2、绑定端口
3、接收/发送消息
4、关闭套接字
客户端:
1、创建套接字
2、发送/接收消息
3、关闭套接字
代码实现:(1对多,循环交互)
Server:
Client:
Tcp并发编程-socketserver
正常情况下,tcp协议的socket server,同一时刻只能处理一个请求
使用socketserver.ThreadingTCPServer就可以让你的tcp协议的server端同时接受多个客户端的请求
基于socket实现的
serverforever方法
启动了一个socket的server端
socketserver启动之后就会一直对外提供服务
一个client服务结束了之后,socketserver会帮助你conn.close
但是sk对象永远不会停止服务
socket聊天并发实现
采用多进程的知识点 来解决原生socket同一时刻只能和一个conn通信的弊端
1 '''能一对多的本质是能接收新客户端的连接与服务端与其他客户端的交互不受打扰 2 开多进程 3 ''' 4 import socket 5 from multiprocessing import Process 6 7 ip_port = ('127.0.0.1', 9999) 8 9 10 def talk(conn, ): 11 """ 12 交互放在子进程中 13 :param conn: 14 :return: 15 """ 16 try: 17 while 1: 18 conn.send('中国'.encode('utf-8')) 19 msg = conn.recv(1024) 20 if not msg: break 21 print(conn.recv(1024)) 22 finally: 23 conn.close() 24 25 26 if __name__ == '__main__': 27 sk = socket.socket() 28 sk.bind(ip_port) 29 sk.listen() 30 while 1: 31 conn, addr = sk.accept() 32 Process(target=talk, args=(conn,)).start()
1 import socket 2 import os 3 4 ip_port = ('127.0.0.1', 9999) 5 sk = socket.socket() 6 sk.connect_ex(ip_port) 7 8 try: 9 while 1: 10 msg = sk.recv(1024).decode('utf-8') 11 if not msg: continue 12 print(sk.recv(1024).decode('utf-8')) 13 sk.send(str(os.getpid()).encode('utf-8')) 14 finally: 15 sk.close()