python select epoll poll的解析

来源:http://www.cnblogs.com/qianyuliang/archive/2017/03/15/6551553.html
-Advertisement-
Play Games

select、poll、epoll三者的區別 select select最早於1983年出現在4.2BSD中,它通過一個select()系統調用來監視多個文件描述符的數組(在linux中一切事物皆文件,塊設備,socket連接等。),當select()返回後,該數組中就緒的文件描述符便會被內核修改標 ...


select、poll、epoll三者的區別 

select 

select最早於1983年出現在4.2BSD中,它通過一個select()系統調用來監視多個文件描述符的數組(在linux中一切事物皆文件,塊設備,socket連接等。),當select()返回後,該數組中就緒的文件描述符便會被內核修改標誌位(變成ready),使得進程可以獲得這些文件描述符從而進行後續的讀寫操作(select會不斷監視網路介面的某個目錄下有多少文件描述符變成ready狀態【在網路介面中,過來一個連接就會建立一個'文件'】,變成ready狀態後,select就可以操作這個文件描述符了)。

【socketserver是通過多線程來處理多個請求,每個連接過來分配一個線程來處理,但是select是單進程的,一個進程執行代碼肯定就是串列的,但是現在就要通過一個進程來實現併發的效果,一個進程下只有一個主線程,也就說說用一個線程實現併發的效果。為什麼要用一個進程實現多併發而不採用多線程實現多併發呢?

==========答:因為一個進程實現多併發比多線程是實現多併發的效率還要高,因為啟動多線程會有很多的開銷,而且CPU要不斷的檢查每個線程的狀態,確定哪個線程是否可以執行。這個對系統來說也是有壓力的,用單進程的話就可以避免這種開銷和給系統帶來的壓力,

那麼單進程是如何實現多併發的呢???

========答:很巧妙的使用了生產者和消費者的模式(非同步),生產者和消費者可以實現非阻塞,一個socketserver通過select接收多個連接過來(之前的socket一個進程只能接收一個連接,當接收新的連接的時候產生阻塞,因為這個socket進程要先和客戶端進行通信,二者是彼此互相等待的【客戶端發一條消息,服務端收到,客戶端等著返回....服務端等著接收.........】一直在阻塞著,這個時候如果再來一個連接,要等之前的那個連接斷了,這個才可以連進來。-----------也就是說用基本的socket實現多進程是阻塞的。為瞭解決這個問題採用每來一個連接產生一個線程,是不阻塞了,但是當線程數量過多的時候,對於cpu來說開銷和壓力是比較大的。)對於單個socket來說,阻塞的時候大部分的時候都是在等待IO操作(網路操作也屬於IO操作)。為了避免這種情況,就出現了非同步=============客戶端發起一個連接,會在服務端註冊一個文件句柄,服務端會不斷輪詢這些文件句柄的列表,主進程和客戶端建立連接而沒有啟動線程,這個時候主進程和客戶端進行交互,其他的客戶端是無法連接主進程的,為了實現主進程既能和已連接的客戶端收發消息,又能和新的客戶端建立連接,就把輪詢變的非常快(死迴圈)去刷客戶端連接進來的文件句柄的列表,只要客戶端發消息了,服務端讀取了消息之後,有另一個列表去接收給客戶端返回的消息,也不斷的去刷這個列表,刷出來後返回給客戶端,這樣和客戶端的這次通信就完成了,但是跟客戶端的連接還沒有斷,但是就進入了下一次的輪詢。】

  

select 優點

select目前幾乎在所有的平臺上支持,良好跨平臺性。

 

select 缺點

  • 每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多的時候會很大
  • 單個進程能夠監視的fd數量存在最大限制,在linux上預設為1024(可以通過修改巨集定義或者重新編譯內核的方式提升這個限制)
  • 並且由於select的fd是放在數組中,並且每次都要線性遍歷整個數組,當fd很多的時候,開銷也很大

python  select 

