socket 模块

socket 模块

我们知道基于网络编写程序时,需要使用 OSI七层协议里的七层,每一层都要写。很复杂很繁琐。
socket 模块相当于集成了这一块,封装了复杂的接口提空了简单快捷的接口。	

socket套接字简介

	基于文件类型的套接字家族(单机)
  	AF_UNIX
  基于网络类型的套接字家族(联网)
  	AF_INET

代码实操

cs架构的软件都应该考虑到服务端,因为只有有了服务场所,才能提供服务

# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()  # 括号内不写参数 默认就是TCP协议  family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池(暂且忽略)
server.listen(5)
# 4.等待接客
sock, addr = server.accept()  # return sock, addr  三次握手 建立链接
print(sock, addr)  # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024)  # 接收客户端发送过来的消息 1024字节
print(data.decode('utf8'))
sock.send('滚开我不开心'.encode('utf8'))  # 给客户端发送消息 注意消息必须是bytes类型
# 6.关闭双向通道
sock.close()  # 四次挥手  关闭链接
# 7.关闭服务端
server.close()  


# 客户端
import socket


# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送消息
client.send('今天你吃饭了吗'.encode('utf8'))
# 4.接收服务端发送过来的消息
date = client.recv(1024)
print(date.decode('utf8'))
# 5.断开与服务端的链接
client.close()

数据交互循环

# 1.解决信息固定的问题
    利用input获取用户输入
# 2.解决通信循环的问题
    将双方用于数据交互的代码循环起来
    
 # 服务端
while True:
    data = sock.recv(1024)  # 获取别人说的什么
    print(data.decode('utf8'))  # 解码别人说的啥
    msg = input('请回复>>>:').strip()
    sock.send(msg.encode('utf8'))  # 回复别人的话,因为基于网络传输,使用二进制编码

    
# 客户端
while True:
    msg = input('请输入你要发送的信息>>>:').strip()
    client.send(msg.encode('utf8'))  # 给服务端发送信息
    data = client.recv(1024)  # 接收服务端回复消息
    print(data.decode('utf8'))

代码优化

1、发送信息不能为空
  统计长度然后进行判断(len)
    
2、苹果电脑 反复重启服务端可能会发生报错: address in use
 解决方法:
# 在最上面放一个
 from socket import SOL_SOCKET,SO_REUSEADDR
# 在bind上面放
 server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    

    
 当客户端异常断开的情况下 如何让服务端继续服务其他客人
"""
  如果在windows客户端异常处理退出之后服务端就会直接报错,直接加上异常处理 break结束退出内循环 跳到外循环 接待另一个客人
  处理方式: 异常处理 """
  while True:
  	sock, addr = server.accept() 
    try:
       	data = sock.recv(1024)  # 获取别人说的什么 
        print(data.decode('utf8'))  # 解码别人说的啥
        msg = input('请回复>>>:').strip()
        sock.send(msg.encode('utf8'))  # 回复别人的话,因为基于网络传输,使用二进制编码
    except Exception:
        break
 """ 
 如果是mac或linux服务端,就会接收到一个空的信息
 处理方式: len判断 """
 while True:
  	sock, addr = server.accept() 
    try:
       	data = sock.recv(1024)  # 获取别人说的什么 
       	if data(len) == 0:
       		break
        print(data.decode('utf8'))  # 解码别人说的啥
        msg = input('请回复>>>:').strip()
        sock.send(msg.encode('utf8'))  # 回复别人的话,因为基于网络传输,使用二进制编码
    except Exception:
        break

半连接池

listen(5)
py文件默认只能同时只能运行一次,如果想要单独分开运行多次:
Edit Configuration选择all

# 半连接池
  设置最大等待人数 >>>:  节省资源,提升效率

黏包现象

我们在使用socket 模块进行数据交互时,服务端连续执行了3次recv, 客户端连续执行了 3次send 
此时如果我们的recv 填写的1024字节 如果send 传输的字节不超过1024,那么我们只第一次recv就会得到3条信息合并成一条。  
如果我们填写的字节大写跟我们传输的一样,那么就会一次性连续得到3条消息  

                                   此现象就称为黏包现象。
