一.socket介绍

1>.TCP/IP协议

img

2>.跨网络的主机间通讯

  在建立通信连接的每一端,进程间的传输要有两个标志:IP地址和端口号,合称为套接字地址 socket address客户机套接字地址定义了一个唯一的客户进程服务器套接字地址定义了一个唯一的服务器进程

img

3>.什么是socket套接字

  套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。Socket:套接字,进程间通信IPC的一种实现,允许位于不同主机(或同一主机)上不同进程之间进行通信和数据交换,SocketAPI出现于1983年,4.2 BSD实现。Socket API:封装了内核中所提供的socket通信相关的系统调用。Python中提供了socket标准库,非常底层的接口库。

img

4>.协议族(Socket Domain)

AF表示Address Family,用于socket()第一个参数AF_INET:对应IPV4AF_INET6对应IPV6AF_UNIX同一主机不同进程之间通信时使用,对应Unix Domain Socket,windows没有。

5>.socket Type(根据使用的传输层协议)

SOCK_STREAM可靠的传递,面向连接的流套接字。默认值,TCP协议。SOCK_DGRAM不可靠的传递,无连接的数据报文套接字。UDP协议。
SOCK_RAW: 裸套接字,无须tcp或udp,APP直接通过IP包通信

6>.C/S编程

  Socket编程,需要两端,一般来说需要一个服务端、一个客户端,服务端称为Server,客户端称为Client。 这种编程模式也称为C/S编程。套接字相关的系统调用:socket(): 创建一个套接字bind(): 绑定IP和端口listen(): 监听accept(): 接收请求connect(): 请求连接建立write(): 发送read(): 接收close(): 关闭连接

img

7>.python中的socket常用方法

  socket.recv(bufsize[, flags])获取数据。默认是阻塞的方式socket.recvfrom(bufsize[, flags])获取数据,返回一个二元组(bytes, address)socket.recv_into(buffer[, nbytes[, flags]])获取到nbytes的数据后,存储到buffer中。如果 nbytes没有指定或0,将buffer大小的数据存入buffer中。返回接收的字节数。socket.recvfrom_into(buffer[, nbytes[, flags]])获取数据,返回一个二元组(bytes, address)到buffer中socket.send(bytes[, flags])TCP发送数据socket.sendall(bytes[, flags])TCP发送全部数据,成功返回Nonesocket.sendto(string[,flag],address)UDP发送数据socket.sendfile(file, offset=0, count=None)python 3.5版本开始,发送一个文件直到EOF,使用高性能的os.sendfile机制,返回发送的字节数。如果win下不支持sendfile, 或者不是普通文件,使用send()发送文件。offset告诉 起始位置。socket.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)socket.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)socket.setblocking(flag)如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值),非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常socket.settimeout(value)设置套接字操作的超时期,timeout是一个浮点数,单位是 秒。值为None表示没有超时期。一般,超时期应该在刚创 建套接字时设置,因为它们可能用于连接的操作(如 connect())socket.setsockopt(level,optname,value)设置套接字选项的值。比如缓冲区大小。太多了,去看文档。不同系统,不同版本都不尽相同socket.makefile(mode='r', buffering=None, *, encoding=None, errors=None, newline=None)创建一个与该套接字相关连的文件对象,将recv方法看做读方法,将send方法看做写方法。

二.TCP服务端编程

1>.服务器端编程步骤

  创建Socket对象 绑定IP地址Address和端口Port。bind()方法IPv4地址为一个二元组('IP地址字符串', Port) 开始监听,将在指定的IP的端口上监听。listen()方法获取用于传送数据的Socket对象socket.accept() -> (socket object, address info) accept方法阻塞等待客户端建立连接,返回一个新的Socket对象和客户端地址的二元组 地址是远程客户端的地址,IPv4中它是一个二元组(clientaddr, port)接收数据recv(bufsize[, flags]) 使用缓冲区接收数据 发送数据send(bytes)发送数据Server端开发socket对象 --> bind((IP, PORT)) --> listen --> accept --> close|--> recv or send --> close

img

2>.实战案例写一个群聊程序的服务端ChatServer

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import logging7 import socket8 import threading9 import datetime
10 
11 logging.basicConfig(level=logging.INFO, format="%(asctime)s %(thread)d %(message)s")
12 
13 
14 """
15     注意,这个代码为实验用,代码中瑕疵还有很多。Socket太底层了,实际开发中很少使用这么底层的接 口。
16 """
17 class ChatServer:
18     def __init__(self,ip="127.0.0.1",port=6666):
19         self.conn = socket.socket()         #创建socket对象
20         self.addr = (ip,port)
21         self.clients = {}                   #存放客户端连接容器
22         self.event = threading.Event()      #判断服务是否启动
23         self.lock = threading.Lock()        #为了线程安全而引入的
24 
25     def start(self):                        #启动服务,会监听IP地址和端口哟~
26         self.conn.bind(self.addr)           #将IP地址和端口绑定到套接字上
27         self.conn.listen()                  #监听绑定的套接字
28 
29         #accept会阻塞主线程,所以开一个新线程
30         threading.Thread(target=self.accept).start()
31 
32     def accept(self):                               #处理客户端的链接
33         while not self.event.is_set():
34             conn,client = self.conn.accept()        #该方法默认会进入阻塞状态
35             f = conn.makefile("rw")                 #将socket封装成一个文件对象来操作,支持读写
36             with self.lock:
37                 self.clients[client] = f,conn       #如果有新链接就添加到客户端字典
38 
39             #准备接收数据,recv是阻塞的,开启新的线程
40             threading.Thread(target=self.recv,args=(f,client)).start()
41 
42     def recv(self,f,client):                        #处理客户端数据
43         print("in recv")
44         while not self.event.is_set():
45             try:
46                 data = f.readline()
47                 print(data)
48             except Exception as e:
49                 logging.error(e)
50                 data = "quit"
51 
52             msg = data.strip()
53             print(msg, "++++++++++++++++++")
54 
55             #和客户端约定退出命令
56             if msg == "quit" or msg == "":           #主动端口得到空串
57                 with self.lock:
58                     _,conn = self.clients.pop(client)
59                     f.close()
60                     conn.close()
61                 logging.info("{} quit ...".format(client))
62                 break
63 
64             msg = "{:%Y/%m/%d %H:%M:%S} ip:port => {}:{}\n data => {}\n".format(datetime.datetime.now(),*client, data)
65             logging.info(msg)
66 
67 
68             with self.lock:
69                 for f1,_ in self.clients.values():
70                     f1.write(msg)
71                     f1.flush()
72 
73     def stop(self):                         #停止服务
74         self.event.set()
75         with self.lock:
76             for f,s in self.clients.values():
77                 f.close()
78                 s.close()
79         self.conn.close()
80 
81 
82 def main():
83     server = ChatServer()
84     server.start()
85 
86     while True:
87         cmd = input(">>> ").strip()
88         if cmd == "quit":
89             server.stop()
90             threading.Event.wait(3)                 #关闭服务需要等待时间
91             break
92         logging.info(threading.enumerate())         #用来观察断开后线程的变化
93         logging.info(server.clients)
94 
95 if __name__ == '__main__':
96     main()