調用select的函數為readable,writable,exceptional = select.select(rlist, wlist, xlist[, timeout]),前三個參數都分別是三個列表,數組中的對象均為waitable object:均是整數的文件描述符(file descriptor)或者一個擁有返迴文件描述符方法fileno()的對象;

  • rlist: 等待讀就緒的list
  • wlist: 等待寫就緒的list
  • errlist: 等待“異常”的list

 

select方法用來監視文件描述符,如果文件描述符發生變化,則獲取該描述符。 1、這三個list可以是一個空的list,但是接收3個空的list是依賴於系統的(在Linux上是可以接受的,但是在window上是不可以的)。 2、當 rlist 序列中的描述符發生可讀時(accetp和read),則獲取發生變化的描述符並添加到 readable 序列中 3、當 wlist 序列中含有描述符時,則將該序列中所有的描述符添加到 writable 序列中 4、當 errlist序列中的句柄發生錯誤時,則將該發生錯誤的句柄添加到 exceptional 序列中 5、當 超時時間 未設置,則select會一直阻塞,直到監聽的描述符發生變化    當 超時時間 = 時,那麼如果監聽的句柄均無任何變化,則select會阻塞 1 秒,之後返回三個空列表,如果監聽的描述符(fd)有變化,則直接執行。 6、在list中可以接受Ptython的的file對象(比如sys.stdin,或者會被open()os.open()返回的object),socket object將會返回socket.socket()。也可以自定義類,只要有一個合適的fileno()的方法(需要真實返回一個文件描述符,而不是一個隨機的整數)。  

select 示例:

Python的select()方法直接調用操作系統的IO介面,它監控sockets,open files, and pipes(所有帶fileno()方法的文件句柄)何時變成readable 和writeable, 或者通信錯誤,select()使得同時監控多個連接變的簡單,並且這比寫一個長迴圈來等待和監控多客戶端連接要高效,因為select直接通過操作系統提供的C的網路介面進行操作,而不是通過Python的解釋器

 1 #coding:UTF8
 2 
 3 import select
 4 import socket
 5 import sys
 6 import Queue
 7 
 8 #創建一個TCP/IP 進程
 9 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
10 server.setblocking(0)
11 
12 #連接地址和埠
13 server_address = ('localhost',10000)
14 print >>sys.stderr,'starting up on %s prot %s' % server_address
15 server.bind(server_address)
16 
17 #最大允許鏈接數
18 server.listen(5)
19 
20 inputs = [ server ]
21 outputs = []
22 
23 message_queues = {}
24 
25 while inputs:
26     print >>sys.stderr,'\nwaiting for the next event'
27     readable,writable,exceptional = select.select(inputs,outputs,inputs)
28 
29     # Handle inputs
30     for s in readable:
31      
32         if s is server:
33             # A "readable" server socket is ready to accept a connection
34             connection, client_address = s.accept()
35             print >>sys.stderr, 'new connection from', client_address
36             #connection.setblocking(0)
37             inputs.append(connection)
38      
39             # Give the connection a queue for data we want to send
40             message_queues[connection] = Queue.Queue()
41         
42         else:
43             data = s.recv(1024)
44             if data:
45                 # A readable client socket has data
46                 print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
47                 message_queues[s].put(data)  #這個s相當於connection
48                 # Add output channel for response
49                 if s not in outputs:
50                     outputs.append(s)
51 
52             else:
53                 # Interpret empty result as closed connection
54                 print >>sys.stderr, 'closing', client_address, 'after reading no data'
55                 # Stop listening for input on the connection
56                 if s in outputs:
57                     outputs.remove(s)  #既然客戶端都斷開了,我就不用再給它返回數據了,所以這時候如果這個客戶端的連接對象還在outputs列表中,就把它刪掉
58                 inputs.remove(s)    #inputs中也刪除掉
59                 s.close()           #把這個連接關閉掉
60                  
61                 # Remove message queue
62                 del message_queues[s]
63     
64     # Handle outputs
65     for s in writable:
66         try:
67             next_msg = message_queues[s].get_nowait()
68         except Queue.Empty:
69             # No messages waiting so stop checking for writability.
70             print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
71             outputs.remove(s)
72         else:
73             print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
74             s.send(next_msg.upper())
75     # Handle "exceptional conditions"
76     for s in exceptional:
77         print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
78         # Stop listening for input on the connection
79         inputs.remove(s)
80         if s in outputs:
81             outputs.remove(s)
82         s.close()
83      
84         # Remove message queue
85         del message_queues[s]     
server

 

