Python网络编程—socket(一)
从今天开始python基础就介绍完毕了,下面我们将进阶到socket网络编程的介绍,那么socket是什么呢?我们带着这个问题开始今天的介绍:
一、socket初探
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket其实也是一种特殊的文件,一些socket函数就是对其进行的操作(读/写、打开、关闭)
那么socket对文件操作和file对文件操作有什么区别呢?
file模块是针对某个指定文件进行【打开】【读写】【关闭】
socket模块时针对服务器和客户端Socket进行【打开】【读写】【关闭】
在介绍socket之前我们复习一下网络的OSI七层协议,方便后面的理解socket,请看下图:

TCP/IP协议是主机接入互联网以及接入互联网的两台主机通信的标准,socket直接通信同样是遵循这个标准,下面介绍socket服务端和客户端直接是如何通信的,请看下图:

下面我给大家解释一下上面这张图,我将socket之间的通信比作是电话之间通信,客户端和服务端通信必须有一个设备也就是手机或电话,然后服务端要绑定自己的手机卡并开机等待客户端打电话过来,客户端打电话过来,进行通话,这时就进入通话死循环直到双方挂电话。其实socket通信就好比打电话,首先实例化socket对象,调用socket对象中的方法,然后绑定本机的IP地址和端口,开启连接池listen(),然后阻塞accept(),直到客户端连接过来,客户端同样实例化socket对象,然后调用服务端通道conn()与服务端建立连接,这时客户端就和服务器端建立了连接,进入收发消息的死循环,直到有一方close()关闭连接,这就是整个socket的通信过程。
下面来写一个单进程的socket通信,代码如下:
Socket_Server端:
|
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
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import socket #导入socket模块ip_port=('127.0.0.1',9999) #定义主机,端口号s=socket.socket() #实例化socket对象(买手机)s.bind(ip_port) #绑定IP端口(买手机卡)s.listen(5) #建立5个连接池,等待接收请求,挂起连接#等待电话conn,addr=s.accept() #conn就是建立通信,负责收发消息的通道,每次只处理一个请求, #accept是阻塞请求,当第二个请求来的时候会进入listen连接池 #挂起等待处理while True: try: recv_data = conn.recv(1024) #收消息 if len(recv_data) == 0:break #如果客户输入为空的话退出 if recv_data == 'exit':break #客户端退出,服务端跟着退出 send_data = recv_data.upper() print(send_data) conn.send(send_data) #回复消息 except Exception: breakconn.close() #挂电话,关闭连接通道 |
Socket-Client端:
|
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 socketip_port=('127.0.0.1',9999)s=socket.socket()s.connect(ip_port) #这里的connect实际是服务端的conn,与服务端建立连接while True: send_data=input(">>>: ").strip() #发送消息 if send_data == 'exit':break if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf-8')) #收消息 recv_data=s.recv(1024) print(str(recv_data,encoding='utf-8'))#挂电话s.close() |
注意:1、基于python3.*版本的socket只能收发字节bytes(),python2.*版本是可以发送字符串str()的。
2、s.accept()和s.recv()是阻塞的,前提必须基于连接正常的情况下,连接不正常就没有阻塞的情况发生。
通过实验代码发现了一个bug,就是客户端退出,服务器端也跟着退出,那怎么让服务器端不退出呢?只要修改一个地方就可以了
|
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
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import socketip_port=('127.0.0.1',9999) s=socket.socket()s.bind(ip_port)s.listen(5) #建立5个连接池,等待接收请求,挂起连接while True: conn,addr=s.accept() while True: try: recv_data = conn.recv(1024) if len(recv_data) == 0:break if recv_data == 'exit':break send_data = recv_data.upper() print(send_data) conn.send(send_data) except Exception: break conn.close() |
下面我们在举一个socket的例子,我们通过socket来写一个远程执行命令的程序:
Socket_server端:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketimport subprocessip_port=('127.0.0.1',9999)s=socket.socket()s.bind(ip_port)s.listen(5)while True: conn,addr=s.accept() while True: try: recv_data=conn.recv(1024) if len(recv_data) == 0:break p=subprocess.Popen(str(recv_data,encoding='utf8'),shell=True,stdout=subprocess.PIPE) res=p.stdout.read() if len(res) == 0: send_data='cmd err' else: send_data=str(res,encoding='gbk') send_data=bytes(send_data,encoding='utf8') conn.send(send_data) except Exception: break conn.close() |
Socket_Client端:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import socketip_port=('127.0.0.1',9999)s=socket.socket()s.connect(ip_port)while True: send_data=input(">>>: ").strip() if send_data == 'exit':break if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf-8')) recv_data=s.recv(1024) print(str(recv_data,encoding='utf-8'))s.close() |
执行结果:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>>: lscmd err #执行错误命令报错>>>: dir2016/07/04 23:49 <DIR> .2016/07/04 23:49 <DIR> ..2016/07/03 17:55 485 socket_client.py2016/07/04 23:44 489 socket_client1.py2016/07/04 23:34 1,088 socket_server.py2016/07/04 23:49 937 socket_server_cmd.py2016/07/03 17:58 650 thread socket server.py>>>:ipconfig /all >>>:dir#当我们执行完ipconfig /all命令后,再执行dir命令,还是显示的ipconfig /all的命令结果,这就涉及到了socket的一个粘包现象 |
大家想一下我们如何来解决socket的粘包现象,就是现在还无法判断客户端发来多少数据,导致一次无法全部接收完,请看下图:

就类似两个水桶注水一样,水桶A往水桶B注水,第一次注水10000毫升,而水桶B每次只能接收1024毫升,这时水桶A里又注入新的水,因为上次的10000毫升的水还没有完全注入水桶B中,所以还会接着注上次没有注完的水,直到将10000毫升的水注完为止,才会将新注入的水进入到B中。这就是socket粘包原理,那么怎么解决这种问题呢?
试想我们每次将注入多少水告诉水桶B,然后让水桶B做好接收同样大小水的准备就可以解决问题,有多少接收多少,具体代码如下:
Socket_Server端:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketimport subprocessip_port=('127.0.0.1',9999)s=socket.socket() #绑定协议,生成套接字s.bind(ip_port) #绑定ip+协议+端口:用来唯一标识一个进程,ip_port必须是元组格式s.listen(5) #定义最大可以挂起的连接数while True: #用来重复接收新的链接 conn,addr=s.accept() #接收客户端胡链接请求,返回conn(相当于一个特定胡链接),addr是客户端ip+port while True: #用来基于一个连接重复收发消息 try: #捕捉客户端异常关闭 recv_data=conn.recv(1024) #收消息,阻塞 if len(recv_data) == 0:break #客户端如果退出,服务端将收到空消息,退出 p=subprocess.Popen(str(recv_data,encoding='utf8'),shell=True,stdout=subprocess.PIPE) #执行系统命令,windows平台命令的标准输出是gbk编码,需要转换 res=p.stdout.read() #获取标准输出 if len(res) == 0: #执行错误命令,标准输出为空, send_data='cmd err' else: send_data=str(res,encoding='gbk') #命令执行ok,字节gbk---->str---->字节utf-8 send_data=bytes(send_data,encoding='utf8') #解决粘包问题 ready_tag='Ready|%s' %len(send_data) conn.send(bytes(ready_tag,encoding='utf8')) #先将数据大小发送给客户端,让客户端等待接收 feedback=conn.recv(1024) #接收客户端确认信息 feedback=str(feedback,encoding='utf8') if feedback.startswith('Start'): conn.send(send_data) #发送命令的执行结果 except Exception: break conn.close() |
Socket_Client端:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketip_port=('127.0.0.1',9999)s=socket.socket()s.connect(ip_port) #链接服务端,如果服务已经存在一个好的连接,那么挂起while True: #基于connect建立的连接来循环发送消息 send_data=input(">>: ").strip() if send_data == 'exit':break if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) #解决粘包问题 ready_tag=s.recv(1024) #收取带数据长度的字节:Ready|9998 ready_tag=str(ready_tag,encoding='utf8') if ready_tag.startswith('Ready'): #判断是不是Ready开头的 msg_size=int(ready_tag.split('|')[-1]) #获取待接收数据长度 start_tag='Start' s.send(bytes(start_tag,encoding='utf8')) #客户端已经准备好,发送确认信息 #基于已经收到的待接收数据长度,循环接收数据 recv_size=0 recv_msg=b'' while recv_size < msg_size: recv_data=s.recv(1024) recv_msg+=recv_data recv_size+=len(recv_data) print('MSG SIZE %s RECE SIZE %s' %(msg_size,recv_size)) print(str(recv_msg,encoding='utf8'))s.close() |
上面是举的几个例子,下面介绍一下socket对象的一些功能:
1, sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
1
2
3
4
5
6
7
8
|
参数一:地址簇 socket.AF_INET #IPv4(默认) socket.AF_INET6 #IPv6 socket.AF_UNIX #只能够用于单一的UNIX系统几件通信参数二:类型 socket.SOCK_STREAM #流式socket,for TCP(默认) socket.SOCK_DGRAM #数据报式,for UDP |
2,sk.bind(address)
s.bind(address):将套接字绑定到地址。address地址的格式取决于地址簇。在AF_INET下,以元组(host,port)的形式表示地址。
3,sk.listen(backlog)
开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5,这个值不能无限大,因为要在内核中维护连接队列。
4,sk.setbloking(bool)
是否阻塞(默认为True),如果设置False,那么accept和recv时一旦无数据,则报错。
5,sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据;address是连接客户端的地址,接收TCP客户的连接(阻塞式)等待连接的到来。
6,sk.connect(address)
连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
7,sk.connect_ex(address)
同上,只不过会有返回值,连接成功时返回0,连接失败时候返回编码,例如:10061
8,sk.close()
关闭套接字
9,sk.recv(bufsize)
接受套接字的数据,数据以字节形式返回,bufsize指定最多可以接收的数量。
10,sk.send(bytes)
将字节数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
11,sk.sendall(bytes)
将字节的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
内部通过递归调用send,将所有内容发送出去。
12,sk.settimeout(timeout)
设置套接字操作的超时时间,timeout是一个浮点数,单位是秒。值为None表示没有超时期,一般超时期应该在刚创建套接字时设置,因为他们可能用于连接的操作(如client连接最多等待5s)
socket的功能就介绍到这里,更多方法请参考源码。
二、SocketServer模块
通过上面的例子我们可以发现我们的程序,都是单进程来工作,服务端一次只能处理一个请求,然而现实生产的情况下,是要支持多并发的连接,下面我们就介绍socket如何处理并发请求。
SocketServer内部使用IO多路复用以及"多线程","多进程",从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接服务器时,Socket服务端都会在服务器上创建一个"线程"或者"进程"专门负责处理当前客户端的所有请求。
Threading TCPServer:ThreadingTCPServer实现的Socket服务器内部会为每个client创建一个"线程",该线程用来和客户端进行交互。
1、ThreadingTCPServer基础
如何使用ThreadingTCPServer:
创建一个继承自SocketServer.BaseRequestHandler的类;
类中必须定义一个名称为handle的方法;
启动ThreadingTCPServer。
请看下面的例子:
Socket_Server端:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): #print self.request,self.client_address,self.server self.request.sendall(bytes('hello,world!!',encoding='utf-8')) while True: data = self.request.recv(1024) if len(data) ==0:break print("[%s] says %s"%(self.client_address,data.decode() )) self.request.sendall(data.upper())if __name__=='__main__': server=socketserver.ThreadingTCPServer(('127.0.0.1',9999),MyServer) server.serve_forever() |
Socket_Client端:
|
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 socketip_port=('127.0.0.1',9999)s=socket.socket()s.connect(ip_port)welcome_msg = s.recv(1024)print("from server:",welcome_msg.decode())while True: send_data=input(">>: ").strip() if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) recv_data=s.recv(1024) print(str(recv_data,encoding='utf8'))s.close() |
2、ThreadingTCPServer源码剖析

