Creating a WebSocket Client in Python

I am trying to learn about socket programming as well as the WebSocket protocol. I know that there are python web socket clients, but I hope to build a toy for my training. To do this, I created an extremely simple Tornado web server server, which I run on localhost:8888 . All he does is print a message when the client connects.

This is the whole server - and it works (I tested it with a small javascript script in my browser)

 import tornado.httpserver import tornado.websocket import tornado.ioloop import tornado.web class WSHandler(tornado.websocket.WebSocketHandler): def open(self): print('new connection') self.write_message("Hello World") def on_message(self, message): print('message received %s' % message) def on_close(self): print('connection closed') application = tornado.web.Application([ (r'/ws', WSHandler), ]) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start() 

So, as soon as I start the server, I try to run the following script

 import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((socket.gethostbyname('localhost'), 8888)) msg = '''GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13'''.encode('ascii') print(len(msg)) sent_count = sock.send(msg) print('sent this many bytes:', sent_count) recv_value = sock.recv(1) print('recvieved:', recv_value) 

I hope the server will send a response header as specified in the RFC. Instead, sock.recv hangs. This makes me think that the server does not recognize the initial websocket handshake. This handshake is also removed from the RFC. I know that the websocket key must be random and that’s it, but I don’t think that this will cause the server to ignore the handshake (the websocket key is valid). I think I can understand the rest as soon as I can initiate a handshake, so I hope that there is some misunderstanding about how webcams work or how to send an initial handshake.

+4
source share
1 answer

1) When you send a message through a socket, you have no idea how many blocks will be split. It can be sent immediately; or the first 3 letters can be sent, then the rest of the message; or the message can be divided into 10 parts.

2) Given 1) how should the server know when it received all the pieces sent by the client? For example, suppose a server receives 1 part of a client message. How does the server know if this was the whole message or are there 9 more pieces?

3) I suggest you read the following:

http://docs.python.org/2/howto/sockets.html

(Plus links in the comments)

4) Now, why don't you use python to create an HTTP server?

python3:

 import http.server import socketserver PORT = 8000 handler = http.server.SimpleHTTPRequestHandler httpd = socketserver.TCPServer(("", PORT), handler) print("serving at port", PORT) httpd.serve_forever() 

python2:

 import SimpleHTTPServer import SocketServer PORT = 8000 handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = SocketServer.TCPServer(("", PORT), handler) print "serving at port", PORT httpd.serve_forever() 

SimpleHTTPRequestHandler serves files from the server program directory and below, matching the request URL with the created directory structure. If you request "/", the server will serve the index.html file from the same directory in which the server is located. Below is an example client socket for python 3 (python 2 example below):

 import socket import sys try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error: print('Failed to create socket') sys.exit() print('Socket Created') #To allow you to immediately reuse the same port after #killing your server: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) host = 'localhost'; port = 8000; s.connect((host , port)) print('Socket Connected to ' + host + ' on port ', port) #Send some data to server message = "GET / HTTP/1.1\r\n\r\n" try : #Send the whole string(sendall() handles the looping for you) s.sendall(message.encode('utf8') ) except socket.error: print('Send failed') sys.exit() print('Message sent successfully') #Now receive data data = [] while True: chunk = s.recv(4096) #blocks while waiting for data if chunk: data.append(chunk.decode("utf8")) #If the recv() returns a blank string, then the other side #closed the socket, and no more data will be sent: else: break print("".join(data)) --output:-- Socket Created Socket Connected to localhost on port 8000 Message sent successfully HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.2.3 Date: Sat, 08 Jun 2013 09:15:18 GMT Content-type: text/html Content-Length: 23 Last-Modified: Sat, 08 Jun 2013 08:29:01 GMT <div>hello world</div> 

In python 3 you have to use byte strings with sockets, otherwise you will get scary:

 TypeError: 'str' does not support the buffer interface 

Here it is in python 2.x:

 import socket import sys try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error: print 'Failed to create socket' sys.exit() print('Socket Created') #To allow you to immediately reuse the same port after #killing your server: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) host = 'localhost'; port = 8000; s.connect((host , port)) print('Socket Connected to ' + host + ' on port ', port) #Send some data to server message = "GET / HTTP/1.1\r\n\r\n" try : #Send the whole string(handles the looping for you) s.sendall(message) except socket.error: print 'Send failed' sys.exit() print 'Message sent successfully' #Now receive data data = [] while True: chunk = s.recv(4096) #blocks while waiting for data if chunk: data.append(chunk) #If recv() returns a blank string, then the other side #closed the socket, and no more data will be sent: else: break print("".join(data)) --output:-- Message sent successfully HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/2.7.3 Date: Sat, 08 Jun 2013 10:06:04 GMT Content-type: text/html Content-Length: 23 Last-Modified: Sat, 08 Jun 2013 08:29:01 GMT <div>hello world</div> 

Note that the GET request header tells the server that HTTP 1.1 will be the protocol, that is, the rules governing the conversation. And, as the RFC describes for HTTP 1.1, the request must have two sequences "\ r \ n". Thus, the server searches for the second second sequence "\ r \ n". If you remove one of the sequences "\ r \ n" from the request, the client will hang on recv () because the server is still waiting for more data because the server did not read this second '\ r \ n' sequence.

Also note that you will send data as bytes (in python 3), so there will be no automatic conversions "\ n", and the server will wait for the sequence "\ r \ n".

+10
source

Source: https://habr.com/ru/post/1485152/


All Articles