代碼解析:

 

select()方法接收並監控3個通信列表, 第一個是所有的輸入的data,就是指外部發過來的數據,第2個是監控和接收所有要發出去的data(outgoing data),第3個監控錯誤信息,接下來我們需要創建2個列表來包含輸入和輸出信息來傳給select().

# Sockets from which we expect to read
inputs = [ server ]

# Sockets to which we expect to write
outputs = [ ]

 

所有客戶端的進來的連接和數據將會被server的主迴圈程式放在上面的list中處理,我們現在的server端需要等待連接可寫(writable)之後才能過來,然後接收數據並返回(因此不是在接收到數據之後就立刻返回),因為每個連接要把輸入或輸出的數據先緩存到queue里,然後再由select取出來再發出去。

# Outgoing message queues (socket:Queue)
message_queues = {}

The main portion of the server program loops, calling select() to block and wait for network activity.

下麵是此程式的主迴圈,調用select()時會阻塞和等待直到新的連接和數據進來

while inputs:

    # Wait for at least one of the sockets to be ready for processing
    print >>sys.stderr, '\nwaiting for the next event'
    readable, writable, exceptional = select.select(inputs, outputs, inputs)

 當你把inputs,outputs,exceptional(這裡跟inputs共用)傳給select()後,它返回3個新的list,我們上面將他們分別賦值為readable,writable,exceptional, 所有在readable list中的socket連接代表有數據可接收(recv),所有在writable list中的存放著你可以對其進行發送(send)操作的socket連接,當連接通信出現error時會把error寫到exceptional列表中。

 

Readable list 中的socket 可以有3種可能狀態,第一種是如果這個socket是main "server" socket,它負責監聽客戶端的連接,如果這個main server socket出現在readable里,那代表這是server端已經ready來接收一個新的連接進來了,為了讓這個main server能同時處理多個連接,在下麵的代碼里,我們把這個main server的socket設置為非阻塞模式。

 

第二種情況是這個socket是已經建立了的連接,它把數據發了過來,這個時候你就可以通過recv()來接收它發過來的數據,然後把接收到的數據放到queue里,這樣你就可以把接收到的數據再傳回給客戶端了。

 

第三種情況就是這個客戶端已經斷開了,所以你再通過recv()接收到的數據就為空了,所以這個時候你就可以把這個跟客戶端的連接關閉了。

 

對於writable list中的socket,也有幾種狀態,如果這個客戶端連接在跟它對應的queue里有數據,就把這個數據取出來再發回給這個客戶端,否則就把這個連接從output list中移除,這樣下一次迴圈select()調用時檢測到outputs list中沒有這個連接,那就會認為這個連接還處於非活動狀態

 

最後,如果在跟某個socket連接通信過程中出了錯誤,就把這個連接對象在inputs\outputs\message_queue中都刪除,再把連接關閉掉

 

 1 #coding:UTF8
 2 
 3 import socket
 4 import sys
 5  
 6 messages = [ 'This is the message. ',
 7              'It will be sent ',
 8              'in parts.',
 9              ]
