一丶套接字(socket)

  tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

  基于UDP协议的socket

  server端:

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字

  client端:

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

二丶粘包  

  在学习粘包之前我们先学几个新模块:

  struct模块:

    1、 struct.pack
      struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。其函数原型为:struct.pack(fmt, v1, v2, ...),参数fmt是格式字符串,关于格式字符串的相关信息在下面有所介绍。v1, v2, ...表示要转换的python值。

    2、 struct.unpack
      struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。

import struct

a = 20
b = 400
s = struct.pack('ii', a, b)
print(s, type(s))
#输出:b'\x14\x00\x00\x00\x90\x01\x00\x00' <class 'bytes'>
print('length: ', len(s))
#输出:length: 8
s2 = struct.unpack('ii', s)
print(s2)
#输出:(20, 400) s2 = struct.unpack('ii', s)
#报错:unpack requires a buffer of 4 bytes
#==>解压需要一个4字节的缓冲区,也就是说'ii'表示8个字节的缓冲

  #格式符"i"表示转换为int,'ii'表示有两个int变量。

  #进行转换后的结果长度为8个字节(int类型占用4个字节,两个int为8个字节)

  可以使用python的内置函数repr来获取可识别的字符串,其中十六进制的0x00000014, 0x00001009分别表示20和400。

  subprocess模块:

    这个模块用的不多,只是用于执行复杂的系统命令而已..我们不必深究.

 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)

res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE) 的结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码 且只能从管道里读一次结果 注意

  同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是粘包。

  那粘包的成因是什么呢?

当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。
MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。
如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

  那我们如何解决粘包呢?

  1.我们可以用time模块,为两个文件之间设置一定的时间间隔,让两个不会粘结在一起,这个方法可行,但是会降低程序运行效率,而且很蠢.

  2.我们上面讲过struct模块,我们可以用struct模块把报头的四个字节取出来,再解包获得文件的大小,通过解包出来的解包大小来读取数据.

 服务端:

