socket层内容详解二
tcp协议三次握手和四次挥手 所有的断开都是单方面的 粘包现象 为什么会出现粘包现象: 本质:接收端不知道发送端发送的数据长度是多少 tcp协议本身的特点导致: 流式传输、无边界 合包机制 缓存机制 拆包机制 如何解决这个问题? 自定义协议:先发送要传递数据的长度 用户登录验证协议
# 先创建一个文件,里面随便放几个用户名及对应的密文密码,如:# userinfoalex|802165d8e6a7e0fbf11b0feca3913271 jane|48610593feea04340acf643a71621e32
# server.pyimport socket import json import hashlibsk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()def get_md5_code(usr, pwd):md5 = hashlib.md5(usr.encode())md5.update(pwd.encode())return md5.hexdigest()conn,addr = sk.accept() msg = conn.recv(1024).decode() dic_msg = json.loads(msg)with open("userinfo") as f:for line in f:usr,pwd = line.strip().split("|")if usr == dic_msg["username"] and pwd == get_md5_code(dic_msg["username"], dic_msg["password"]):ret = {"code": 1}res_msg = json.dumps(ret).encode()conn.send(res_msg)breakelse:ret = {"code": 0}res_msg = json.dumps(ret).encode()conn.send(res_msg)conn.close() sk.close()
# client.pyimport socket import jsonusr = input("username: ") pwd = input("password: ") dic = {"username": usr, "password": pwd, "operate": "login"} json_dic = json.dumps(dic) bytes_msg = json_dic.encode()sk = socket.socket() sk.connect(("127.0.0.1", 8080)) # 为什么要放在这里? # 因为比如有的用户打开客户端要输入用户名的时候想了半天还没想出来 # 但是客户端却一直开着,所以为了避免这种情况把上面两行放这里 sk.send(bytes_msg)recv_msg = sk.recv(1024).decode() print(recv_msg) dic_code = json.loads(recv_msg) if dic_code["code"]:print("登录成功") else:print("登录失败")sk.close()
如何让服务端与一个客户端聊天时,客户端退出后,服务端还能继续等待下一个客户端聊天?
# server.pyimport socketsk = socket.socket()sk.bind(('127.0.0.1',9000)) sk.listen()while True: # 和n个人聊天conn,addr = sk.accept()while True: # 和一个人聊n久send_msg = input('msg : ')conn.send(send_msg.encode())if send_msg == 'q':breakmsg = conn.recv(1024).decode()if msg == 'q':breakprint(msg)conn.close()sk.close()
# client.pyimport socketsk = socket.socket() sk.connect(('127.0.0.1',9000))while True:msg = sk.recv(1024).decode()if msg == 'q':breakprint(msg)send_msg = input('msg : ')sk.send(send_msg.encode())if send_msg == 'q':breaksk.close()# 这里注意,客户端输入q退出程序后,会发现服务端没有关闭 # 这时只需重启客户端,相当于另一个用户进入,就可继续聊天
完成一个上传文件的程序
# server.pyimport struct import socket import jsonsk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()conn, addr = sk.accept() bytes_len = conn.recv(4) info_len = struct.unpack("i", bytes_len)[0] json_info = conn.recv(info_len).decode() info_dic = json.loads(json_info) print(info_dic) with open(info_dic["filename"], "wb") as f:# 这里有问题,如果把这里的1024和client.py里的1024都改为2048# 发现运行结果后查看该文件时文件大小变小了# 原因是这里虽然写conn.recv(1024)# 但不代表每次一定就能接收到1024个字节# 比如最后还剩800多字节,就会发生拆包现象# 也就是说,虽然写的是接收1024,其实最后只有800多# 这样就要把info_dic["filesize"] -= 1024的1024改掉while info_dic["filesize"]:content = conn.recv(1024)f.write(content)# info_dic["filesize"] -= 1024info_dic["filesize"] -= len(content)conn.close() sk.close()
import socket import os import json import struct# 在上传之前,应该先向server端传递文件信息:文件大小、文件名、以及要做的操作,这里就要用到send,但是可能会与下面的send产生粘包现象 # 打开文件 # 读文件 # 按照字节读取 # 5000:1024 1024 1024 1024 904 # 这样就要send五次,不用担心粘包,只要传过去5000字节就行# server端先接收4字节 # 然后直到了文件信息的长度 # 按照长度接收文件信息 # 从文件信息中得到文件的大小 # 就知道要收取的文件的大小是多少 # 开始接收,直到收完文件大小这么多的数据# 在上传之前,应该先向server端传递文件信息:文件大小、文件名、以及要做的操作 file_path = r"G:\day01 视频以及笔记\20181219_163449.mp4" # input("file_path") filesize = os.path.getsize(file_path) filename = os.path.basename(file_path) # print(filesize) # 143799118 # print(filename) # 20181219_163449.mp4 file_info = {"filesize": filesize, "filename": filename, "operate": "upload"} json_info = json.dumps(file_info) file_info_bytes = json_info.encode() bytes_len = len(file_info_bytes) sk = socket.socket() sk.connect(("127.0.0.1", 8080)) # 先发送文件信息的长度,再发送文件信息 sk.send(struct.pack("i", bytes_len)) sk.send(file_info_bytes)with open(file_path, "rb") as f:while filesize > 0:content = f.read(1024)filesize -= len(content)sk.send(content) # 这里已经是bytes类型 sk.close()
udp协议
# server.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 8080)) # 这里不用写sk.listen(),没有三次握手 msg, client_addr = sk.recvfrom(1024) print(msg) sk.sendto(b"hello", client_addr)sk.close()
# client.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM)sk.sendto(b"hello", ("127.0.0.1", 8080)) msg, addr = sk.recvfrom(1024) print(msg)sk.close()
使用udp协议实现多人聊天功能
# server.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 8080)) # 这里不用写sk.listen(),没有三次握手# 和一个人多次聊天 while True:msg, client_addr = sk.recvfrom(1024)print(msg.decode())content = input(">>>")sk.sendto(content.encode(), client_addr)sk.close()
# client.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM)# 和一个人多次聊天 # 可以再创建一个client.py,内容一模一样,然后依次运行 # 都可以跟server端聊天 while True:content = input(">>>")# 多人聊天时,可以这样区分谁跟server端聊天content = "%s : %s" % ("alex", content)sk.sendto(content.encode(), ("127.0.0.1", 8080))msg, addr = sk.recvfrom(1024)print(msg.decode())sk.close()
使用socketserver来实现tcp协议socket的并发
# server.pyimport socketserver# socketserver是一个上层模块,socket是一个下层模块 # 因此 socketserver 可以实现并发 # socket是基于socketserver的下层模块 # socketserver是基于socket的上层模块class Myserver(socketserver.BaseRequestHandler):# 注意:有多少个client端,就相当于这里有多少个handledef handle(self):# print("连接上了")# print(self.request) # 这个就是conn,因此这样写# conn = self.request# conn.send(b"hello")# 可以与多个client端运行,即并发while True:conn = self.requestconn.send(b"hello")# 这里不需要实例化一个对象 server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), Myserver) server.serve_forever()
# socketserver# 写一个socket的server端,能够同时和多个client端进行通信,互不干扰# socketserver的server端不能用input,因为server端使用了并发机制# 如何写?# 1.必须继承socketserver.BaseRequestHandler# 2.自定义一个类必须实现handler方法# 3.自定义类中的self.request就是conn# 4.启动程序的方法:# socket.server.ThreadingTCPServer(ip, port),自定义类名)# obj.server_forever()
# client.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()
# client1.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()
# client2.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()
1.两个连续的send会产生粘包现象 2.用struct自定义协议可以解决粘包问题 3.文件传输时不用考虑粘包问题 4.自定义协议的进阶版本:先发送字符串的长度,再发送字符串先发送json的长度,再发送jsonjson的字典中包含着下一条信息的长度,然后按照长度接收tcp与udp协议 tcp面向连接的,可靠的,全双工的,流式传输面向连接:同一时刻只能和一个客户端通信要进行三次握手,四次挥手可靠的:数据不丢失、慢全双工:能够双向通信(send,recv谁先都可以)流式传输:粘包、无边界 udp无连接的,面向数据包,不可靠的,快速无连接的:不需要accept/connect, 也没有握手面向数据包的:不会粘包不可靠的:没有自动回复的机制快速的:没有复杂的计算、保证数据传输的机制
# 再讲 pickle 模块import pickleclass Course:def __init__(self, name, period, price, teacher):self.name = nameself.period = periodself.price = priceself.teacher = teacherpython = Course("python", "six months", 19800, "alex") linux = Course("linux", "five months", 15800, "taibai") with open("course_info", "wb") as f:pickle.dump(python, f)pickle.dump(linux, f)with open("course_info", "rb") as f:while 1:try:# print(pickle.load(f).name) # python linuxobj = pickle.load(f)print(obj.__dict__)# {'name': 'python', 'period': 'six months',# 'price': 19800, 'teacher': 'alex'}# {'name': 'linux', 'period': 'five months',# 'price': 15800, 'teacher': 'taibai'}except EOFError:break# dumps str-->bytes # dump str-->文件里的bytes# 1.json能处理的数据类型有限,但是所有语言通用 # pickle支持python中几乎所有对象,但是只能在python语言中使用 # 2.json的dumps的结果是str, pickle的dumps的结果是bytes # 3.json不能连续dump多个数据到文件中 # pickle可以
# 验证客户端的合法性# server.pyimport os import hmac import socketdef auth(conn):secret_key = b"jane"rand_b = os.urandom(32)conn.send(rand_b)obj = hmac.new(secret_key, rand_b)res1 = obj.digest()res2 = conn.recv(1024)cmp_res = hmac.compare_digest(res1, res2)return cmp_ressk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()conn, addr = sk.accept() res = auth(conn) if res:print("是合法的客户端")conn.send("您好".encode()) else:conn.close()sk.close()# 运行后得出结果:是合法的客户端 # 只要两边的secret_key的值不一样,服务端就知道不是合法的客户端
# client.pyimport socket import hmacdef auth(sk):secret_key = b"jane"rand_b = sk.recv(32) # 注意这里obj = hmac.new(secret_key, rand_b)res2 = obj.digest()sk.send(res2)sk = socket.socket() sk.connect(("127.0.0.1", 8080)) auth(sk) msg = sk.recv(1024) print(msg.decode())sk.close()
转载于:https://www.cnblogs.com/shawnhuang/p/10309089.html
socket层内容详解二相关推荐
- C#Socket开发TCP详解(二)
文章目录 C#Socket开发TCP详解(二)--面向连接的套接字编程 简介: **面向连接的套接字** 1.建立连接 2.发送和接收消息 3.关闭连接 C#Socket开发TCP详解(二)–面向连接 ...
- 工业通讯 | CAN基础内容详解(二)——物理层
[往期回顾]工业通讯 | CAN基础内容详解(一) 物理层主要完成设备间的信号传送,把各种信号转换成物理信号,并将这些信号传输到其他目标设备.在这一层中,CAN-bus对信号电平.通信时使用的电缆及连 ...
- SQL Server DBA工作内容详解
原文:SQL Server DBA工作内容详解 在Microsoft SQL Server 2008系统中,数据库管理员(Database Administration,简称为DBA)是最重要的角色. ...
- PopUpWindow使用详解(二)——进阶及答疑
相关文章: 1.<PopUpWindow使用详解(一)--基本使用> 2.<PopUpWindow使用详解(二)--进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...
- Pytorch|YOWO原理及代码详解(二)
Pytorch|YOWO原理及代码详解(二) 本博客上接,Pytorch|YOWO原理及代码详解(一),阅前可看. 1.正式训练 if opt.evaluate:logging('evaluating ...
- matlab的NLP功能,pyhanlp 共性分析与短语提取内容详解
pyhanlp 共性分析与短语提取内容详解 简介 HanLP中的词语提取是基于互信息与信息熵.想要计算互信息与信息熵有限要做的是 文本分词进行共性分析.在作者的原文中,有几个问题,为了便于说明,这里首 ...
- linux 进程间通信 dbus-glib【实例】详解二(上) 消息和消息总线(附代码)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- OS--进程间通信详解(二)
OS–进程间通信详解(二) 文章目录 OS--进程间通信详解(二) 一.进程间通信 1.互斥量 Futexes Pthreads中的互斥量 2.管程 3.消息传递 消息传递系统的设计要点 用消息传递解 ...
- 数据结构--图(Graph)详解(二)
数据结构–图(Graph)详解(二) 文章目录 数据结构--图(Graph)详解(二) 一.图的存储结构 1.图的顺序存储法 2.图的邻接表存储法 3.图的十字链表存储法 4.图的邻接多重表存储法 二 ...
最新文章
- MySQL复习资料(七)——MySQL-存储过程
- 《细胞》重磅成果!任兵课题组绘制迄今最大规模人类单细胞染色质可及性图谱...
- Crazypony四轴飞行器代码框架
- Drawable之color示例
- java hostwrite_Java IOUtils.write方法代碼示例
- ubuntu 9.10安装sopcast gmlive 成功(带文件下载)
- 摄影爱好者尝试图库销售时需要注意什么?| 拍者手记
- Qlv转换MP4格式 让你不再错过
- 优化 MT4 性能让你的MT4更加流畅
- 个人图床的最简单制作-腾讯云COS
- solr mysql 增量索引_基于Solr DIH实现MySQL表数据全量索引和增量索引
- windows11任务栏全透明
- 工控通讯经历1:(C#)三菱FX5U-32M与上位机通讯(超详细!)
- 某手app的__NS_sig3,sig,__NStokensig算法
- 疯狂的大柚柚带你玩转MSP-ESP430G2(基础篇)----(十一)看门狗
- Linux系统设置用户密码规则(复杂密码策略)方法
- BUGKU------extract变量覆盖
- linux 限制带宽命令,怎样在 Linux 中限制网络带宽使用
- 如何建立有效的项目管理制度?
- [RK3288][Android6.0] 调试笔记 --- RT5640的ctl name列表
热门文章
- android系统学习笔记十一
- 84.断点 85.显卡
- 户籍改革更进一步!我国将推动户籍准入年限同城化累计互认
- Linux后台进程管理以及ctrl+z(挂起)、ctrl+c(中断)、ctrl+\(退出)和ctrl+d(EOF)的区别
- 学习Python编程,推荐最好的五本参考书,必读~~~
- Fedora下网络配置及相关命令
- qt的输出中文,数字到表格
- 架构 php_十年PHP架构师的成长之路,程序员必备
- 向日葵win10远程linux主机,大神为你细说win10系统使用向日葵远程桌面软件远程的操作办法...
- easypoi导出数值型_SpringBoot使用EasyPoi进行数据导入导出Excel(一)