10 server_address = ('localhost', 10003)
11  
12 # Create a TCP/IP socket
13 socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
14           socket.socket(socket.AF_INET, socket.SOCK_STREAM),
15           ]
16  
17 # Connect the socket to the port where the server is listening
18 print >>sys.stderr, 'connecting to %s port %s' % server_address
19 for s in socks:
20     s.connect(server_address)
21 
22 for message in messages:
23  
24     # Send messages on both sockets
25     for s in socks:
26         print >>sys.stderr, '%s: sending "%s"' % (s.getsockname(), message)
27         s.send(message)
28  
29     # Read responses on both sockets
30     for s in socks:
31         data = s.recv(1024)
32         print >>sys.stderr, '%s: received "%s"' % (s.getsockname(), data)
33         if not data:
34             print >>sys.stderr, 'closing socket', s.getsockname()
client

客戶端程式展示瞭如何通過select()對socket進行管理並與多個連接同時進行交互,通過迴圈通過每個socket連接給server發送和接收數據。

server:
starting up on localhost prot 10000

waiting for the next event
new connection from ('127.0.0.1', 54812)

waiting for the next event
new connection from ('127.0.0.1', 54813)
received "This is the message. " from ('127.0.0.1', 54812)

waiting for the next event
received "This is the message. " from ('127.0.0.1', 54813)
sending "This is the message. " to ('127.0.0.1', 54812)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
sending "This is the message. " to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
received "It will be sent " from ('127.0.0.1', 54812)
received "It will be sent " from ('127.0.0.1', 54813)

waiting for the next event
sending "It will be sent " to ('127.0.0.1', 54812)
sending "It will be sent " to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
received "in parts." from ('127.0.0.1', 54812)
received "in parts." from ('127.0.0.1', 54813)

waiting for the next event
sending "in parts." to ('127.0.0.1', 54812)
sending "in parts." to ('127.0.0.1', 54813)

waiting for the next event
output queue for ('127.0.0.1', 54812) is empty
output queue for ('127.0.0.1', 54813) is empty

waiting for the next event
closing ('127.0.0.1', 54813) after reading no data
closing ('127.0.0.1', 54813) after reading no data

waiting for the next event





client:
connecting to localhost port 10000
('127.0.0.1', 54812): sending "This is the message. "
('127.0.0.1', 54813): sending "This is the message. "
('127.0.0.1', 54812): received "THIS IS THE MESSAGE. "
('127.0.0.1', 54813): received "THIS IS THE MESSAGE. "
('127.0.0.1', 54812): sending "It will be sent "
('127.0.0.1', 54813): sending "It will be sent "
('127.0.0.1', 54812): received "IT WILL BE SENT "
('127.0.0.1', 54813): received "IT WILL BE SENT "
('127.0.0.1', 54812): sending "in parts."
('127.0.0.1', 54813): sending "in parts."
('127.0.0.1', 54812): received "IN PARTS."
('127.0.0.1', 54813): received "IN PARTS."
運行結果

 

 

原文:http://pymotw.com/2/select/ 

 

 

poll 
poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差別,但是poll沒有最大文件描述符數量的限制。

poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨著文件描述符數量的增加而線性增大。

另外,select()和poll()將就緒的文件描述符告訴進程後,如果進程沒有對其進行IO操作,那麼下次調用select()和poll() 的時候將再次報告這些文件描述符,所以它們一般不會丟失就緒的消息,這種方式稱為水平觸發(Level Triggered)。

 

在Python中調用poll

  • select.poll(),返回一個poll的對象,支持註冊和註銷文件描述符。

  • poll.register(fd[, eventmask])註冊一個文件描述符,註冊後,可以通過poll()方法來檢查是否有對應的I/O事件發生。fd可以是i 個整數,或者有返回整數的fileno()方法對象。如果File對象實現了fileno(),也可以當作參數使用。

  • eventmask是一個你想去檢查的事件類型,它可以是常量POLLINPOLLPRI和 POLLOUT的組合。如果預設,預設會去檢查所有的3種事件類型。