"黏包现象产生的原因"
	1.不知道每次的数据到底多大
	2.TCP也称为流式协议:数据像水流一样绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
"解决方法:"
	明确的得知即将获取的数据具体有多大。使用 "struct模块"

socket基本使用

bind()					 绑定地址
listen()				 半连接池
accept()				 等待客户端链接
send()					 发送消息
recv()					 接收消息
connect()				 链接服务端

黏包终极解决思路

# 发送方
1.先构造一个字典,里面放真实数据的信息
eg: 数据大小、名称、简介、、、
2、对字典做打包处理pack
3、发送固定长度的报头 (字典的长度)
4、发送真实的字典数据
5、发送真实的真正数据

# 接收方
1、先接收打包好固定长度的字典
2、解析出字典的真实长度unpack
3、接收字典数据
4、从字典数据中解析出各种信息
5、接收真实的数据

代码实操

服务端:

import socket
import struct
import json
server = socket.socket()
server.bind(('127.0.0.1', 14334))
server.listen(5)

sock, addr = server.accept()
# 接收固定长度的报头
date_dict_head = sock.recv(4)
# 根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i',date_dict_head)[0]
# 接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
# 利用jason 模块 解码并反序列化
data_dict = json.loads(data_dict_bytes)
# 获取真实字典数据
print(data_dict)
total_size = data_dict.get('file_size')
recv_size = 0
with open(data_dict.get('file_name'),'wb') as f:
    while recv_size < total_size:
        data = sock.recv(1024)
        f.write(data)
        recv_size += len(data)
        print(recv_size)
        
第二种获取真实数据的方法:
total_size = data_dict.get('file_size')
with open(data_dict.get('file_name'), 'wb') as f:
     f.write(sock.recv(total_size))

客户端:

import socket
import os
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1',14334))
# 获取真实数据大小
file_size = os.path.getsize(r'D:\pythonProject\11.16\1.txt')
# 制作真实数据的字典数据
data_dict = {
    'file_name': '我爱你.txt',
    'file_size': file_size,
    'file_desc': '很带劲',
    'file_info': '我的心里话'
}
# 制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
# 获取字典报头长度   打包
data_dict_len = struct.pack('i',len(data_dict_bytes))
# 发送字典报头  (i模式报头固定长度是4)
client.send(data_dict_len)
# 发送字典
client.send(data_dict_bytes)
# 发送真实数据
with open(r'D:\pythonProject\11.16\1.txt','rb') as f:
    for line in f:
        client.send(line)

socket UDP协议

客户端

import socket
# 指定使用UDP协议, 不指定的话,默认为TCP协议
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8888))
msg, addr = server.recvfrom(1024)  # recvfrom 是UDP协议的接收
print(msg.decode('utf8'))  # 解析发送来的内容
print(addr)  # 服务端的位置
server.sendto(b'wuhu, i am you father',addr)
  # 接收到('127.0.0.1', 52376)发来的hello i am superman
    

服务端:

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8888)
client.sendto(b'hello i am superman', server_addr)  # sendto就是UDP协议的发送

msg, addr = client.recvfrom(1024)
print(msg.decode('utf8'))
print(addr)
# 接收到('127.0.0.1', 8888)发来的wuhu, i am you father

基于UDP实现简易版聊天室

# 服务端

import socket
server = socket.socket(type= socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8888))
while True:
    msg, addr = server.recvfrom(1024)
    print(addr)
    print(msg.decode('utf8'))
    back_msg = input('请回复信息>>>:').strip()
    server.sendto(back_msg.encode('utf8'),addr)



# 客户端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8888)

while True:
    msg = input('请输入要输入的内容>>>:').strip()
    msg = '这是来自客户端1号发来的信息: %s'%msg
    client.sendto(msg.encode('utf8'), server_addr)
    msg, addr = client.recvfrom(1024)
    print(msg.decode('utf8'), addr)
    
# 增加新的客户端时,代码不需要改变,只需要msg修改一些这是客户n发来的信息
posted @ 2022-11-16 16:46  李阿鸡  阅读(108)  评论(0编辑  收藏  举报
Title