import socket
import subprocess server = socket.socket() server.bind(('127.0.0.1',8008)) server.listen(5) while True:
print("server is working.....")
conn,addr = server.accept() #建立链接
# 字节类型
while True:
# 针对window系统
try:
cmd = conn.recv(1024).decode("utf8") # 阻塞 if cmd == b'exit': #判断与客户端链接是否结束
break res=subprocess.Popen(cmd,
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
# print("stdout",res.stdout.read())
# print("stderr",res.stderr.read().decode("gbk"))
out=res.stdout.read()
err=res.stderr.read() print("out响应长度",len(out)) #输出出来的长度
print("err响应长度",len(err)) #错误的长度
if err:
import struct
header_pack = struct.pack("i", len(err)) #压包
conn.send(header_pack) #发送
conn.send(err) #发送错误信息
else:
#构建报头
import struct
header_pack=struct.pack("i",len(out)) #压包
print("header_pack",header_pack) #压包信息
# # 发送报头
conn.send(str(len(out)).encode("utf8")) #发送压包
# 发送数据
conn.send(out) #发送输出出来的数据 except Exception as e: #错误信息处理
break conn.close()

服务端

 客户端:

import socket
import struct
sk = socket.socket() sk.connect(('127.0.0.1',8008)) while 1:
cmd = input("请输入命令:")
sk.send(cmd.encode('utf-8')) # 字节 #发送输出的指令
if cmd=="":
continue
if cmd == 'exit': #判断是否退出
break data_length=int(sk.recv(1024).decode("utf8")) #接收到的信息
print("data_length",data_length) #打印出来 recv_data_length=0 #接收到的长度
recv_data=b"" while recv_data_length<data_length: #如果接收到的长度小于设定的数据长度,则全输出出来,否则直接输出最大长度
data=sk.recv(1024) #阻塞 设定最大输出长度
recv_data_length+=len(data) #计算数据长度
recv_data+=data print(recv_data.decode("gbk")) sk.close()

客户端

  3.还可以通过客户端发一个数据,服务器回一个数据,表示已经收到,然后再发送数据,这样就不会导致粘包了.

 服务端:

import socket
import json sock = socket.socket()
sock.bind(("192.168.13.226",8080))
sock.listen(5) while 1:
print("正在等待链接.....")
conn,addr = sock.accept() #建立链接
while 1:
data = conn.recv(1024).decode("utf8") #接收数据,并解码
file_info = json.loads(data)
print("file_info",file_info) #查看反序列化出来的是什么 #文件信息
action = file_info.get("action")
filename = file_info.get("filename")
filesize = file_info.get("filesize") conn.send(b"") #告诉客户端,我已准备接收数据 #接收文件数据
with open("put/" + filename,"wb") as f:
recv_data_length = 0
while recv_data_length < filesize:
data = conn.recv(1024)
recv_data_length += len(data)
f.write(data)
print("总文件大小为%s,已接收%s"% (filesize,recv_data_length))
print("接收成功! ")

服务端

 客户端:

import socket
import os
import json sock = socket.socket()
sock.connect(("192.168.13.226",8080)) while 1:
cmd = input("请输入命令:") #put 111.jpg action,filename = cmd.strip().split(" ")
filesize = os.path.getsize(filename) file_info = {
"action":action, #命令
"filename":filename, #文件名字
"filesize":filesize #文件大小
} file_info_json = json.dumps(file_info).encode("utf8") #将字典序列化
sock.send(file_info_json) #发送序列化后的字典 #确认服务端接收到了文件信息
code = sock.recv(1024).decode("utf8")
if code == "": #服务端已准备接收文件
#发送文件数据
with open(filename,"rb")as f:
for line in f: #把文件一行一行的发过去
sock.send(line)
else:
print("服务器异常! ")

客户端

  关于hashlib补充

import hashlib

md5=hashlib.md5()
md5.update(b"hello")
md5.update(b"yuan") print(md5.hexdigest())
print(len(md5.hexdigest())) #helloyuan: d843cc930aa76f7799bba1780f578439
# d843cc930aa76f7799bba1780f578439

  综合例题:上传文件

服务端

import struct
import socket
import json
import hashlib sock = socket.socket()
sock.bind(("127.0.0.1",8080))
sock.listen(5) while 1:
print("正在等待连接.....")
conn,addr = sock.accept()
while 1: #接收json的打包长度
file_info_length_pack = conn.recv(4)
file_info_length = struct.unpack("i",file_info_length_pack)[0] #解包 #接收json字符串
file_info_json = conn.recv(file_info_length).decode("utf8")
file_info = json.loads(file_info_json) #反序列化 action = file_info.get("action")
filename = file_info.get("filename")
filesize = file_info.get("filesize") #循环接收文件
md5 = hashlib.md5()
with open("put/"+ filename,"wb") as f:
recv_data_length = 0
while recv_data_length < filesize:
data = conn.recv(1024)
recv_data_length += len(data)
f.write(data)
#MD5摘要
md5.update(data) #写入要加密的的东西
print("文件总大小:%s,已成功接收%s"% (filesize,recv_data_length)) print("接收成功!")
conn.send(b"OK")
print(md5.hexdigest())
md5_val = md5.hexdigest() #解密
client_md5 = conn.recv(1024).decode('utf8')
if md5_val == client_md5:
conn.send(b"")
else:
conn.send(b"")

服务端

客户端

import socket
import os
import json
import struct
import hashlib sock = socket.socket()
sock.connect(("127.0.0.1",8080)) while 1:
cmd = input("请输入命令:") #put 111.jpg action,filename = cmd.strip().split(" ")
filesize = os.path.getsize(filename) #获取文件大小 file_info = {
"action":action, #命令
"filename":filename,#名字
"filesize":filesize,#文件大小
} #将file_info字典先序列化,再编码赋值给file_info_json
file_info_json = json.dumps(file_info).encode("utf8") #打包
ret = struct.pack("i",len(file_info_json))
#发送file_info_json的打包长度
sock.send(ret)
#发送 file_info_json字节串
sock.send(file_info_json)
#发送 文件数据
md5 = hashlib.md5()
with open(filename,"rb") as f:
for line in f:
sock.send(line)
md5.update(line) #写入要加密的东西 data = sock.recv(1024)
print(md5.hexdigest())
md5_val = md5.hexdigest() #获取密文
sock.send(md5_val.encode("utf8"))
is_valid = sock.recv(1024).decode("utf8")
if is_valid == "":
print("文件完整!")
else:
print("文件上传失败!")

