tongsiying

阅读|运动|自律

0%

BlockStorage-一致性对比

CRC32、MD5和SHA1是目前用来校验文件信息真实性的主要手段,使用这些校验算法可以发现保存或传输的信息是否受到损坏或篡改,防止文件或信息被恶意篡改。下面将分别介绍这几种校验算法。

crc32

CRC全称为Cyclic Redundancy Check,又叫循环冗余校验。CRC是目前使用中最老的一种校验算法,它是由W. Wesley Peterson在1961年发表的论文中提出,CRC是种根据网络数据封包或电脑档案等数据产生简短固定位数校验码的一种散列函數(HASH,把任意长度的输入通过散列算法,最终变换成固定长度的摘要输出,其结果就是散列值,按照HASH算法,HASH具有单向性,不可逆性),主要用来检测或校验数据传输或者保存后可能出现的错误。生成的数字在传输或者储存之前计算出来并且附加到数据后面,然后接收方进行检验确定数据是否发生变化。一般来说,循环冗余校验的值都是32位的整数。由于本函数易于用二进制的电脑硬件使用、容易进行数学分析并且尤其善于检测传输通道干扰引起的错误,因此获得广泛应用。

尽管CRC在错误检测中非常有用,但CRC并不能可靠地校验数据完整性,这是因为CRC多项式是线性结构,可以非常容易地通过改变数据方式达到CRC碰撞,这里给一个更加通俗的解释,假设一串带有CRC校验的代码在传输中,如果连续出现差错,当出错次数达到一定次数时,那么几乎可以肯定会出现一次碰撞(值不对但CRC结果正确),但随着CRC数据位增加,碰撞几率会显著降低,比如CRC32比CRC16具有更可靠的验证性,CRC64又会比CRC32更可靠,当然这都是按照ITU规范标准条件下。

正因为CRC具有以上特点,对于网络上传输的文件类很少只使用CRC作为校验依据,文件传输相比通信底层传输风险更大,很容易受到人为干预影响。

  • 通信数据的校验,差错控制,检错和纠错能力强,
  • 16位或32位(带符号)

md5,sha1(安全领域)

MD5全称为Message-Digest Algorithm 5,又叫摘要算法和哈希算法。是Ronald L. Rivest在1992年间提出的,MD5由MD4、MD3、MD2改进而来,MD5散列长度通常是128位,是目前被大量广泛使用的散列算法之一,主要用于密码加密和文件校验等,虽然MD5比CRC的安全可靠性要高的多,但目前已经找到可行的破解方法。现在网上虽然出现有些破解网站和软件,不过可以肯定实际作用范围相当有限,比如,即使黑客拿到了PASSWORD MD5值,除了暴力破解,即使找到碰撞结果也未必能够影响用户安全问题,因为对于密码还要限定位数、类型等,但是如果是面向数字签名等应用,可能就会被破解掉。

  • 文件加密
  • 数字签名
  • 鉴权协议
  • 128位
  • 验证数据的完整性
    -(推荐)比较的列(项)较多的情况,性能会有所提升

sha1

SHA全称为Secure Hash Algorithm,又叫安全散列算法。SHA是由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布,SHA家族算法有SHA-1、SHA-224、SHA-256、SHA-384和SHA-512(后四者通常并称SHA2),原理和MD4、MD5相似。SHA可将一个最大2^64位(2305843009213693952字节)信息,转换成一串160位(20字节)的散列值(摘要信息),是目前应用最广的HASH算法。同MD5一样,从理论角度,SHA1也不是绝对可靠,目前也已经找到SHA1的碰撞条件,但“实用”的碰撞算法软件还没出现。于是美国NIST又开始使用SHA2,研究更新的加密算法。

补充:虽然目前这几种校验算法都找到了破解条件,但像目前主流使用的MD5、SHA1还是值得信赖的,因为MD5和SHA1都具有高度的离散性,哪怕是只修改一个字节值都会导致MD5或SHA1值“巨大”变化,从实践角度,不同信息具有相同MD5或SHA1码的可能性非常低,通常认为是不可能的,对于普通的下载文件或操作系统,想通过简单的修改某个字节或某些字节,又要保证文件名、大小和安装可靠性的前提下,想达到MD5、SHA1碰撞效果也几乎是不可能的。

  • 类似于md4,md5

  • 单线程,多线程下载,下载文件的准确性校验拼装