三.TCP客户端编程

1>.客户端编程步骤

  创建Socket对象 连接到远端服务端的ip和port,connect()方法 传输数据  使用send、recv方法发送、接收数据 关闭连接,释放资源

2>.实战案例写一个群聊程序的客户端ChatClient

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import socket7 import threading8 import datetime9 import logging
10 
11 
12 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
13 logging.basicConfig(format=FORMAT, level=logging.INFO)
14 
15 """
16     同样,这样的客户端还是有些问题的,仅用于测试。
17 """
18 class ChatClient:
19     def __init__(self, ip='127.0.0.1', port=6666):
20         self.sock = socket.socket()
21         self.addr = (ip, port)
22         self.event = threading.Event()
23 
24     def start(self): # 启动对远端服务器的连接
25         self.sock.connect(self.addr)
26         self.send("I'm ready.")
27         # 准备接收数据,recv是阻塞的,开启新的线程
28         threading.Thread(target=self.recv, name="recv").start()
29 
30     def recv(self): # 接收服务端的数据
31         while not self.event.is_set():
32             try:
33                 data = self.sock.recv(1024) # 阻塞
34             except Exception as e:
35                 logging.error(e)
36                 break
37 
38             msg = "{:%Y/%m/%d %H:%M:%S} ip:port => {}:{}\n data => {}\n".format(datetime.datetime.now(),*self.addr, data.strip())
39             logging.info(msg)
40 
41     def send(self, msg:str):
42         data = "{}\n".format(msg.strip()).encode() # 服务端需要一个换行符
43         self.sock.send(data)
44 
45     def stop(self):
46         self.sock.close()
47         self.event.wait(3)
48         self.event.set()
49         logging.info('Client stops...')
50 
51 def main():
52     client = ChatClient()
53     client.start()
54     while True:
55         cmd = input('>>>')
56         if cmd.strip() == 'quit':
57             client.stop()
58             break
59         client.send(cmd) # 发送消息
60 
61 
62 if __name__ == '__main__':
63     main()

一.UDP编程概述

1>.UDP服务端编程流程

  创建socket对象。socket.SOCK_DGRAM 绑定IP和Port,bind()方法传输数据接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address)发送数据,socket.sendto(string, address) 发给某地址某信息 释放资源

img

2>.UDP客户端编程流程

  创建socket对象。socket.SOCK_DGRAM发送数据,socket.sendto(string, address)发给某地址某信息 接收数据,socket.recvfrom(bufsize[, flags]),获得一个二元组(string, address) 释放资源 

3>.UDP编程中常用的方法

  bind方法可以指定本地地址和端口laddr,会立即占用connect方法可以立即占用本地地址和端口laddr,填充远端地址和端口raddrsendto方法可以立即占用本地地址和端口laddr,并把数据发往指定远端。只有有了本地绑定端口,sendto就可以向任何远端发送数据send方法需要和connect方法配合,可以使用已经从本地端口把数据发往raddr指定的远端recv方法要求一定要在占用了本地端口后,返回接收的数据recvfrom方法要求一定要占用了本地端口后,返回接收的数据和对端地址的二元组

4>.心跳机制

  增加心跳heartbeat机制或ack机制。这些机制同样可以用在TCP通信的时候。 心跳,就是一端定时发往另一端的信息,一般每次数据越少越好。心跳时间间隔约定好就行。 ack即响应,一端收到另一端的消息后返回的确认信息。心跳机制实现策略:1.一般来说是客户端定时发往服务端的,服务端并不需要ack回复客户端,只需要记录该客户端还活 着就行了。当然服务端也可响应客户端2.如果是服务端定时发往客户端的,一般需要客户端ack响应来表示活着,如果没有收到某客户端的ack响应,服务端移除其信息。这种实现较为复杂,用的较少3.也可以双向都发心跳的,用的更少 

二.UDP版群聊案例

1>.UDP版群聊服务端代码

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import socket7 import threading8 import datetime9 import logging
10 
11 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
12 logging.basicConfig(format=FORMAT, level=logging.INFO)
13 
14 
15 #在服务器端代码中使用第一种心跳机制改进
16 class ChatUDPServer:
17     def __init__(self, ip='127.0.0.1', port=6688, interval=10):
18         self.addr = (ip, port)
19         self.sock = socket.socket(type=socket.SOCK_DGRAM)
20         self.clients = {} # 记录客户端,改为字典
21         self.event = threading.Event()
22         self.interval = interval # 默认10秒,超时就要移除对应的客户端
23 
24     def start(self):
25         self.sock.bind(self.addr) # 立即绑定
26         # 启动线程
27         threading.Thread(target=self.recv, name='recv').start()
28 
29     def recv(self):
30         removed = set()  # 超时的
31         while not self.event.is_set():
32             data, raddr = self.sock.recvfrom(1024)  # 阻塞接收数据
33             current = datetime.datetime.now().timestamp()  # float
34             if data.strip() == b'^hb^': # 心跳信息
35                 print('^^^^^^^^hb', raddr)
36                 self.clients[raddr] = current
37                 continue
38             elif data.strip() == b'quit':
39                 #有可能发来数据的不在clients中
40                 self.clients.pop(raddr, None)
41                 logging.info('{} leaving'.format(raddr))
42                 continue
43 
44             #有信息来就更新时间
45             #什么时候比较心跳时间呢? 发送信息的时候,反正要遍历一遍
46             self.clients[raddr] = current
47             msg = '{}. from {}:{}'.format(data.decode(), *raddr)
48             logging.info(msg)
49             msg = msg.encode()
50 
51             for c, stamp in self.clients.items():
52                 if current - stamp > self.interval:
53                     removed.add(c)
54                 else:
55                     self.sock.sendto(msg, c)  # 不保证对方能够收到
56 
57             for c in removed:
58                self.clients.pop(c)
59                removed.clear()
60 
61     def stop(self):
62         self.event.set()
63         self.clients.clear()
64         self.sock.close()
65 
66 def main():
67     server = ChatUDPServer()
68     server.start()
69 
70     while True:
71         cmd = input(">>> ")
72         if cmd.strip() == 'quit':
73             server.stop()
74             break
75         logging.info(threading.enumerate())
76         logging.info(server.clients)
77 
78 if __name__ == '__main__':
79     main()