客户端

面向对象之套接字(socket)和黏包的更多相关文章

  1. socket套接字模块及黏包现象

    一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...

  2. 2、网络并发编程--套接字编程、黏包问题、struct模块、制作简易报头、上传文件数据

    昨日内容回顾 面向对象复习(json序列化类) 对象.类.父类的概念 三大特性:封装 继承 多态 双下开头的方法(达到某个条件自动触发) __init__:对象实例化自动触发 __str__:对象执行 ...

  3. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  4. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

  5. Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()

    前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套 ...

  6. 套接字socket 的地址族和类型、工作原理、创建过程

    注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...

  7. [置顶] Java套接字Socket编程

    1)概念 网络编程基本模型就客户端到服务器的模型,也就是我们常见的C/S模型.简单的说就是两个进程间相互通信的过程.即通信双方一方作为服务器等待客户端提出请求并给以回应,另一方作为客户端向服务器提出请 ...

  8. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  9. 套接字编程,创建套接字socket

    1.套接字地址结构: struct sockaddr { sa_family_t sa_family; char sa_data[14]; }; 其中,成员sa_family表示套接字的协议族类型,对 ...

随机推荐

  1. Struts2工作原理和执行流程图

    在struts2的应用中,从用户请求到服务器返回相应响应给用户端的过程中,包含了许多组件如:Controller.ActionProxy.ActionMapping.Configuration Man ...

  2. ASP.NET 调试出现<%@ Application Codebehind="Global.asax.cs" Inherits="XXX.XXX.Global" Language="C#" %>

    ASP.NET 调试出现<%@ Application Codebehind="Global.asax.cs" Inherits="XXX.XXX.Global&q ...

  3. php学习笔记-变量的作用域

    这个东西很难理解,但很重要,我觉得非常容易出错. PHP中的变量按照作用域分为有两种,一种是global,一种是local. 函数内部声明的变量就叫local型变量,只能在函数内部被访问到.一句话,l ...

  4. 学习过程的记录:实验室电脑上的jdk环境变量

    亲爱的,先区分安装路径和软件的存放路径好不好呢? 1.变量名:JAVA_HOME 变量值:D:\Program Files\Java\jdk1.7.0_21 2. 编辑 Path(粘贴到最后) %JA ...

  5. redis系列:通过通讯录案例学习hash命令

    前言 这一篇文章将讲述Redis中的hash类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了. 项目Github地址:https://github.com/rainbowda/learn ...

  6. Beta博客总结

    描述项目预期计划和现实进展 冲刺 时间 预期任务以及预估时间 现实完成情况以及实际用时 冲刺1 12.4 修改等级答题界面:30,修改获取用户信息接口:30 修改等级答题界面:60,修改获取用户信息接 ...

  7. 解决Navicat无法连接到Mysql

    Navicat无法连接到Mysql,返回的错误码是Lost connection to MySQL server at ‘reading initial communication packet’, ...

  8. struts2学习笔记——常见报错及解决方法汇总(持续更新)

    操作环境:(1)Tomcat 7.0.72.0 (2)OS Name: Windows 7  (3)JVM Version:  1.8.0_25-b18  (4)eclipse Version: Ke ...

  9. ssh-ssh整合(Struts2+Sprinig+hibernate)

    在之前呢我们已经讲解过ssm以及ssm2的整合开发,今天我们进行ssh的整合,在之前已经有一篇整合ssh的文章,那是基于注解开发的,今天讲解的为基于配置文件注入方式进行开发.思路:spring管理hi ...

  10. P1072 HanksonHankson 的趣味题

    题意:给定$a_0,a_1,b_0,b_1$ 问有多少x满足1:$gcd(x,a_0)=a_1$ 2:$lcm(x,b_0)=b_1$ 思路:暴力枚举(当然不是死枚举) 枚举$a_1$的倍数,判断.. ...