Python学习 之 网络编程


概念

  • 两个程序如何通信
    • 两个程序都在同一台机器上 --> 基于文件通信
    • 两个程序在不同机器上 --> 基于网络通信
  • 如何定位
    • ip + mac 能够从网络中很快找到一台唯一的机器
    • ip + port 能够精准的定位一个服务
  • 交换机和路由器
    • 交换机用于相同网络内的通信
    • 路由器用于不同网络内的通信
  • 协议
    • arp协议是通过ip寻找mac的协议
    • rarp协议是通过mac寻找ip的协议
  • 软件开发的架构
    • C/S架构
    • B/S架构 --> 属于C/S架构的一种

TCP和UDP

TCP

简单了解tcp协议, 三次握手和四次挥手


黏包问题

  • 这个问题只会发生在tcp协议中
  • 情景
    连续发送的情况下容易发生
  • 结果
    发送数据时, 无法区分两次数据发送边界, 导致数据不可靠
  • 原因:
    tcp协议为了减少网络带来的损耗(减少网络传输的次数), 将数据包先存进内存空间, 再从内存空间中取部分发送
    所以拿到的数据不一定是想发送的字节数
  • 解决:
    发送数据前, 先发送该数据的大小, 再按照数据大小的发送

解决黏包问题

  • 发送方
    import struct
    
    """
    	使用struct.pack('i', int)方法
    	将数字转为4位的bytes类型数据
    
    """
    
    # 发送数据前, 先发送长度
    # struct.pack 方法将长度转换为固定 4 位的 bytes 类型数据
    msg_len = struct.pack('i', len(msg))
    conn.send(msg_len)
    
    
  • 接收方
    import struct
    
    """
    	使用struct.unpack(arg)方法
    	将传过来的4位bytes类型数据转回数字
    	
    """
    
    # 接收4位数据
    rec = sk.recv(4)
    
    # 将4位bytes转为数字, 返回值是个元祖, 取第一个元素即可
    l = struct.unpack(rec)[0]
    
    # 根据长度接收数据
    msg = sk.recv(l)
    
    

UDP

  • 不建立双向连接, 直接向server端发送
  • 优点: 速度快
  • 缺点: 不可靠, QQ通过回执机制, 实现了相对可靠
  • 场景: 适合即时通信类, 直播类服务

socket

TCP协议

server端

import socket
# from socket import socket, AF_INET, SOCK_STREAM
import struct

"""先发送数据大小, 再发送数据, 已经解决黏包问题"""

# 实例化socket连接
s = socket.socket()
# s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定ip和端口号
ip_port = ('127.0.0.1', 9000)
s.bind(ip_port)

#  开启监听, 允许最大的连接等待数, 若python版本较低需要使用sk.listen(int)
s.listen()

while True:
	# 此时接收到请求, 开始三次握手, 成功后建立连接并拿到地址
	conn, addr = s.accept()
	
	while True:
		# 接收
		rec = conn.recv(4)
		l = struct.unpack('i',rec)[0] 
		msg = conn.recv(l).decode('utf-8')
		print(msg)
	
		# 发送数据, 只能发送bytes类型
		# 将发送的数据用utf8编码
		msg = 'srv tcp'.encode('utf-8') 
		# 将msg长度转为4位bytes类型
		l = struct.pack('i',len(msg))
		# 发送数据长度
		conn.send(l)
		# 发送数据
		conn.send(msg)

	# 关闭连接
	conn.close()
s.close()

client端

import socket
import struct

sk = socket.socket()
ip_port = (' 127.0.0.1', 9000)

# 发起连接请求
sk.connect(ip_port)

# 接收
# 先接收数据长度
rec = sk.recv(4)
# 将数据长度转为数字
l = struct.unpack('i',rec)[0]
# 根据数据长度接收数据
msg = sk.recv(l).decode('utf-8')

# 发送
msg = 'cli tcp'.encode('utf-8')
l = struct.pack('i',len(msg))
sk.send(l)
sk.send(msg)

sk.close()


UDP协议

server端

import socket

# 实例化udpsocket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)

# 绑定服务的 ip 和端口号
ip_port = ('127.0.0.1', 9001)
udp_sk.bind(ip_port)

# 接收数据, 并拿到地址
rec, addr = udp_sk.recvfrom(1024)
ret = rec.decode('utf-8')

# 发送
msg = 'src udp'.encode('utf-8')
# 根据地址发送消息
udp_sk.sendto(msg, addr)

udp_sk.close()

client端

import socket

# 实例化udpsocket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ('127.0.0.1', 9001)

# 根据地址发送数据
msg = 'cli udp'.encode('utf-8')
udp_sk.sendto(msg, ip_port)


# 接收
rec, addr = udp_sk.recvfrom(1024)
ret = rec.decode('utf-8')


socketserver

server端

import socketserver # 优化了socket模块

"""可以同时对多个client端提供服务"""

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        """		具体接收操作写在下面	"""
        # 接收
        msg = conn.recv().decode('utf-8')
        print(msg)
        
        # 发送数据
        msg = 'server'.encode('utf-8') # 将发送的数据用utf8编码
        conn.send(msg) # 发送真实数据
        
# 启动socketserver
ip_port = ('ip_addr',port)
myserver = socketserver.ThreadingTCPServer(ip_port, Myserver)
myserver.serve_forever()

源码

class BaseRequestHandler:

    """Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.

    """
	
    def __init__(self, request, client_address, server):
        self.request = request	# conn
        self.client_address = client_address	# 客户端地址+端口号
        self.server = server	# 服务端的ip+端口号
        self.setup()
        try:
            self.handle()	# myserver的实例, 所以必须自定义一个同名的方法
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值