2>.UDP版群聊客户端代码

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import threading7 import socket8 import logging9 
10 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
11 logging.basicConfig(format=FORMAT, level=logging.INFO)
12 
13 
14 
15 #增加定时发送心跳代码
16 class ChatUdpClient:
17     def __init__(self, rip='127.0.0.1', rport=6688):
18         self.sock = socket.socket(type=socket.SOCK_DGRAM)
19         self.raddr = (rip, rport)
20         self.event = threading.Event()
21 
22     def start(self):
23         self.sock.connect(self.raddr) # 占用本地地址和端口,设置远端地址和端口
24         threading.Thread(target=self._sendhb, name='heartbeat', daemon=True).start()
25         threading.Thread(target=self.recv, name='recv').start()
26 
27     def _sendhb(self): # 心跳
28         while not self.event.wait(5):
29             self.send('^hb^')
30 
31     def recv(self):
32         while not self.event.is_set():
33             data, raddr = self.sock.recvfrom(1024)
34             msg = '{}. from {}:{}'.format(data.decode(), *raddr)
35             logging.info(msg)
36 
37     def send(self, msg:str):
38         self.sock.send(msg.encode())
39         # self.sock.sendto(msg.encode(), self.raddr)
40 
41     def stop(self):
42         self.event.set()
43         self.send('quit')   #通知服务端退出
44         self.sock.close()
45 
46 
47 def main():
48     client1 = ChatUdpClient()
49     client2 = ChatUdpClient()
50     client1.start()
51     client2.start()
52     print(client1.sock)
53     print(client2.sock)
54 
55     while True:
56         cmd = input('Input your words >> ')
57         if cmd.strip() == 'quit':
58             client1.stop()
59             client2.stop()
60             break
61         client1.send(cmd)
62         client2.send(cmd)
63 
64 
65 if __name__ == '__main__':
66     main()

三.UDP协议应用

  UDP是无连接协议,它基于以下假设:网络足够好消息不会丢包包不会乱序但是,即使是在局域网,也不能保证不丢包,而且包的到达不一定有序。应用场景:视频、音频传输,一般来说,丢些包,问题不大,最多丢些图像、听不清话语,可以重新发话语来解 决。海量采集数据,例如传感器发来的数据,丢几十、几百条数据也没有关系。 DNS协议,数据内容小,一个包就能查询到结果,不存在乱序,丢包,重新请求解析。一般来说,UDP性能优于TCP,但是可靠性要求高的场合的还是要选择TCP协议。 DNS使用的就是UDP协议和TCP协议。

一.SocketServer概述

  socket编程过于底层,编程虽然有套路,但是想要写出健壮的代码还是比较困难的,所以很多语言都对 socket底层API进行封装,Python的封装就是socketserver模块。它是网络服务编程框架,便于企业级 快速开发。类的继承关系如下所示: +------------+| BaseServer |+------------+||v+-----------+        +--------------------+| UDPServer |------->| UnixDatagramServer |+-----------+        +--------------------+v+-----------++------------------+| TCPServer |------->| UnixStreamServer |+-----------+        +------------------+SocketServer简化了网络服务器的编写。 它有4个同步类:TCPServerUDPServer UnixStreamServer UnixDatagramServer。2个Mixin类:ForkingMixIn 和 ThreadingMixIn 类,用来支持异步。由此得到class ForkingUDPServer(ForkingMixIn, UDPServer): pass class ForkingTCPServer(ForkingMixIn, TCPServer): passclass ThreadingUDPServer(ThreadingMixIn, UDPServer): pass class ThreadingTCPServer(ThreadingMixIn, TCPServer): passfork是创建多进程,thread是创建多线程。 fork需要操作系统支持,Windows不支持。

二.编程接口

1>.创建服务器需要几个步骤

  从BaseRequestHandler类派生出子类,并覆盖其handle()方法来创建请求处理程序类,此方法将 处理传入请求实例化一个服务器类,传参服务器的地址和请求处理类调用服务器实例的handle_request()或serve_forever()方法调用server_close()关闭套接字

2>.案例展示

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 7 import threading8 import socketserver9 import logging
10 
11 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
12 logging.basicConfig(format=FORMAT, level=logging.INFO)
13 
14 """
15 BaseRequestHandler:
16     def __init__(self, request, client_address, server):
17         self.request = request
18         self.client_address = client_address
19         self.server = server
20         self.setup()
21         try:
22             self.handle()
23         finally:
24             self.finish()
25 
26 参数说明:
27     它是和用户连接的用户请求处理类的基类
28     服务端Server实例接收用户请求后,最后会实例化这个类。
29     它被初始化时,送入3个构造参数:request, client_address, server自身 
30     以后就可以在BaseRequestHandler类的实例上使用以下属性:
31         self.request是和客户端的连接的socket对象 
32         self.server是TCPServer实例本身 
33         self.client_address是客户端地址
34     这个类在初始化的时候,它会依次调用3个方法。子类可以覆盖这些方法。
35 """
36 class MyHandler(socketserver.BaseRequestHandler):
37     def handle(self):
38         # super().handle()                                        #可以不调用,父类handle什么都没有做
39         print('-'*30)
40         print(self.server)                                       #服务
41         print(self.request)                                      #服务端负责客户端连接请求的socket对象
42         print(self.client_address)                               #客户端地址
43         print(self.__dict__)
44         print(self.server.__dict__)                              #能看到负责accept的socket
45         print(threading.enumerate())
46         print(threading.current_thread())
47         print('-'*30)
48         for i in range(3):
49             data = self.request.recv(1024)
50             logging.info(data)
51         logging.info('====end====')
52 
53 addr = ('172.30.1.2', 9999)
54 
55 """
56     将ThreadingTCPServer换成TCPServer,同时连接2个客户端观察效果。 ThreadingTCPServer是异步的,可以同时处理多个连接。
57     TCPServer是同步的,一个连接处理完了,即一个连接的handle方法执行完了,才能处理另一个连接, 且只有主线程。
58 """
59 server = socketserver.ThreadingTCPServer(addr, MyHandler)       #注意参数是MyHandler类
60 server.serve_forever()                                          #永久循环执行

