Python-网络编程
1.TCP/IP简介:
互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
8572700-bbfc3d88d8817a2e.pngTCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
一个IP包除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个IP包来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
TCP/IP协议簇,包括应用层协议(http 超文本传输协议、DNS域名解析、文本传输协议TFTP等)、网络层协议(IP协议、ICMP控制信息协议、ARP地址解析协议、RARP反向地址协议)、传输层(TCP协议、UDP协议)
TCP:
- 面向连接的,通过三次握手建立连接,通过四次挥手甘比连接
- 传输可靠,通过TCP连接传送的数据,无差别、不丢失、不重复,且按序到达。通过序列号、校验和、确认应答信号、重发控制、连接管理、窗口控制、流量控制、阻塞控制实现可靠性。
- 全双工通信
- 传输速度慢、占用系统资源较多
- 一对一通信
UDP:
- 面向无连接的,传输数据前不需要与对方建立连接,对接收的数据不用发送确认信号。只要目的地址,端口号,源地址,端口号确定了,就可以直接发送信息报文,并且不需要一定能收到或者完整的数据。它仅仅提供了校验和机制来保障报文是否完整,若校验失败,则直接将报文丢弃,不做任何处理。
- 不可靠传输,传输过程中如果出现丢包,不负责重发,数据包到达顺序乱掉也没有纠正顺序的功能,也没有流程控制避免网络拥塞的行为。
- 传输速度快,占用系统资源少
- 可以一对一,一对多,多对一通信
- 较好的实时性
TCP/UDP使用场景:
TCP:http文件传输协议、文件的上传下载、发送邮件
UDP:QQ语音、QQ视频、微信
2.基于TCP的socket聊天室实现
8572700-be62f1c1f6544e5f.png服务端实现:
import socket
import threading
class chatRoom:
def __init__(self,ip='127.0.0.1',port=9999):
self.addr = (ip,port)
self.server = socket.socket()
self.clients = {}
def start(self):
self.server.bind(self.addr)
self.server.listen()
# 子线程处理客户端连接,防止阻塞主线程
threading.Thread(target=self.accept(),name='accept').start()
def accept(self):
# 监听客户端的连接
while True:
# 没有客户端连接阻塞线程,有连接返回新的socket和客户端ip
sock,ip = self.server.accept()
# 把socket 装入容器
self.clients[ip] = sock
# 监听到连接创建一个socket,与客户端交互,子线程循环监听数据的接收
threading.Thread(target=self.recive(sock),name='recive').start()
def recive(self,sock:socket.socket):
while True:
try:
# data 是字节
data = sock.recv(1024)
print(data.decode())
except Exception as e:
print('客户端异常,连接失败')
data = b'quit'
# 客户端输入quit,断开socket链接
if data == b'quit':
# 根据key,移除想要退出群聊的客户端
self.clients.pop(sock.getpeername())
socket.close()
break
# 收到数据发送到每一个客户端
for sock in self.clients.values():
# sock.getpeername() 获取远程客户端地址和端口元组形式
sock.send('你是猪吗{}'.format(sock.getpeername()).encode('utf-8'))
def stop(self):
for sock in self.clients.values():
sock.close()
self.server.close()
if __name__ == '__main__':
chatServer = chatRoom()
chatServer.start()
客户端实现:
import socket
class client:
def __init__(self):
self.socket = socket.socket()
self.socket.connect(('127.0.0.1',9999))
def sendMsg(self,msg):
self.socket.send('客户端:{}'.format(msg).encode('uft-8'))
def recvMsg(self):
data = self.socket.recv(1024)
if data:
print('服务器:'.format(data.decode('utf-8')))
return True
return False
def run(self):
self.sendMsg('我来了哈哈哈')
while True:
if self.recvMsg():
msg = input("\n客户端:")
self.sendMsg(msg)
if __name__ == '__main__':
c = client()
c.run()
Mac 查看端口占用情况命令:
lsof -i #全部端口占用进程
lsof -i :9999 #查看指定端口占用进程 ,i后跟一个空格
kill pid #kill 加上占用端口的进程PID,就可以杀掉占用端口的进程