事件常量意義
POLLIN 有數據讀取
POLLPRT 有數據緊急讀取
POLLOUT 準備輸出:輸出不會阻塞
POLLERR 某些錯誤情況出現
POLLHUP 掛起
POLLNVAL 無效請求:描述無法打開
  • poll.modify(fd, eventmask) 修改一個已經存在的fd,和poll.register(fd, eventmask)有相同的作用。如果去嘗試修改一個未經註冊的fd,會引起一個errnoENOENTIOError
  • poll.unregister(fd)從poll對象中註銷一個fd。嘗試去註銷一個未經註冊的fd,會引起KeyError
  • poll.poll([timeout])去檢測已經註冊了的文件描述符。會返回一個可能為空的list,list中包含著(fd, event)這樣的二元組。 fd是文件描述符, event是文件描述符對應的事件。如果返回的是一個空的list,則說明超時了且沒有文件描述符有事件發生。timeout的單位是milliseconds,如果設置了timeout,系統將會等待對應的時間。如果timeout預設或者是None,這個方法將會阻塞直到對應的poll對象有一個事件發生。
#coding: utf-8 

import select, socket

response = b"hello world"

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('localhost', 10000))
serversocket.listen(1)
serversocket.setblocking(0)

#
poll = select.poll()
poll.register(serversocket.fileno(), select.POLLIN)

connections = {}
while True:
    for fd, event in poll.poll():
        if event == select.POLLIN:
            if fd == serversocket.fileno():
                con, addr = serversocket.accept()
                poll.register(con.fileno(), select.POLLIN)
                connections[con.fileno()] = con
            else:
                con = connections[fd]
                data = con.recv(1024)
                if data:
                    poll.modify(con.fileno(), select.POLLOUT)
        elif event == select.POLLOUT:
            con = connections[fd]
            con.send(response)
            poll.unregister(con.fileno())
            con.close()
View Code

 

epoll 
直到Linux2.6才出現了由內核直接支持的實現方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下性能最好的多路I/O就緒通知方法。

epoll可以同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有採取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發),理論上邊緣觸發的性能要更高一些,但是代碼實現相當複雜。

epoll同樣只告知那些就緒的文件描述符,而且當我們調用epoll_wait()獲得就緒文件描述符時,返回的不是實際的描述符,而是一個代表 就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的文件描述符即可,這裡也使用了記憶體映射(mmap)技術,這樣便徹底省掉了 這些文件描述符在系統調用時複製的開銷。

另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法後,內核才對所有監視的文件描 述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用類似callback的回調 機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便得到通知。

 

在Python中調用epoll

  • select.epoll([sizehint=-1])返回一個epoll對象。

  • eventmask