三.实现EchoServer(顾名思义,Echo,来什么消息回显什么消息 客户端发来什么信息,返回什么信息)

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 7 import threading8 import socketserver9 
10 class Handler(socketserver.BaseRequestHandler):
11     def setup(self):
12         super().setup()
13         self.event = threading.Event()
14         
15     def finish(self):
16         super().finish()
17         self.event.set()
18         
19     def handle(self):
20         super().handle()
21         print('-' * 30)
22         while not self.event.is_set():
23             data = self.request.recv(1024).decode()
24             print(data)
25             msg = '{} {}'.format(self.client_address, data).encode()
26             self.request.send(msg)
27 
28 server = socketserver.ThreadingTCPServer(('172.30.1.2', 9999), Handler)
29 print(server)
30 threading.Thread(target=server.serve_forever, name='EchoServer', daemon=True).start()
31 
32 while True:
33     cmd = input('>>')
34     if cmd == 'quit':
35         server.server_close()
36         break
37     print(threading.enumerate())

四.实战—改写ChatServer

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 7 import datetime8 import threading9 from socketserver import ThreadingTCPServer, StreamRequestHandler
10 import logging
11 
12 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
13 logging.basicConfig(format=FORMAT, level=logging.INFO)
14 
15 """
16     注意:此程序线程不安全
17 """
18 class ChatHandler(StreamRequestHandler):
19     clients = {}
20 
21     def setup(self):
22         super().setup()
23         self.event = threading.Event()
24         self.clients[self.client_address] = self.wfile
25 
26     def handle(self):
27         super().handle()
28         # for k,v in self.__dict__.items():
29         #     print(k, type(v), v)
30 
31         while not self.event.is_set():
32             data = self.rfile.read1(1024) # 可以读取到数据
33             data = data.decode().rstrip()
34             print(data, '~~~~~~~~~~~~~')
35 
36             if data == 'quit' or data == '': # 主动退出和断开
37                 break
38 
39             msg = '{} {}:{} {}'.format(datetime.datetime.now(), *self.client_address,data)
40 
41             for f in self.clients.values():
42                 f.write(msg.encode())
43                 f.flush()
44 
45     def finish(self):
46         self.clients.pop(self.client_address)
47         super().finish()
48         self.event.set()
49 
50 server = ThreadingTCPServer(('172.30.1.2', 9999), ChatHandler)
51 server.daemon_threads = True # 让所有启动线程都为daemon
52 
53 threading.Thread(target=server.serve_forever, name='chatserver', daemon=True).start()
54 
55 while True:
56     cmd = input('>>')
57     if cmd.strip() == 'quit':
58         server.server_close()
59         break
60     print(threading.enumerate())

五.总结

  为每一个连接提供RequestHandlerClass类实例,依次调用setup、handle、finish方法,且使用了try...finally结构保证finish方法一定能被调用。这些方法依次执行完成,如果想维持这个连接和客户端 通信,就需要在handle函数中使用循环。socketserver模块提供的不同的类,但是编程接口是一样的,即使是多进程、多线程的类也是一样,大 大减少了编程的难度。 将socket编程简化,只需要程序员关注数据处理本身,实现Handler类就行了。这种风

一.操作系统相关知识

1>.同步和异步

  函数或方法被调用的时候,调用者是否得到最终结果的。 直接得到最终结果结果的,就是同步调用。不直接得到最终结果的,就是异步调用。

2>.阻塞和非阻塞

  函数或方法调用的时候,是否立刻返回。立即返回就是非阻塞调用;不立即返回就是阻塞调用。

3>.同步,异步,阻塞,非阻塞之间的区别

  同步、异步,与阻塞、非阻塞不相关。 同步、异步强调的是,是否得到(最终的)结果; 阻塞、非阻塞强调是时间,是否等待。同步与异步区别在于:调用者是否得到了想要的最终结果。同步就是一直要执行到返回最终结果;异步就是直接返回了,但是返回的不是最终结果。调用者不能通过这种调用得到结果,以后可以通过被调用者提供的某种方式(被调用着通知调用者、调用者反复查询、回调),来取回最终结果。阻塞与非阻塞的区别在于,调用者是否还能干其他事。阻塞,调用者就只能干等;非阻塞,调用者可以先去忙会别的,不用一直等。

4>.同步,异步,阻塞,非阻塞之间的联系

  同步阻塞,我啥事不干,就等你打饭打给我。打到饭是结果,而且我啥事不干一直等,同步加阻塞。同步非阻塞,我等着你打饭给我,饭没好,我不等,但是我无事可做,反复看饭好了没有。打饭是结果,但是我不一直等。异步阻塞,我要打饭,你说等叫号,并没有返回饭给我,我啥事不干,就干等着饭好了你叫我。例如,取了号什么不干就等叫自己的号。异步非阻塞,我要打饭,你给我号,你说等叫号,并没有返回饭给我,我去看电视、玩手机,饭打好了叫我。

5>.x86 CPU的工作级别

在386之前,CPU工作在实模式下,之后,开始支持保护模式,对内存进行了划分。 我们知道计算机的运行就是运行指定的。指令还分特权指令级别和非特权指令级别。了解过计算机的朋友可能知道X86的CPU架构大概分成了四个层次,由内之外共有四个环,分别为Ring0、Ring1、Ring2、Ring3 Ring0级,可以执行特权指令,可以访问所有级别数据,可以访问IO设备等 Ring3级,级别最低,只能访问本级别数据 内核代码运行在Ring0,用户代码运行在Ring3Ring1和Ring2未使用,一般来讲,特权指令级别是指操作硬件,控制总线等等。