比较对比

内容 算法 校验值 安全性 效率 用途
crc32 多项式除法,16或32位 crc值 比较弱 通信数据的校验
md5、sha1 16字节(128位) hash值或散列值 文件加密、数字签名等

python实现方式

md5:

1
2
3
4
5
import hashlib

md5 = hashlib.md5()
md5.update(bytes('http://www.baidu.com',encoding="utf-8"))
result = md5.hexdigest()

  

SHA1:

1
2
3
4
5
import hashlib

sha1 = hashlib.sha1()
sha1.update(bytes('how to use sha1 in ',encoding="utf-8"))
result = sha1.hexdigest()

  

CRC32:

1
2
from zlib import crc32
result = crc32(b'http://www.baidu.com')

其中SHA-1与MD5 的最大区别在于其摘要比MD5 摘要长 32 比特。对于强行攻击,产生任何一个报文使之摘要等于给定报文摘要的难度:MD5 是2128 数量级的操作,SHA-1 是2160 数量级的操作。但由于SHA-1 的循环步骤比MD5 多(80:64)且要处理的缓存大(160 比特:128 比特),SHA-1 的运行速度比MD5 慢。

虽然在数据表非常大的时候CRC32会出现大量hash冲突,但是能提供更好的性能,在mysql性能优化能起到很好的作用。查询语句可以这样来写:

1
select id from table where url_crc=%s and url=%s;

https://blog.csdn.net/anypkv/article/details/16823797

https://www.cnblogs.com/zenan/p/9165719.html

基础积累

Python使用struct处理二进制

https://www.cnblogs.com/xywq/p/7594457.html

https://www.cnblogs.com/gala/archive/2011/09/22/2184801.html

有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.

struct模块中最重要的三个函数是pack(), unpack(), calcsize()

1
2
3
4
5
6
7
8
#  按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
pack(fmt, v1, v2, ...)

# 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple
unpack(fmt, string)

# 计算给定的格式(fmt)占用多少字节的内存
calcsize(fmt)

上述fmt中,支持的格式为:

FORMAT C TYPE PYTHON TYPE STANDARD SIZE NOTES
x pad byte no value
c char string of length 1 1
b signed char integer 1 (3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
f float float 4 (4)
d double float 8 (4)
s char[] string
p char[] string
P void * integer (5), (3)

unsigned 无符号

signed 有符号

注1.q和Q只在机器支持64位操作时有意思

注2.每个格式前可以有一个数字,表示个数

注3.s格式表示一定长度的字符串,4s表示长度为4的字符串,但是p表示的是pascal字符串

注4.P用来转换一个指针,其长度和机器字长相关

注5.最后一个可以用来表示指针类型的,占4个字节

为了同c中的结构体交换数据,还要考虑有的c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下:

C TYPE 范围
unsigned int 0~4294967295
int -2147483648~2147483647
unsigned long 0~4294967295
long -2147483648~2147483647
long long最大值 9223372036854775807
long long最小值 -9223372036854775808
unsigned long long的最大值 18446744073709551615
_int64的最大值 9223372036854775807
int64最小值 -9223372036854775808
unsigned_int64的最大值 18446744073709551615

为了同c中的结构体交换数据,还要考虑有的c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下:

Character Byte order Size and alignment
@ native native 凑够4个字节
= native standard 按原字节数
< little-endian standard 按原字节数
> big-endian standard 按原字节数
! network (= big-endian) standard 按原字节数

https://www.cnblogs.com/qionglouyuyu/p/4175480.html

Little和Big指的是内存地址的大小,end指的是数据的末尾。

不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序。

最常见的有两种:

1. Little-endian:将低序字节存储在起始地址(低位编址)

2. Big-endian:将高序字节存储在起始地址(高位编址)

Big-endian 的内存顺序和数字的书写顺序是一致的,方便阅读理解。
Little-endian 在变量指针转换的时候地址保持不变,比如 int64* 转到 int32*

各有利弊,统一就好,目前看来是 little-endian成为主流了。

  1. little-endain是小头端编码方式,即内存的低位对应数值低位

Little-endian指内存地址低的地方存数据的末尾(即低字节)

特点:

1.最符合人的思维的字节序

2.地址低位存储值的低位

3.地址高位存储值的高位

4.怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说 :

-低位值小,就应该放在内存地址小的地方,也即内存地址低位

-反之,高位值就应该放在内存地址大的地方,也即内存地址高位

举个例子:存放值12345678

1
2
3
4
5
6
 低地址 ------------------> 高地址
       ####################################################
地址 #      100        #     101        #          102       #      103          # 
       ####################################################
值    #01111000 #01010110 # 00110100  #  00010010  #
7 8 5 6 3 4 1 2

举例的数据要看成16进制,就对了

一个地址8位,一个十六进制4位

所以一个地址存两个十六进制数

  1. big-endain是大头端编码方式,即内存的高位对应数值低位

Big-endian指内存地址高的地方存数据的末尾(即高字节)

特点:

1.最直观的字节序

2.地址低位存储值的高位

3.地址高位存储值的低位

4.为什么说直观,不要考虑对应关系

5.只需要把内存地址从左到右按照由低到高的顺序写出

6.把值按照通常的高位到低位的顺序写出

9.两者对照,一个字节一个字节的填充进去

举个例子:存放值12345678

1
2
3
4
5
6
       低地址 ------------------> 高地址
       ####################################################
地址 #      100        #     101        #          102       #      103          # 
       ####################################################
值    # 00010010  #00110100 # 01010110  #  01111000  #
1 2 3 4 5 6 7 8

例:0x1234要存放进从0x4000开始的内存中

在Little-endian中

内存地址 存放内容
0x4000 0x34
0x4001 0x12

在Big-endian中

内存地址 存放内容
0x4000 0x12
0x4001 0x34

https://www.cnblogs.com/passingcloudss/archive/2011/05/03/2035273.html

例子1:在内存中双字0x01020304(DWORD)的存储方式。

1
2
3
4
内存地址 4000 4001 4002 4003 
LE 04 03 02 01
BE 01 02 03 04
注:每个地址存1个字节,每个字有4个字节。216进制数是1个字节(0xFF=11111111)。

例子2:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为

1
2
3
4
5
6
    big-endian  little-endian
0x0000 0x12 0xcd
0x0001 0x23 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86系列的CPU都是little-endian的字节序。

使用方法是放在fmt的第一个位置,就像‘@5s6sif’

<QqQqQ8s64sQqqq

1
2
3
4
5
6
7
8
9
10
11
12
13
解释:
< : little-endain是小头端编码方式,即内存的低位对应数值低位;内存地址低的地方存数据的末尾(即低字节)
Q : 第一个字符,长度为unsigned long long;最大值为18446744073709551615
q : 第二个字符,长度为long long;最大值为9223372036854775807
Q : 第三个字符
q : 第四个字符
Q : 第五个字符
8s : 第六个字符;长度为8的字符串;
64s:第七个字符;长度为64的字符串;
Q : 第八个字符
q : 第九个字符
q : 第十个字符
q : 第十一个字符

Python之socket(套接字)

https://www.cnblogs.com/fanweibin/p/5053328.html

一、概述

​ 传输层实现端到端的通信,因此,每一个传输层连接有两个端点。那么传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口。 传输层连接的端点叫做套接字(socket)。

根据RFC793的定义:端口号拼接到IP地址就构成了套接字。所谓套接字,实际上是一个通信端点,每个套接字都有一个套接字序号,包括主机的IP地址与一个16位的主机端口号,即形如(主机IP地址:端口号)。例如,如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。

​ 总之,套接字Socket=(IP地址:端口号),套接字的表示方法是点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。

​ 套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点。通信时,其中的一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过网络接口卡的传输介质将这段信息发送给另一台主机的Socket中,使这段信息能传送到其他程序中。因此,两个应用程序之间的数据传输要通过套接字来完成。

​ 在网络应用程序设计时,由于TCP/IP的核心内容被封装在操作系统中,如果应用程序要使用TCP/IP,可以通过系统提供的TCP/IP的编程接口来实现。 在Windows环境下,网络应用程序编程接口称作Windows Socket。为了支持用户开发面向应用的通信程序,大部分系统都提供了一组基于TCP或者UDP的应用程序编程接口(API),该接口通常以一组函数的形式出现,也称为套接字(Socket)。

分类

为了满足不同的通信程序对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接字,以供用户在设计网络应用程序时根据不同的要求来选择。这三种套接为:流式套接字(SOCK-STREAM)、数据报套接字(SOCK-DGRAM)和原始套接字(SOCK-RAW)。

(1)流式套接字。它提供了一种可靠的、面向连接的双向数据传输服务,实现了数据无差错、无重复的发送。流式套接字内设流量控制,被传输的数据看作是无记录边界的字节流。在TCP/IP协议簇中,使用TCP协议来实现字节流的传输,当用户想要发送大批量的数据或者对数据传输有较高的要求时,可以使用流式套接字。

(2)数据报套接字。它提供了一种无连接、不可靠的双向数据传输服务。数据包以独立的形式被发送,并且保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端按发送顺序接收数据。在TCP/IP协议簇中,使用UDP协议来实现数据报套接字。在出现差错的可能性较小或允许部分传输出错的应用场合,可以使用数据报套接字进行数据传输,这样通信的效率较高。

(3)原始套接字。该套接字允许对较低层协议(如IP或ICMP)进行直接访问,常用于网络协议分析,检验新的网络协议实现,也可用于测试新配置或安装的网络设备。

套接字属性

套接字的特性由3个属性确定,他们是:域、类型、协议

域指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络

套接字类型

一个套接字可能有多种不同的通信方式

流套接字,流套接字提供一个有序,可靠,双向节流的链接,流套接字由类型SOCK_STREAM指定,它是在AF_INET域中通过TCP/IP链接实现的,这就是套接字类型(其实就是通信方式)

与流套接字相反,由类型SOCK_DGRAM指定的数据报套接字不建立和维持一个连接,它对可以发送的数据长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失,复制或乱序

最后一个是套接字协议,通常使用默认就可以了(也就是最后一个参数填0)

调用流程:

bind():指定本地地址。一个套接字用socket()创建后,它其实还没有与任何特定的本地或目的地址相关联。在很多情况下,应用程序并不关心它们使用的本地地址,这时就可以不用调用bind指定本地的地址,而由协议软件为它们选择一个。但是,在某个知名端口(Well-known Port)上操作的服务器进程必须要对系统指定本地端口。所以一旦创建了一个套接字,服务器就必须使用bind()系统调用为套接字建立一个本地地址。

connect():将套接字连接到目的地址。初始创建的套接字并未与任何外地目的地址关联。客户机可以调用connect()为套接字绑定一个永久的目的地址,将它置于已连接状态。对数据流方式的套接字,必须在传输数据前,调用connect()构造一个与目的地的TCP连接,并在不能构造连接时返回一个差错代码。如果是数据报方式,则不是必须在传输数据前调用connect。如果调用了connect(),也并不像数据流方式那样发送请求建连的报文,而是只在本地存储目的地址,以后该socket上发送的所有数据都送往这个地址,程序员就可以免去为每一次发送数据都指定目的地址的麻烦。

listen():设置等待连接状态。对于一个服务器的程序,当申请到套接字,并调用bind()与本地地址绑定后,就应该等待某个客户机的程序来要求连接。listen()就是把一个套接字设置为这种状态的函数。

accept():接受连接请求。服务器进程使用系统调用socket,bind和listen创建一个套接字,将它绑定到知名的端口,并指定连接请求的队列长度。然后,服务器调用accept进入等待状态,直到到达一个连接请求。

send()/recv()和sendto()/recvfrom():发送和接收数据 。在数据流方式中,一个连接建立以后,或者在数据报方式下,调用了connect()进行了套接字与目的地址的绑定后,就可以调用send()和reev()函数进行数据传输。

socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:

  • file模块是针对某个指定文件进行【打开】【读写】【关闭】
  • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

创建套接字

socket系统调用创建一个套接字并返回一个描述符,该描述符可以用来访问该套接字

创建的套接字是一条通信线路的一个端点,domain参数指定协议族(使用的网络介质),type参数指定这个套接字的通信类型(通信方式),protocot参数指定使用的协议

domain参数可以指定如下协议族

AF_UNIX UNIX域协议(文件系统套接字)

AF_INET ARPA因特网协议

AF_ISSO ISO标准协议

AF_NS Xerox网络协议

AF_IPX Novell IPX协议

AF_APPLETALK Appletalk DDS协议

最常用的套接字域是AF_UNIX和AF_INET,前者用于通过UNIX和Linux文件系统实现本地套接字

socket函数的第二个参数type指定用于新套接字的特性,它的取值包括SOCK_STREAM和SOCK_DGRAM

SOCK_STREAM是一个有序,可靠,面向连接的双向字节流,一般用这个

最后一个protocol参数,将参数设为0表示使用默认协议。

socket_server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket

ip_port = ('127.0.0.1',9999)

sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
print 'server waiting...'
conn,addr = sk.accept()

client_data = conn.recv(1024)
print client_data
conn.sendall('不要回答,不要回答,不要回答')

conn.close()

#socket server

socket_client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ('127.0.0.1',9999)

sk = socket.socket()
sk.connect(ip_port)

sk.sendall('请求占领地球')

server_reply = sk.recv(1024)
print server_reply

sk.close()

#socket client

Web实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python
#coding:utf-8
import socket

def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n")
client.send("Hello, World")

def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8080))
sock.listen(5)

