-
-
Save ishworgurung/2779992 to your computer and use it in GitHub Desktop.
| #!/usr/bin/env python | |
| """ | |
| Example on using Kqueue/Kevent on BSD/Mac | |
| using Python. | |
| The TCP server essentially echoes back the | |
| message it receives on the client socket. | |
| """ | |
| __author__ = "Ishwor Gurung <[email protected]>" | |
| __license__ = "3 clause BSD" | |
| from socket import socket, \ | |
| AF_INET, SOCK_STREAM,\ | |
| SOL_SOCKET, SO_REUSEADDR,\ | |
| SHUT_WR | |
| import sys | |
| import select | |
| BUFSIZE = 512 | |
| def handle_connection(cl_socket): | |
| """ | |
| Handle each client socket. Receive data on it and send | |
| the data back to the client. If there are no more data | |
| available on the read side, shutdown and close the | |
| socket. | |
| """ | |
| while True: | |
| m = cl_socket.recv(BUFSIZE) | |
| if m and len(m)>0: | |
| cl_socket.send(m) | |
| else: | |
| cl_socket.shutdown(SHUT_WR) | |
| cl_socket.close() | |
| break | |
| def main(): | |
| s = socket(AF_INET, SOCK_STREAM) | |
| s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) | |
| s.bind(("127.0.0.1",10000)) | |
| s.listen(10) | |
| kq = select.kqueue() | |
| # Initialise the master fd(s.fileno()) from server socket | |
| kevent = select.kevent(s.fileno(), | |
| filter=select.KQ_FILTER_READ, # we are interested in reads | |
| flags=select.KQ_EV_ADD | select.KQ_EV_ENABLE) | |
| while True: | |
| revents = kq.control([kevent], 1, None) | |
| for event in revents: | |
| # If the kernel notifies us saying there is a read event available | |
| # on the master fd(s.fileno()), we accept() the | |
| # connection so that we can recv()/send() on the the accept()ed | |
| # socket | |
| if (event.filter == select.KQ_FILTER_READ): | |
| cl,_ = s.accept() | |
| handle_connection(cl) | |
| if __name__=="__main__": | |
| sys.exit(main()) |
handle_connection here would actually block the kqueue loop, right?
Gee @xh4n3 I've clearly missed this discussion!
As @zhanglistar has said already - yes the above code can handle only a single connection at a time because of handle_connection.
However, fixing it is simple. Consider something like this that I whipped up (untested):
class ThreadedEchoConnection(threading.Thread):
def __init__(self, _socket):
super(ThreadedEchoConnection, self).__init__()
self._socket = _socket
def run(self):
while True:
msg = self._socket.recv(BUFSIZE)
if msg and len(msg)>0:
self._socket.send(msg)
else:
self._socket.shutdown(SHUT_WR)
self._socket.close()
break@ishworgurung FWIW I believe you only need to pass in a kevent list if you want to change the queue, so should be able to call kqueue.control([event], 0, 0) once before the loop (add your event to the watchqueue, don't retrieve any events, return immediately) then call kqueue.control(None, 1) to retrieve your events.
@xmo-odoo you are right.
@ishworgurung
handle_connection is blocking due to the recv(), send() methods of the server socket is blocking, can we avoid using thread to fix this blocking, and just use non-blocking sockets for recv(), send() in this context?
yes @xh4n3