6>.用户态和内核态

  现代操作系统采用虚拟存储器,理论上,对于32位系统来说,进程对虚拟内存地址的内存寻址空间为 4G(232)。64位操作系统理论上最大内存寻址空间(264)。操作系统中,内核程序独立且运行在较高的特权级别上,它们驻留在被保护的内存空间上,拥有访问硬 件设备的所有权限,这部分内存称为内核空间(内核态,最高地址1G)。普通应用程序运行在用户空间(用户态)。应用程序想访问某些硬件资源就需要通过操作系统提供的系统调用,系统调用可以使用特权指令运行在 内核空间,此时进程陷入内核态运行。系统调用完成,进程将回到用户态执行用户空间代码。操作系统运行时为了呢能够实现协调多任务,操作系统被分割成了2段,其中接近于硬件一段具有特权权限的叫做内核空间,而进程运行在用户空间当中。所以说,应用程序需要使用特权指令或是要访问硬件资源时需要系统调用。只要是被开发成应用程序的,不是作为操作系统本身的一部分而存在的,我们称之为用户空间的程序。他们运行状态称之为用户态。需要在内核(我们可以认为是操作系统)空间运行的程序,我们称之他们运行在内核空间,他们运行的状态为用户态,也叫核心态。注意:内核不负责完成具体工作。在内核空间可用执行任何特权操作。每一个程序要想真正运行起来,它最终是向内核发起系统调用来完成的,或者有一部分的程序不需要内核的参与,有我们的应用程序就能完成。我们打个比方,你要计算2的32次方的结果,是否需要运行在内核态呢?答案是否定的,我们知道内核是不负责完成具体工作的,我们只是想要计算一个运算结果,也不需要调用任何的特权模式,因此,如果你写了一些关于计算数值的代码,只需要把这个代码交给CPU运行就可以了。如果一个应用程序需要调用内核的功能而不是用户程序的功能的话,应用程序会发现自己需要做一个特权操作,而应用程序自身没有这个能力,应用程序会向内核发申请,让内核帮忙完成特权操作。内核发现应用程序是有权限使用特权指令的,内核会运行这些特权指令并把执行结果返回给应用程序,然后这个应用程序拿到特权指令的执行结果后,继续后续的代码。这就是模式转换。因此一个程序员想要让你的程序具有生产力,就应该尽量让你的代码运行在用户空间,如果你的代码大多数都运行在内核空间的话,估计你的应用程序并不会给你打来太大的生产力哟。因为我们知道内核空间不负责产生生产力。博主推荐阅读:https://www.cnblogs.com/yinzhengjie/p/6957726.html

**二.**IO模型

1>.IO两个阶段

IO过程分两阶段: 1、数据准备阶段。从设备读取数据到内核空间的缓冲区(淘米,把米放饭锅里煮饭) 2、内核空间复制回用户空间进程缓冲区阶段(盛饭,从内核这个饭锅里面把饭装到碗里来)系统调用——read函数、recv函数等

2>.同步IO

  同步IO模型包括 阻塞IO、非阻塞IO、IO多路复用。阻塞IO如下图所示,进程等待(阻塞),直到读写完成。(全程等待)

img

  非阻塞IO如下图所示。进程调用recvfrom操作,如果IO设备没有准备好,立即返回ERROR,进程不阻塞。用户可以再次发起 系统调用(可以轮询),如果内核已经准备好,就阻塞,然后复制数据到用户空间。 第一阶段数据没有准备好,可以先忙别的,等会再来看看。检查数据是否准备好了的过程是非阻塞的。 第二阶段是阻塞的,即内核空间和用户空间之间复制数据是阻塞的。 淘米、蒸饭我不阻塞等,反复来询问,一直没有拿到饭。盛饭过程我等着你装好饭,但是要等到盛好饭 才算完事,这是同步的,结果就是盛好饭。

img

  IO多路复用也称Event-driven IO,工作原理如下图所示。所谓IO多路复用,就是同时监控多个IO,有一个准备好了,就不需要等了开始处理,提高了同时处理IO的能力。select几乎所有操作系统平台都支持,poll是对的select的升级。 epoll,Linux系统内核2.5+开始支持,对select和poll的增强,在监视的基础上,增加回调机制。BSD、 Mac平台有kqueue,Windows有iocp。以select为例,将关注的IO操作告诉select函数并调用,进程阻塞,内核“监视”select关注的文件描述符 fd,被关注的任何一个fd对应的IO准备好了数据,select返回。再使用read将数据复制到用户进程。select举例: 食堂供应很多菜(众多的IO),你需要吃某三菜一汤,大师傅(操作系统)说要现做,需要等,你只好 等待大师傅叫。其中一样菜好了,大师傅叫你,说你点的菜有好的了,你得自己遍历找找看哪一样才好了,请服务员把做好的菜打给你。 epoll是有菜准备好了,大师傅喊你去几号窗口直接打菜,不用自己找菜了。一般情况下,select最多能监听1024个fd(可以修改,但不建议改),但是由于select采用轮询的方 式,当管理的IO多了,每次都要遍历全部fd,效率低下。epoll没有管理的fd的上限,且是回调机制,不需遍历,效率很高。

img

3>.信号驱动IO

  进程在IO访问时,先通过sigaction系统调用,提交一个信号处理函数,立即返回。进程不阻塞。当内核准备好数据后,产生一个SIGIO信号并投递给信号处理函数。可以在此函数中调用recvfrom函数 操作数据从内核空间复制到用户空间,这段过程进程阻塞。工作原理如下图所示。

4>.异步IO

  同步IO,因为核心操作recv函数调用时,进程阻塞直到拿到最终结果为止。 而异步IO进程全程不阻塞。 进程发起异步IO请求,立即返回。内核完成IO的两个阶段,内核给进程发一个信号。 举例1:来打饭,跟大师傅说饭好了叫你,饭菜准备好了,窗口服务员把饭盛好了打电话叫你。两阶段都 是异步的。在整个过程中,进程都可以忙别的,等好了才过来。 举例2:今天不想出去到饭店吃饭了,点外卖,饭菜在饭店做好了(第一阶段),快递员从饭店送到你家 门口(第二阶段)。Linux的aio的系统调用,内核从版本2.6开始支持工作原理如下图所示。

img

**三.**Python中IO多路复用

1>.IO多路复用方案

  大多数操作系统都支持select和poll Linux 2.5+ 支持epoll BSD、Mac支持kqueue Solaris实现了/dev/poll Windows的IOCP

2>.开发中的选择