while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()

if __name__ == '__main__':
main()

#Web实例
#执行后浏览器打开:http://10.244.25.44:8080

二、解释

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
参数一:地址簇

  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

UDP demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)

while True:
data = sk.recv(1024)
print data




import socket
ip_port = ('127.0.0.1',9999)

sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
inp = raw_input('数据:').strip()
if inp == 'exit':
break
sk.sendto(inp,ip_port)

sk.close()
#UDP demo

sk.bind(address)

1
s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog)

1
2
3
4
开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列

sk.setblocking(bool)

1
是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

sk.accept()

1
2
3
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

  接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address)

1
 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address)

1
同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

sk.close()

1
关闭套接字

sk.recv(bufsize[,flag])

1
接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.recvfrom(bufsize[.flag])

1
 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(string[,flag])

1
string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

sk.sendall(string[,flag])

1
string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

sk.sendto(string[,flag],address)

1
将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout)

1
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

sk.getpeername()

1
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.getsockname()

1
返回套接字自己的地址。通常是一个元组(ipaddr,port)

sk.fileno()

1
套接字的文件描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)

while True:
data = sk.recv(1024)
print data




import socket
ip_port = ('127.0.0.1',9999)

sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
inp = raw_input('数据:').strip()
if inp == 'exit':
break
sk.sendto(inp,ip_port)