事件常量意義
EPOLLIN 讀就緒
EPOLLOUT 寫就緒
EPOLLPRI 有數據緊急讀取
EPOLLERR assoc. fd有錯誤情況發生
EPOLLHUP assoc. fd發生掛起
EPOLLRT 設置邊緣觸發(ET)(預設的是水平觸發)
EPOLLONESHOT 設置為 one-short 行為,一個事件(event)被拉出後,對應的fd在內部被禁用
EPOLLRDNORM 和 EPOLLIN 相等
EPOLLRDBAND 優先讀取的數據帶(data band)
EPOLLWRNORM 和 EPOLLOUT 相等
EPOLLWRBAND 優先寫的數據帶(data band)
EPOLLMSG 忽視
  • epoll.close()關閉epoll對象的文件描述符。
  • epoll.fileno返回control fd的文件描述符number。
  • epoll.fromfd(fd)用給予的fd來創建一個epoll對象。
  • epoll.register(fd[, eventmask])在epoll對象中註冊一個文件描述符。(如果文件描述符已經存在,將會引起一個IOError
  • epoll.modify(fd, eventmask)修改一個已經註冊的文件描述符。
  • epoll.unregister(fd)註銷一個文件描述符。
  • epoll.poll(timeout=-1[, maxevnets=-1])等待事件,timeout(float)的單位是秒(second)。
 1 #coding:Utf8
 2 import socket, select
 3 
 4 EOL1 = b'\n\n'
 5 EOL2 = b'\n\r\n'
 6 response  = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
 7 response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
 8 response += b'Hello, world!'
 9 
10 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
11 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
12 serversocket.bind(('localhost', 10000))
13 serversocket.listen(1)
14 serversocket.setblocking(0)
15 
16 epoll = select.epoll()
17 epoll.register(serversocket.fileno(), select.EPOLLIN)
18 
19 try:
20    connections = {}; requests = {}; responses = {}
21    while True:
22       events = epoll.poll(1)
23       for fileno, event in events:
24          if fileno == serversocket.fileno():
25             connection, address = serversocket.accept()
26             connection.setblocking(0)
27             epoll.register(connection.fileno(), select.EPOLLIN)
28             connections[connection.fileno()] = connection
29             requests[connection.fileno()] = b''
30             responses[connection.fileno()] = response
31          elif event & select.EPOLLIN:
32             requests[fileno] += connections[fileno].recv(1024)
33             if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
34                epoll.modify(fileno, select.EPOLLOUT)
35                print('-'*40 + '\n' + requests[fileno].decode()[:-2])
36          elif event & select.EPOLLOUT:
37             byteswritten = connections[fileno].send(responses[fileno])
38             responses[fileno] = responses[fileno][byteswritten:]
39             if len(responses[fileno]) == 0:
40                epoll.modify(fileno, 0)
41                connections[fileno].shutdown(socket.SHUT_RDWR)
42          elif event & select.EPOLLHUP:
43             epoll.unregister(fileno)
44             connections[fileno].close()
45             del connections[fileno]
46 finally:
47    epoll.unregister(serversocket.fileno())
48    epoll.close()
49    serversocket.close()
View Code

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • import java.io.IOException;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.s ...
  • 20170314問題解析請點擊今日問題下方的“【Java每日一題】20170315”查看(問題解析在公眾號首發,公眾號ID:weknow619) 今日問題: 請問主程式運行結果是什麼?(點擊以下“【Java每日一題】20170315”查看20170314問題解析) 題目原發佈於公眾號、簡書:【Jav ...
  • 最近,"大前端"這個詞被頻繁提及,很多團隊也在重新思考"大前端團隊"和"移動團隊+前端團隊"這兩種模式的優劣。而在大家還在熱火朝天地討論概念的時候,餓了麽大前端團隊已經茁壯成長,有了很多先人一步的實踐了。InfoQ 特別邀請了餓了麽大前端部門負責人林建鋒,請他結合餓了麽大前端團隊的實踐,向大家分享如 ...
  • 欄目數組:$arr=Array( Array('cid' => 2,'cname' => '新聞','pid' => 0), Array('cid' => 4,'cname' =>'體育','pid' => 0), Array('cid' => 5,'cname' => '娛樂','pid' => ...
  • 其實就是卡特蘭數的定義。。。 將放置一個1視為(1,1),放置一個0視為(1,-1) 則答案就是從(0,0)出發到(n+m,n-m)且不經過y=-1的方案數。 從(0,0)出發到(n+m,n-m)的總方案數是C(n+m,n)。 若一條路徑經過y=-1,那麼將其從(0,0)到y=-1的一段路徑以y=- ...
  • 用類歐不斷縮小規模,就能在O(T*log2n)時間內求出答案。 題解:http://blog.csdn.net/coldef/article/details/62035919 代碼: 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream ...
  • Python 沙箱逃逸是通過 eval 的安全問題來實現的一種 PWN 方法。 ...
  • 接手了一個maven管理的多模塊項目,又是javaconfig,又是spring data jpa,還算是比較新比較正規的模塊化結構吧..然後我往其中的一個模塊中新添加了一個jpa的entity,然後運行提示找不到新添加的類..倘若這不是jpa項目,倘若這不是javaconfig寫的也許就不會出現之 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...