开发中的选择1、完全跨平台,使用select、poll。但是性能较差 2、针对不同操作系统自行选择支持的技术,这样做会提高IO处理的性能Python的select库实现了select、poll系统调用,这个基本上操作系统都支持。对Linux内核2.5+支持了epoll。select维护一个文件描述符数据结构,单个进程使用有上限,通常是1024,线性扫描这个数据结构。效率低。pool和select的区别是内部数据结构使用链表,没有这个最大限制,但是依然是线性遍历才知道哪个设备就绪了。epoll使用事件通知机制,使用回调机制提高效率。 select/poll还要从内核空间复制消息到用户空间,而epoll通过内核空间和用户空间共享一块内存来减少复制。

3>.selectors库

  3.4版本提供selectors库,高级IO复用库。类层次结构BaseSelector+-- SelectSelector      实现select +-- PollSelector      实现poll +-- EpollSelector      实现epoll +-- DevpollSelector    实现devpoll+-- KqueueSelector     实现kquueselectors.DefaultSelector返回当前平台最有效、性能最高的实现。 但是,由于没有实现Windows下的IOCP,所以,Windows下只能退化为select。在selects模块源码最下面有如下代码# Choose the best implementation, roughly:# epoll|kqueue|devpoll > poll > select.# select() also can't accept a FD > FD_SETSIZE (usually around 1024) if 'KqueueSelector' in globals():DefaultSelector = KqueueSelectorelif 'EpollSelector' in globals():DefaultSelector = EpollSelectorelif 'DevpollSelector' in globals():DefaultSelector = DevpollSelectorelif 'PollSelector' in globals():DefaultSelector = PollSelectorelse:DefaultSelector = SelectSelector事件注册class SelectSelector(_BaseSelectorImpl):"""Select-based selector."""def register(fileobj, events, data=None) -> SelectorKey: pass为selector注册一个文件对象,监视它的IO事件。返回SelectKey对象。fileobj 被监视文件对象,例如socket对象events 事件,该文件对象必须等待的事件data 可选的与此文件对象相关联的不透明数据,例如,关联用来存储每个客户端的会话ID,关联 方法。通过这个参数在关注的事件产生后让selector干什么事。EVENT_READ可读 0b01,内核已经准备好输入设备,可以开始读了EVENT_WRITE可写 0b10,内核准备好了,可以往里写了selectors.SelectorKey 有4个属性:1. fileobj 注册的文件对象2. fd 文件描述符3. events 等待上面的文件描述符的文件对象的事件 4. data 注册时关联的数据 

4>.IO多路复用TCP Server

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import selectors7 import threading8 import socket9 import logging
10 import time
11 
12 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
13 logging.basicConfig(format=FORMAT, level=logging.INFO)
14 
15 # 构建本系统最优Selector
16 selector = selectors.DefaultSelector()
17 
18 sock = socket.socket() # TCP Server
19 sock.bind(('127.0.0.1', 9999))
20 sock.listen()
21 logging.info(sock)
22 
23 sock.setblocking(False) # 非阻塞
24 
25 # 回调函数,sock的读事件
26 # 形参自定义
27 def accept(sock:socket.socket, mask):
28     """mask:事件的掩码"""
29     conn, raddr = sock.accept()
30     conn.setblocking(False) # 非阻塞
31     logging.info('new client socket {} in accept.'.format(conn))
32     key = selector.register(conn, selectors.EVENT_READ, read)
33     logging.info(key)
34 
35 # 回调函数
36 def read(conn:socket.socket, mask):
37     data = conn.recv(1024)
38     msg = "Your msg = {} ~~~~".format(data.decode())
39     logging.info(msg)
40     conn.send(msg.encode())
41 
42 # 注册sock的被关注事件,返回SelectorKey对象
43 # key记录了fileobj, fileobj的fd, events, data
44 key = selector.register(sock, selectors.EVENT_READ, accept)
45 logging.info(key)
46 
47 
48 # 开始循环
49 while True:
50     # 监听注册的对象的事件,发生被关注事件则返回events
51     events = selector.select()
52     print(events) # [(key, mask)]
53     # 表示那个关注的对象的某事件发生了
54     for key, mask in events:
55         # key.data => accept; key.fileobj => sock
56         callback = key.data
57         callback(key.fileobj, mask)

5>.IO多路复用群聊软件

 1 #!/usr/bin/env python2 #_*_conding:utf-8_*_3 #@author :yinzhengjie4 #blog:http://www.cnblogs.com/yinzhengjie5 6 import selectors7 import threading8 import socket9 import logging
10 import time
11 
12 FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
13 logging.basicConfig(format=FORMAT, level=logging.INFO)
14 
15 class ChatServer:
16     def __init__(self, ip='127.0.0.1', port=9999):
17         self.sock = socket.socket()
18         self.addr = ip, port
19         self.event = threading.Event()
20 
21         # 构建本系统最优Selector
22         self.selector = selectors.DefaultSelector()
23 
24     def start(self):
25         self.sock.bind(self.addr)
26         self.sock.listen()
27         self.sock.setblocking(False)
28         # 注册sock的被关注事件,返回SelectorKey对象
29         # key记录了fileobj, fileobj的fd, events, data
30         self.selector.register(self.sock, selectors.EVENT_READ, self.accept)
31 
32         # 事件监听循环
33         threading.Thread(target=self.select, name='selelct', daemon=True).start()
34 
35     def select(self):
36         # 开始循环
37         while not self.event.is_set():
38             # 监听注册的对象的事件,发生被关注事件则返回events
39             events = self.selector.select()
40             print(events)  # [(key, mask)]
41             # 表示那个关注的对象的某事件发生了
42             for key, mask in events:
43                 # key.data => accept; key.fileobj => sock
44                 callback = key.data
45                 callback(key.fileobj,mask)
46 
47     # 回调函数,sock的读事件
48     # 形参自定义
49     def accept(self, sock: socket.socket, mask):
50         """mask:事件的掩码"""
51         conn, raddr = sock.accept()
52         conn.setblocking(False)  # 非阻塞
53         logging.info('new client socket {} in accept.'.format(conn))
54         key = self.selector.register(conn, selectors.EVENT_READ, self.recv)
55         logging.info(key)
56 
57     # 回调函数
58     def recv(self, conn: socket.socket, mask):
59         data = conn.recv(1024)
60         data = data.strip()
61         if data == b'quit' or data == b'':
62             self.selector.unregister(conn)
63             conn.close()
64             return
65         msg = "Your msg = {} ~~~~".format(data.decode()).encode()
66         logging.info(msg)
67 
68         for key in self.selector.get_map().values():
69             print(self.recv)  # 当前绑定的
70             print(key.data)  # 注册时注入的绑定的对象
71             print(self.recv is key.data)  # 是否一致!!!
72             print(self.recv == key.data)  # 是否一致?
73             if key.data == self.recv:
74                 key.fileobj.send(msg)
75 
76     def stop(self):  # 关闭关注的文件对象,关闭selector
77         self.event.set()
78         fobjs = []
79         for fd, key in self.selector.get_map().items():
80             fobjs.append(key.fileobj)
81 
82         for fobj in fobjs:
83              self.selector.unregister(fobj)
84              fobj.close()
85 
86         self.selector.close()
87 
88 if __name__ == '__main__':
89     cs = ChatServer()
90     cs.start()
91     while True:
92         cmd = input('>>')
93         if cmd.strip() == 'quit':
94             logging.info('quit')
95             cs.stop()
96             break
97         print(threading.enumerate())