sk.close()

#UDP Demo

三、实例

智能机器人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
# -*- coding:utf-8 -*-


import socket

ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
conn,address = sk.accept()
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '0':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.')
conn.close()

#服务端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket


ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)

while True:
data = sk.recv(1024)
print 'receive:',data
inp = raw_input('please input:')
sk.sendall(inp)
if inp == 'exit':
break

sk.close()

#客户端

具体实现脚本:

gateway内部:

Q uint64
q int64
64s —> 64长字符串

苏宁豆芽图片20200526202942946

Gateway的参数 占位
MessSeq uint64
SendTime int64
MessType uint64
MetaLen int64
CheckSum uint64
Version uint32
Flag [4]byte
Name [64]byte
Offset uint64
WriteDataLen int64
ReadDataLen int64
Result int64

注:

之前Flag [8]byte现在拆分成uint32(Version)和[4]byte(Flag)实际还是8位,上面显示当前gateway是12个参数。从占用空间角度去数就没问题的,所以下面的gw_rw.py是11个参数就不需要调整了

gw_rw.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import socket	
import struct

class GwFd(object):
def __init__(self):
self.gwfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#IPv4(默认),流式socket
self.fmt = "<QqQqQ8s64sQqqq"#q和Q只在机器支持64位操作时有意思,11个字符
def connect(self, ip, port):
self.gwfd.connect((ip,port))
def write(self, volname,offset,buflen,buf):
sendMsg = struct.pack(self.fmt, 0, 0, 3002, 0, 0, "", volname, offset, buflen, 0, 0);#按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
#data = sendMsg+buf
self.gwfd.send(sendMsg+buf)#sendMsg通过struct.pack打包成字符串发给服务端gateway

recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11 = struct.unpack(self.fmt, recvMsg)
#recvMsg表示接收了gateway的消息进行struct.unpack
return r10
def read(self,volname,offset,buflen):
sendMsg = struct.pack(self.fmt, 0, 0, 3001, 0, 0, "", volname, offset, 0, buflen, 0);
self.gwfd.send(sendMsg)
recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
if len(recvMsg) != struct.calcsize(self.fmt):
print("read head error")
return ""
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11 = struct.unpack(self.fmt, recvMsg)
if r11 != 0:
print("read %s res error" % volname)
return ""
total_data=""
while True:
recvMsg2 = self.gwfd.recv(buflen)
total_data = total_data+recvMsg2
if len(total_data) == r9: break
if len(total_data) != buflen:
print("data len error")
return ""
return ''.join(total_data)
def openVolume(self, volname):
sendMsg = struct.pack(self.fmt, 0, 0, 3006, 0, 0, "", volname, 0, 0, 0, 0);
self.gwfd.send(sendMsg)

recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11 = struct.unpack(self.fmt, recvMsg)
return r11
def size(self, volname):
sendMsg = struct.pack(self.fmt, 0, 0, 3004, 0, 0, "", volname, 0, 0, 0, 0);
self.gwfd.send(sendMsg)

recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11 = struct.unpack(self.fmt, recvMsg)
return r11
def closeVolume(self, volname):
sendMsg = struct.pack(self.fmt, 0, 0, 3007, 0, 0, "", volname, 0, 0, 0, 0);
self.gwfd.send(sendMsg)

recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11 = struct.unpack(self.fmt, recvMsg)
return r11
def removeVolume(self, volname):
sendMsg = struct.pack(self.fmt, 0, 0, 3003, 0, 0, "", volname, 0, 0, 0, 0);
self.gwfd.send(sendMsg)

recvMsg = self.gwfd.recv(struct.calcsize(self.fmt))
r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11 = struct.unpack(self.fmt, recvMsg)
return r11

def close(self):
self.gwfd.close()

gw_rw_test8.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
import gw_rw
import sys
import zlib
import hashlib
import optparse
import requests
import signal
import time
import copy
from multiprocessing import Process, JoinableQueue
from threading import Thread
# from Queue import Queue

q = JoinableQueue() #JoinableQueue创建队进程池
logQ = JoinableQueue() #JoinableQueue创建队进程池

volUrl = "http://%s:8686/region/pool/vol?volume=%s"


class Task:
def __init__(self, begin, end):
self.begin = begin
self.end = end


class SNBS:
def __init__(self, gw, target, opened=True):#主要用来定义全局变量
self.gw = gw
self.target = target
self.opened = opened
self.fd = None

def clear(self):
self.fd = None
self.opened = True
return self

def connect(self):
if self.fd != None:
self.disconnect()

self.fd = gw_rw.GwFd() #gw_rw为gw_rw.py,GwFd为gw_rw.py中class
self.fd.connect(self.gw, 8585)

def disconnect(self):
if self.fd != None:
self.fd.close()
self.fd = None