内部调用流程为:
启动服务端程序;
执行TCPServer.__init__方法,创建服务端Socket对象并绑定IP和端口;
执行BaseServer.__init__方法,将自定义的继承自SocketServer.BaseRequestHandler的类MyRequestHandle赋值给self.RequestHandlerClass;
执行BaseServer.server_forever方法,While循环一直监听是否有客户端请求到达;
当客户端请求到达服务器;
执行ThreadingMixIn.process_request方法,创建一个"线程"用来处理请求;
执行ThreadingMixIn.process_request_thread方法;
执行BaseServer.finish_request方法,执行self.RequestHandlerClass(),即:执行自定义MyRequestHandler的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用MyRequestHandler的Handle方法)
大概就是这个执行过程,下面在我们将上面的远程执行命令的脚本做些修改让它可以处理多并发的请求:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketserverimport subprocess class MyServer(socketserver.BaseRequestHandler): def handle(self): # print self.request,self.client_address,self.server self.request.sendall(bytes('欢迎致电 10086,请输入1xxx,0转人工服务.',encoding="utf-8")) while True: data = self.request.recv(1024) if len(data) == 0:break print("[%s] says:%s" % (self.client_address,data.decode() )) cmd = subprocess.Popen(data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) cmd_res =cmd.stdout.read() if not cmd_res: cmd_res = cmd.stderr.read() if len(cmd_res) == 0: #cmd has not output cmd_res = bytes("cmd has output",encoding="utf-8") self.request.send(cmd_res )if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer) server.serve_forever() |
下面将今天介绍的内容都串起来写个小程序,我们大家都使用过FTP来上传和下载文件,现在我们尝试自己来写一个FTP的功能,这里只实验一个FTP上传的功能:
Socket_Server端:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketserver,jsonclass MyServer(socketserver.BaseRequestHandler): def handle(self): # print self.request,self.client_address,self.server self.request.sendall(bytes('欢迎致电 10086,请输入1xxx,0转人工服务.',encoding="utf-8")) while True: data = self.request.recv(1024) if len(data) == 0:break print("data", data) print("[%s] says:%s" % (self.client_address,data.decode() )) task_data = json.loads( data.decode() ) task_action = task_data.get("action") if hasattr(self, "task_%s"%task_action): func = getattr(self,"task_%s" %task_action) func(task_data) else: print("task action is not supported",task_action) def task_put(self,*args,**kwargs): print("---put",args,kwargs) filename = args[0].get('filename') filesize = args[0].get('file_size') server_response = {"status":200} self.request.send(bytes( json.dumps(server_response), encoding='utf-8' )) f = open(filename,'wb') recv_size = 0 while recv_size < filesize: data = self.request.recv(4096) f.write(data) recv_size += len(data) print('filesize: %s recvsize:%s' % (filesize,recv_size)) print("file recv success") f.close()if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('0.0.0.0',8009),MyServer) server.serve_forever() |
Socket_Client端:
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import socketimport os ,jsonip_port=('192.168.11.150',8009)s=socket.socket()s.connect(ip_port)welcome_msg = s.recv(1024)print("from server:",welcome_msg.decode())while True: send_data=input(">>: ").strip() if len(send_data) == 0:continue cmd_list = send_data.split() if len(cmd_list) <2:continue task_type = cmd_list[0] if task_type == 'put': abs_filepath = cmd_list[1] if os.path.isfile(abs_filepath): file_size = os.stat(abs_filepath).st_size filename = abs_filepath.split("\\")[-1] print('file:%s size:%s' %(abs_filepath,file_size)) msg_data = {"action":"put", "filename":filename, "file_size":file_size} s.send( bytes(json.dumps(msg_data),encoding="utf-8") ) server_confirmation_msg = s.recv(1024) confirm_data = json.loads(server_confirmation_msg.decode()) if confirm_data['status'] ==200: print("start sending file ",filename) f = open(abs_filepath,'rb') for line in f: s.send(line) print("send file done ") else: print("\033[31;1mfile [%s] is not exist\033[0m" % abs_filepath) continue else: print("doesn't support task type",task_type) continue recv_data=s.recv(1024) print(str(recv_data,encoding='utf8'))s.close() |
在这里补充一个如何在终端上打印进度条的代码,供大家参考:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#!/usr/bin/env python#-*- coding:utf-8-*-import timeimport sysdef progress_test(): bar_length = 20 for percent in range(0, 101): hashes = '#' * int(percent / 100.0 * bar_length) spaces = ' ' * (bar_length - len(hashes)) sys.stdout.write("\rPercent: [%s] %d%%" % (hashes + spaces, percent)) sys.stdout.flush() time.sleep(0.1)progress_test() |
今天的socket就介绍到这里,后续会有Socket的进阶,请大家关注。
Python网络编程—socket(一)的更多相关文章
- Day07 - Python 网络编程 Socket
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
- Python网络编程socket
网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...
- python网络编程-socket编程
一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...
- python网络编程socket /socketserver
提起网络编程,不同于web编程,它主要是C/S架构,也就是服务器.客户端结构的.对于初学者而言,最需要理解的不是网络的概念,而是python对于网络编程都提供了些什么模块和功能.不同于计算机发展的初级 ...
- python --- 网络编程Socket
网络编程 定义:所为网络编程即是对信息的发送和接收. 主要工作: (1)发送端:将信息以规定的协议组装成数据包. (2)接收端:对收到的数据包解析,以提取所需要的信息. Socket:两个在网络上的程 ...
- Python网络编程-Socket简单通信(及python实现远程文件发送)
学习python中使用python进行网络编程,编写简单的客户端和服务器端进行通信,大部分内容来源于网络教程,这里进行总结供以后查阅. 先介绍下TCP的三次握手: 1,简单的发送消息: 服务器端: i ...
- Day10 Python网络编程 Socket编程
一.客户端/服务器架构 1.C/S架构,包括: 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务)[QQ,SSH,MySQL,FTP] 2.C/S架构与socket的关系: 我们学习soc ...
- python网络编程——socket基础篇
python的网络编程比c语言简单许多, 封装许多底层的实现细节, 方便程序员使用的同时, 也使程序员比较难了解一些底层的东西. 1 TCP/IP 要想理解socket,首先得熟悉一下TCP/IP协议 ...
- Python 网络编程——socket
一 客户端/服务器架构 客户端(Client)服务器(Server)架构,即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 理想/目标状态—— 最常用的软件服务器是 ...
- Python网络编程—socket(二)
http://www.cnblogs.com/phennry/p/5645369.html 接着上篇博客我们继续介绍socket网络编程,今天主要介绍的内容:IO多路复用.多线程.补充知识点. 一.I ...
随机推荐
- bzoj 1575: [Usaco2009 Jan]气象牛Baric【dp】
完了不会dp了 设f[i][j]为以i结尾,有j个时的最优值,辅助数组g[i][j]为s选了i和j,i~j中的误差值 转移是f[j][i]=min(f[k][i-1]+g[k][j]) #includ ...
- python实现对某招聘网接口测试获取平台信息
"""通过接口测试的技术获取某招聘网平台的资料"""url = "https://www.lagou.com/jobs/posit ...
- AOP面向方面编程---postsharp
PostSharp是一个用于在.NET平台上实现AOP(Aspect-Oriented Programming,面向方面编程)的框架,现通过简单的示例代码来演示如何使用postsharp. 1.新建一 ...
- Visual Studio 相关
基础配置: 背景色:豆沙绿(色调84 饱和度118 亮度205) 字体字号:Consolas 11号 离线下载方法: vs_enterprise.exe --layout c:\vs2017offl ...
- Markdown基本语法学习
Markdown是一种纯文本格式的标记语言.通过简单的标记语法,它可以使普通文本内容具有一定的格式. 创始人 John Gruber 的 Markdown 语法说明 Markdown 中文版语法说明 ...
- 从 C++ 到 Objective-C 的快速指南
简介 当我开始为iOS写代码的时候,我意识到,作为一个C++开发者,我必须花费更多的时间来弄清楚Objective-C中怪异的东西.这就是一个帮助C++专家的快速指南,能够使他们快速的掌握Apple的 ...
- 《从Paxos到ZooKeeper 分布式一致性原理与实践》阅读【Leader选举】
从3.4.0版本开始,zookeeper废弃了0.1.2这3种Leader选举算法,只保留了TCP版本的FastLeaderElection选举算法. 当ZooKeeper集群中的一台服务器出现以下两 ...
- Position属性四个值:static、fixed、absolute和relative的区别
1.static(静态定位):默认值.没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明). 2.relative(相对定位):生成相对 ...
- TCP/IP和UDP的比较
TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间,无法了解数据未达终点的状态.因此有必要增强网络层提供服务的服务质量. 2.引入传输层的原因 面向 ...
- select count(1) 和 select count(*)的区别
统计一个表T有多少行数据,通常写法是: 查询A:select count(*) from T 但也可以采用下面语句来查: 查询B:select count(1) from T 结果通常是一样的.那么二 ...