四.总结

使用IO多路复用 +(select、epoll) 并不一定比多线程 + 同步阻塞IO性能好,其最大优势减少了大量线程,可以处理更多的连接。多线程 + 同步阻塞IO模式开辟太多线程,线程开辟、销毁开销还是较大,倒是可以使用线程池;线程多,线程自己使用的内存也很可观;多线程切换时要保护现场和恢复现场,线程过多,切换会占用大量的时间。连接较少,多线程 + 同步阻塞IO模式比较适合,效率也不低。如果连接非常多,对服务端程序来说,IO并发还是比较高的,这时候,开辟太多线程其实也不是很划算,这时候IO多路复用或许是更好的选择。
= "Your msg = {} ~~~~".format(data.decode()).encode()
66         logging.info(msg)
67 
68         for key in self.selector.get_map().values():
69             print(self.recv)  # 当前绑定的
70             print(key.data)  # 注册时注入的绑定的对象
71             print(self.recv is key.data)  # 是否一致!!!
72             print(self.recv == key.data)  # 是否一致?
73             if key.data == self.recv:
74                 key.fileobj.send(msg)
75 
76     def stop(self):  # 关闭关注的文件对象,关闭selector
77         self.event.set()
78         fobjs = []
79         for fd, key in self.selector.get_map().items():
80             fobjs.append(key.fileobj)
81 
82         for fobj in fobjs:
83              self.selector.unregister(fobj)
84              fobj.close()
85 
86         self.selector.close()
87 
88 if __name__ == '__main__':
89     cs = ChatServer()
90     cs.start()
91     while True:
92         cmd = input('>>')
93         if cmd.strip() == 'quit':
94             logging.info('quit')
95             cs.stop()
96             break
97         print(threading.enumerate())

四.总结