def open(self):
self.connect()
if not self.opened:
ret = self.fd.openVolume(self.target)
if ret != 0:
err = "target: " + self.target + " gw: " + \
self.gw + " ret: " + str(ret) + " open fail"
print(err)
raise Exception(err)

def close(self):
if not self.opened:
self.fd.removeVolume(self.target)
self.fd.closeVolume(self.target)

if self.fd != None:
self.disconnect()

def read(self, begin, length):
return self.fd.read(self.target, begin, length)

def size(self):
return self.fd.size(self.target)


class IMG:
def __init__(self, localvol):
self.file = localvol
self.fd = None

def clear(self):
self.fd = None

def open(self):
if self.fd != None:
self.close()
self.fd = open(self.file, 'r')

def close(self):
if self.fd != None:
self.fd.close()
self.fd = None

def read(self, begin, length):
self.fd.seek(begin, 0)
return self.fd.read(length)


class Vol:
def __init__(self, name, type, gw=""):
self.name = name
self.type = type
self.gw = gw

# def vol(vol):
# if vol.type=="origin":
# return SNBS(vol.gw, vol.Name, True)
# elif vol.type=="local":
# pass


def main(gw, target, source, sourceType, blocksize, skip, count, hashMethod, process_num):
print("target:", target, "source:", source, "type:", sourceType,
"bs:", blocksize, "skip:", skip, "count:", count,
"hash:", hashMethod, "processNum:", process_num)

tar = None
src = None
try:
gwport = queryGW(gw, target)
if gwport == "":
targetGW = gw
opened = False
else:
targetGW = gw.split(":")[0]
opened = True
print("target: %s gw: %s opend: %s" % (target, targetGW, str(opened)))
tar = SNBS(targetGW, target, opened)
tar.open()
size = tar.size()

if sourceType == "origin":
gwport = queryGW(gw, source)
if gwport == "":
sourceGW = gw
opened = False
else:
sourceGW = gw.split(":")[0]
opened = True
print("source: %s gw: %s opend: %s" %
(source, sourceGW, str(opened)))
src = SNBS(sourceGW, source, opened)
runTask = localCheck
elif sourceType == "local":
src = IMG(source)
runTask = localCheck
src.open()
begin = 0
if skip != 0:
begin = blocksize * skip

if begin > size:
print("begin offset:", begin, "target size:", size)
exit(1)

if count != -1:
end = blocksize * (skip + count)
if end > size:
print("end offset:", end, "target size:", size, "set end:", size)
end = min(end, size)
else:
end = size

for i in range(begin + blocksize, end, blocksize):
task = Task(begin, i)
q.put(task)
begin = i

if begin != end:
task = Task(begin, end)
q.put(task)
print("generate task finish")
t = Thread(target=display, args=(q.qsize(),))
t.setDaemon(True)
t.start()

t = Thread(target=displayLog, args=(logQ,))
t.setDaemon(True)
t.start()

for _ in range(process_num):
p = Process(target=runTask, args=(
copy.deepcopy(tar).clear(), copy.deepcopy(src).clear(),
hashMethod, q, logQ))
p.daemon = True
p.start()
q.join()
print("Finished")

except KeyboardInterrupt:
while True:
try:
q.get_nowait()
q.task_done()
except:
print("Terminated")
break

finally:
if tar != None:
print("closing target: %s gw: %s opend: %s" %
(tar.target, tar.gw, str(tar.opened)))
tar.close()
if src != None:
print("closing target: %s gw: %s opend: %s" %
(src.target, src.gw, str(src.opened)))
src.close()


def queryGW(gw, target):
r = requests.get(volUrl % (gw, target), timeout=5)
res = r.json()
if res.get("error") != None:
raise Exception(" gw: " + gw + "vol: " + target + " not exist")
return r.json().get("Owner")


def localCheck(target, source, hashMethod, q, logQ):
try:
work = True
target.open()
source.open()

while True:
try:
task = q.get()

if work:
str1 = target.read(task.begin, task.end - task.begin)
str2 = source.read(task.begin, task.end - task.begin)
errTimes = 0
for method in hashMethod:
if hashSet[method](str1) != hashSet[method](str2):
errTimes += 1
logQ.put("error offset:%d, len:%d, method:%s" %
(task.begin, task.end - task.begin, method))