使用IO多路复用 +(select、epoll) 并不一定比多线程 + 同步阻塞IO性能好,其最大优势减少了大量线程,可以处理更多的连接。多线程 + 同步阻塞IO模式开辟太多线程,线程开辟、销毁开销还是较大,倒是可以使用线程池;线程多,线程自己使用的内存也很可观;多线程切换时要保护现场和恢复现场,线程过多,切换会占用大量的时间。连接较少,多线程 + 同步阻塞IO模式比较适合,效率也不低。如果连接非常多,对服务端程序来说,IO并发还是比较高的,这时候,开辟太多线程其实也不是很划算,这时候IO多路复用或许是更好的选择。
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Stratifyd整理:Gartner关于客户体验指标管理报告

    本文内容节选自Gartner报告 “How to Manage Customer Experience Metrics”,总结了不同类型的客户体验度量标准,并为支持客户体验计划的IT领导提供有益建议。 点链接获取Gartner原版报告全文:http://stratifyd.mikecrm.com/4pc9dXl Gartner…...

    2024/5/10 4:15:51
  2. [转](33)调试驱动程序

    一、驱动和应用程序在调试上的区别 接上一节课,我们学习了如何运行一个驱动,今天,学习如何调试驱动。 我们以前编写应用程序,可以直接在IDE里调试,VS,OD这些都是三环调试器。驱动运行在内核层,需…...

    2024/4/17 4:38:59
  3. 01-Go简介

    文章目录一 Go语言介绍二 Go安装三 HelloWorld四 Go语法注意五 开发工具推荐一 Go语言介绍 Go语言是Google公司开发的一种静态、编译型语言,具备自动垃圾回收功能,原生支持并发开发。 Go的诞生是为了解决当下编程语言对并发支持不友好、编译速度慢、编…...

    2024/5/1 14:12:57
  4. HBTC 霍比特巨建华:平台通证模型已进入2.0时代

    7月5日上午9点,由杭州市余杭区政府指导,杭州未来科技城管委会、巴比特主办的「2020 杭州区块链国际周」正式开幕,来自世界各地的行业大咖、互联网大厂、创新企业、投资机构、学术机构、主流媒体将齐聚亮相,共同探讨区块链产业面临…...

    2024/4/16 1:34:23
  5. 2020知识点 从smart到intelligent智能智造+NEPCON China

    旨在引领国内电子制造产业前沿的中国国际电子生产设备暨微电子工业展(NEPCON China 2019),将于4月24日在上海世博展览馆拉开帷幕。作为智能制造行业瞩目、“电子人”期待的年度国际化盛会,NEPCON China 2019先进技术与工艺齐飞&am…...

    2024/5/10 6:26:57
  6. 人工智能-科普-人工智能是什么?

    人工智能在商业中的应用是通过机器学习算法来实现的。机器学习是计算机科学的一部分,专注于计算机系统学习执行特定任务,而不使用明确的指令,而是依靠模式和推理。 机器学习算法通过处理数据,而不是通过接收明确的编程指令&#…...

    2024/5/10 5:45:21
  7. tomcat启动报错java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang

    G:\apache-tomcat-9.0.38\bin\catalina.bat run [2020-11-17 11:45:11,753] Artifact BMS:war exploded: 正在等待服务器连接以启动工件部署… Using CATALINA_BASE: "C:\Users\高朋\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\未命名_BMS" Using CATALIN…...

    2024/5/10 3:58:17
  8. Docker-compose容器编排,consul,consul-template

    目录一. docker-compose1.1 docker compose 容器编排概述1.2 Docker Compose 环境安装1.3 Docker Compose文件结构1.4 Docker Compose配置常用字段1.5 Docker Compose常用命令1.6 使用Docker Compose编排nginx容器1.61 安装docker-ce1.62 下载compose工具1.63 创建dockerfile的…...

    2024/5/1 12:25:02
  9. 遇到bug不要慌,都是小场面。

    1. 澄清 今天早上准备跑步,看到外面雾蒙蒙的,好像是下雨了。 也只是好像下雨了,不一定真下呢,毕竟眼见不一定为实,一定要亲身实践,小马过河才可以。 所谓不淋雨不知道雨是真实的,不挨揍不知道…...

    2024/5/4 0:43:15
  10. 可穿戴市场的回暖,更像是巨头们自导自演的游戏

    苹果春季发布会AirPods 2将是全场最大的看点,尤其将加入健康数据追踪功能的消息。 按照一些果粉的观点,AirPods 可谓是苹果继iPhone 4之后最强大的创新。隔三差五在科技媒体上发声的郭明錤,声称AirPods 的销量有望从2017 年的 1600 万增长到…...

    2024/5/10 4:17:16
  11. Javaweb技术之git操作

    目录 1.版本控制 2.版本控制工具 2.1.Visual Source Safe(简称VSS) 2.2.Concurrent Version System(简称CVS) 2.3.SVN 2.4.Git 3.Git的使用 3.1安装 3.2 git的工作区域 3.3 初始化仓库 4. Git远程服务器介绍 4.1 GitHub介绍 4.2.GitLab 4.3…...

    2024/4/22 16:12:45
  12. mac下升级node版本

    第一步:使用npm安装n模块 n模块是专门用来管理nodejs版本的,名字就叫n执行命令 npm install -g n第二步:使用n模块升级node 第一种是升级到最新版本 sudo n latest第二种是升级到稳定版本 sudo n stable第三种是升级到指定版本 sudo n …...

    2024/4/26 10:35:17
  13. Oracle merge into

    MERGE INTO table_name A USING (table|view|query_sql) B ON (A.col B.col) WHEN MATCHED THEN UPDATE SET A.col1 B.col_val1 WHEN NOT MATCHED THEN INSERT (A.column_list) VALUES (column_values)/*判断A表和B表是否满足 ON中的条件,如果满足则执行更新操…...

    2024/4/30 12:43:09
  14. 谈谈html几个重要标签用法

    转自:http://www.pinlue.com/article/2019/03/2420/428379740128.html...

    2024/4/24 13:20:47
  15. 机器人系统实验(8)2020.11.17

    一、实验内容 1.补齐前面课程拆掉的墙壁 2.根据房间形态修改机器人控制节点, 3.要求移动人至少覆盖一个房间 4.在RVIZ中查看机器人覆盖轨迹 二、相关知识 1.路径规划概述 (1)移动机器人的一个重要组成部分,它的任务就是在具有障碍物的环境内按照一定的评价标准如…...

    2024/5/3 4:35:56
  16. 手机端局部滚动问题 overflow-y:auto

    手机端局部滚动问题 overflow-y:auto参考文章: (1)手机端局部滚动问题 overflow-y:auto (2)scroll无效,使用iscroll解决 备忘一下。...

    2024/4/22 6:57:09
  17. 企业上云如何享受普惠安全红利?丨产业安全观智库访谈

    当今现代社会每一个人都享受到社会的公共资源的各种普惠红利,比如说水、电、煤,以及IT信息基础设施。伴随着数字化进程不断提速,安全已然成为产业发展的底座,普惠安全正在成为供需双方都要思考的命题。 为了进一步厘清普惠安全的…...

    2024/4/25 11:39:40
  18. 基于Servlet的增删改查案例(文后有源码)

    效果图 功能点 简单功能列表查询登录添加 删除修改复杂功能删除选中分页查询 减轻服务器内存的开销 提升用户体验复杂条件查询 项目结构 主要逻辑代码页面 UserDaoImpl package com.yang.dao.Impl;import com.yang.dao.UserDao; import com.yang.domain.User; im…...

    2024/5/7 2:20:10
  19. 想知道HBC到底怎么回事儿?来看这个视频吧!

    牛市财经牛人牛问——HBC 牛人牛问之HBCHBC概述 HBC是由霍比特HBTC交易平台为了支持公链上线和品牌升级,对前BHEX交易平台通证BHT的升级版通证,HBC继承了原BHT通证的权益,BHT将逐步替换为HBC,HBC通证代表了霍比特交易平台HBTC 、…...

    2024/5/6 22:00:08
  20. 将app从azure devops推送至非国际版azure比如azure中国

    Publish app from Azure DevOps to non-global Azure like Azure CN app注册 点击azure active directory() 点击App registrations->new registration 输入一个app名称,点击注册 点击刚注册的app,点击certificates & secrets 点击new clien…...

    2024/4/20 13:04:55

最新文章

  1. 每日OJ题_记忆化搜索⑤_力扣329. 矩阵中的最长递增路径

    目录 力扣329. 矩阵中的最长递增路径 解析代码1_爆搜递归(超时) 解析代码2_记忆化搜索 力扣329. 矩阵中的最长递增路径 329. 矩阵中的最长递增路径 难度 困难 给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。 对于每…...

    2024/5/10 12:17:56
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/9 21:23:04
  3. 深入浅出 -- 系统架构之微服务中Nacos的部署

    前面我们提到过,在微服务架构中,Nacos注册中心属于核心组件,通常我们会采用高性能独立服务器进行部署,下面我们一起来看看Nacos部署过程: 1、环境准备 因为Nacos是支持windows和Linux系统的,且服务器操作…...

    2024/5/9 19:07:28
  4. 安装Docker(CentOS)

    Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道。 官方网站上…...

    2024/5/9 10:03:38
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/10 1:36:26
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/9 7:40:42
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...

    2024/5/9 2:44:26
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...

    2024/5/10 2:07:45
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...

    2024/5/10 8:07:24
  10. 【Objective-C】Objective-C汇总

    方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/9 5:40:03
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    👨‍💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...

    2024/5/10 8:16:30
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/10 2:07:43
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...

    2024/5/10 2:07:43
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/9 4:12:16
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/9 7:40:35
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/9 19:47:07
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/10 10:17:11
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...

    2024/5/10 2:07:41
  19. --max-old-space-size=8192报错

    vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...

    2024/5/9 5:02:59
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...

    2024/5/9 4:31:45
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...

    2024/5/9 16:54:42
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...

    2024/5/10 1:31:37
  23. python django 小程序图书借阅源码

    开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图&#xff0…...

    2024/5/10 9:24:29
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/10 10:40:03
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57