if errTimes != 0:
work = False
except KeyboardInterrupt:
exit(254)
finally:
q.task_done()
finally:
target.close()
source.close()


def originCheck(target, source, gw, hashMethod, q, logQ):
try:
fd = gw_rw.GwFd()
fd.connect(gw, 8585)
while True:
try:
task = q.get()
str1 = fd.read(target, task.begin, task.end - task.begin)
str2 = fd.read(source, task.begin, task.end - task.begin)
# print(str1, str2)
if hashMethod(str1) != hashMethod(str2):
logQ.put("error offset:%d, len:%d" %
(task.begin, task.end - task.begin))
except KeyboardInterrupt:
exit(254)

finally:
q.task_done()
finally:
fd.close()


def displayLog(logQ):
while True:
log = logQ.get()
print(log)


def display(total):
while not q.empty():
print("================ task left:%d total:%d ================" %
(q.qsize(), total))
time.sleep(1)


def hashcheck(method):
def check(string):
md5 = method()
md5.update(string)
return md5.hexdigest()
return check


hashSet = {"crc32": zlib.crc32,
"md5": hashcheck(hashlib.md5),
"sha1": hashcheck(hashlib.sha1)}

m = hashlib.md5()


def aaa(b):
m.update(b)
id_md5 = m.hexdigest()
print id_md5


if __name__ == "__main__":

p = optparse.OptionParser()
p.add_option('--gw', '-g') # gateway IP
p.add_option('--target', '-t') # target volume
p.add_option('--source', '-s') # source volume
# source volume type (origin/local)
p.add_option('--type', '-T', default="origin")
p.add_option('--blocksize', '-b', default=4194304) # blocksize
# skip k of blocksize, begin offset = blocksize * skip
p.add_option('--skip', '-k', default=0)
p.add_option('--count', '-c', default=-1) # block count
# verify hash method, crc32 only now
p.add_option('--verify', '-v', default="crc32")
p.add_option('--num', '-n', default=4) # number of process
options, args = p.parse_args()
gw = options.gw
target = options.target
source = options.source
sourceType = options.type
blocksize = int(options.blocksize)
skip = int(options.skip)
count = int(options.count)
num = int(options.num)

h = options.verify
hashMethod = set()
if h != "crc32" and h != "md5" and h != "sha1" and h != "all":
raise Exception("unsupport verify hash method: %s" % h)
if h == "crc32" or h == "all":
hashMethod.add("crc32")
if h == "md5" or h == "all":
hashMethod.add("md5")
if h == "sha1" or h == "all":
hashMethod.add("sha1")
print("hashMethod:", hashMethod)
main(gw, target, source, sourceType, blocksize, skip, count, hashMethod, num)

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 有时间复现一下磁盘损坏的问题
2. 文件在 全闪.6 /root/verify,本地没测。。。。 运行案例:python gw_rw_test2.py -t dev0 -s dev160 -T origin -g 10.37.2.20 -n 2 -v crc32 -b 4096 -k 400 -c 40000
3. 各个参数
p. add_option('--gw','-g')# gateway IP
p. add_option('--target','-t")# target volume
p. add_option('--source','-s')# source volume
p. add_option("--type','-T', default="origin")# source volume type(origin/local)
p. add_option('--blocksize','-b', default=4194304)# blocksize
p. add_option('--skip','-k", default=e)# skip k of blocksize, begin offset=blocksize* skip
p. add_option('--count','-c', default=-1)# block count
p. add_option('--verify','-v', default="crc32")# verify hash method, crc32 only now
p. add option('--num','-n', default=4)# number of process
-t是克隆卷/上传卷
-s是原卷/本地卷

-v all ---> md5 + crc32
-v md5 ---> md5
-v crc32 ---> crc32
-v sha1 --->sha1

grpc(gateway和master之间,查询卷之类),因为效率的问题

tcp接口

HTTP接口(应用层)-

赞赏一下吧~