Creating a WebSocket Client in Python - sockets

I am trying to learn about socket programming as well as the WebSocket protocol. I know that there are python web socket clients in existence but I am hoping to just build a toy version for my own learning. To do this I have created an extremely simple Tornado websocket server that I am running on localhost:8888. All it does is print a message when a client connects.
This is the entire server - and it works (I have 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 once I start up 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)
What I am hoping is that the server will send back the response header as specified in the RFC. Instead the sock.recv is hanging. This leads me to believe the server isn't acknowledging the websocket initial handshake. This handshake is pulled off of the RFC as well. I know that the websocket key should be random and everything, but I don't think that would cause the server to ignore the handshake (the websocket key is valid). I think I can figure the rest out once I can initiate the handshake so I am hoping that there is just some misunderstanding in either how websockets work or how to send the initial handhake.

1) When you send a message over a socket, you have no idea how many chunks it will be divided into. It may all get sent at once; or the first 3 letters may be sent, then the rest of the message; or the message may be split into 10 pieces.
2) Given 1) how is the server supposed to know when it has received all the chunks sent by the client? For instance, suppose the sever receives 1 chunk of the client's message. How does the server know whether that was the whole message or whether there are 9 more chunks coming?
3) I suggest you read this:
http://docs.python.org/2/howto/sockets.html
(Plus the links in the comments)
4) Now, why aren't you using 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()
The SimpleHTTPRequestHandler serves files out of the server program's directory and below, matching the request url to the directory structure you create. If you request '/', the server will serve up an index.html file out of the same directory the server is in. Here is an example of a 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 the dreaded:
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 header of the GET requests tells the server that HTTP 1.1 will be the protocol, i.e. the rules governing the conversation. And as the RFC for HTTP 1.1 describes, there has to be two '\r\n' sequences in the request. So the server is looking for that second '\r\n' sequence. If you delete one of the '\r\n' sequences from the request, the client will hang on the recv() because the server is still waiting for more data because the server hasn't read that second '\r\n' sequence.
Also note that you will be sending the data as bytes(in python 3), so there are not going to be any automatic '\n' conversions, and the server will be expecting the sequence '\r\n'.

Related

Unable to establish connection to a tcp socket server on aws ec2

I am trying to run a socket server on an aws ec2 ubuntu instance and then connecting with it using my local machine. I was successful with sending HTTP GET requests to my server hosted on aws ec2 instance and getting a response, but I am unable to connect the server and client using sockets, even after enabling custom TCP on the complete range of ports on the instance.
Here is a screenshot describing my instance's inbound and outbound rules :
screenshot
I used this python code from the internet to test socket networking :
socket_client.py :
import socket
def client_program():
host = "<aws instance public ip address>"
port = 8080 # socket server port number
client_socket = socket.socket() # instantiate
client_socket.connect((host, port)) # connect to the server
message = input(" -> ") # take input
while message.lower().strip() != 'bye':
client_socket.send(message.encode()) # send message
data = client_socket.recv(1024).decode() # receive response
print('Received from server: ' + data) # show in terminal
message = input(" -> ") # again take input
client_socket.close() # close the connection
if __name__ == '__main__':
client_program()
socket_server.py :
import socket
def server_program():
host = "0.0.0.0"
port = 8080 # initiate port no above 1024
server_socket = socket.socket() # get instance
# look closely. The bind() function takes tuple as argument
server_socket.bind((host, port)) # bind host address and port together
# configure how many client the server can listen simultaneously
server_socket.listen(2)
conn, address = server_socket.accept() # accept new connection
print("Connection from: " + str(address))
while True:
# receive data stream. it won't accept data packet greater than 1024 bytes
data = conn.recv(1024).decode()
if not data:
# if data is not received break
break
print("from connected user: " + str(data))
data = input(' -> ')
conn.send(data.encode()) # send data to the client
conn.close() # close the connection
if __name__ == '__main__':
server_program()
Note that the socket server-client are working completely fine when run on my local machine.
Please help if you know about how to fix this .
Edit - I have also tried with disabling firewall on the linux ec2 instance, but the problem still persists.
Thanks.

SMTP - 503 Bad sequence of commands when using nim's std/smtp

I am building a web application in the nim programming language and recently wanted to start implementing features regarding sending mails.
Nim has a library std/smtp for this that comes with some pretty simple examples for using both starttls and ssl.
For that purpose I created a dummy mail address on a local mail service provider to play around a bit and can't get it to work, despite following the examples as closely as I could.
Here the code I used:
import std/[smtp]
let smtpServerName = "smtp.web.de"
let startTlsportNumber = 587
let un = "dummymail" # replace with actual username
let pw = "dummypw" # replace with actual pw
let target = "bla#mailinator.com"
#StartTLS code block - Have either this or the SSL code block commented in, not both
let smtpConn = newSmtp(debug=true)
smtpConn.connect(smtpServerName, Port startTlsportNumber)
smtpConn.startTls()
#SSL code block - Have either this or the startTLS code block commented in, not both
# let sslPortNumber = 465
# let smtpConn = newSmtp(useSsl = true, debug=true)
# smtpConn.connect(smtpServerName, Port sslPortNumber)
var msg = createMessage(mSubject = "Hello from Nim's SMTP",
mBody = "Hello!.\n Is this awesome or what?",
mTo = #[target])
smtpConn.auth(un, pw)
smtpConn.sendmail(un, #[target], $msg)
With startTLS this causes a runtime error as the server sends back a 503 response on smtpConn.startTls:
S:220 web.de (mrweb105) Nemesis ESMTP Service ready
C:HELO smtp.web.de
S:250 web.de Hello smtp.web.de [46.183.103.17]
C:STARTTLS
S:503 Bad sequence of commands
C:QUIT
/home/philipp/dev/playground/src/playground.nim(11) playground
/usr/lib/nim/pure/smtp.nim(246) startTls
/usr/lib/nim/pure/smtp.nim(226) checkReply
/usr/lib/nim/pure/smtp.nim(110) quitExcpt
Error: unhandled exception: Expected 220 reply, got: 503 Bad sequence of commands [ReplyError]
With SSL this causes a runtime error as the server sends back a 503 response on smtpConn.connect:
S:220 web.de (mrweb005) Nemesis ESMTP Service ready
C:HELO smtp.web.de
S:503 Bad sequence of commands
C:QUIT
/home/philipp/dev/playground/src/playground.nim(15) playground
/usr/lib/nim/pure/smtp.nim(240) connect
/usr/lib/nim/pure/smtp.nim(231) helo
/usr/lib/nim/pure/smtp.nim(226) checkReply
/usr/lib/nim/pure/smtp.nim(110) quitExcpt
Error: unhandled exception: Expected 250 reply, got: 503 Bad sequence of commands [ReplyError]
I've triple checked the ports, the service provider states explicitly that 587 is the port for startTls and 465 for SSL. I compile on Arch Linux with -d:ssl in both cases, so that shouldn't be an issue either.
Trying the same with google-mail it all worked out, but I am slightly annoyed that this didn't work with my original mail provider.
Googling the error for a bit and looking at other questions of other programming languages, the error seems to be related to authentication? Which is weird, because I thought authentication starts after I made the connection secure with startTls. This looks to me like I'm using the std/smtp library wrong, but I don't quite see where. Does anyone here see the issue?
The server doesn't like the HELO that std/smtp uses, it needs EHLO:
$ telnet smtp.web.de 587
[ ... ]
220 web.de (mrweb105) Nemesis ESMTP Service ready
HELO smtp.web.de
250 web.de Hello smtp.web.de [92.191.80.254]
STARTTLS
503 Bad sequence of commands
$ telnet smtp.web.de 587
[ ... ]
220 web.de (mrweb006) Nemesis ESMTP Service ready
EHLO smtp.web.de
250-web.de Hello smtp.web.de [92.191.80.254]
250-8BITMIME
250-SIZE 141557760
250 STARTTLS
STARTTLS
220 OK
So you can get the Nim dev version with this patch, or copy-paste the patch code into your program while it doesn't reach the stable:
import std / [net, strutils, asyncnet, asyncdispatch]
proc recvEhlo(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} =
## Skips "250-" lines, read until "250 " found.
## Return `true` if server supports `EHLO`, false otherwise.
while true:
var line = await smtp.sock.recvLine()
echo("S:" & line) # Comment out this if you aren't debugging
if line.startsWith("250-"): continue
elif line.startsWith("250 "): return true # last line
else: return false
proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} =
# Sends the EHLO request
await smtp.debugSend("EHLO " & smtp.address & "\c\L")
return await smtp.recvEhlo()
proc connect*(smtp: Smtp | AsyncSmtp, address: string, port: Port) {.multisync.} =
smtp.address = address
await smtp.sock.connect(address, port)
await smtp.checkReply("220")
let speaksEsmtp = await smtp.ehlo()
if speaksEsmtp:
await smtp.debugSend("STARTTLS\c\L")
await smtp.checkReply("220")
let smtpConn = newSmtp(debug=true)
smtpConn.connect("smtp.web.de", Port 587)
Notice that you need to avoid the call to startTls because the unpatched version does a HELO that would raise another 503, so we send the STARTTLS at the end of our modified connect.
I've noticed that you need to modify your local smtp.nim file to make sock field for SmtpBase object public for this to work. The file is at nim-1.6.6/lib/pure/smtp.nim and you need to change:
62 sock: SocketType
to
62 sock*: SocketType
So it might be better to just patch the whole smtp.nim local file with the code linked above.

How should I do to check if the service running on a remote host?

I'm writing a port scanner and I want to verify if a specific port is running the standard service expected on that port, such as SSH and HTTP. I know one of the methods is to send a query to that port and analyze the returned information. For example, SSH sends me the version information immediately upon connection. However, when I did connect() to port 22 on an ip address, I only got Error number: 110. Error message: Connection timed out. Any idea will be appreciated.
The whole code is a long story. I paste some excerpt here.
struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(22);
dest_addr.sin_addr.s_addr = inet_addr("8.8.8.8");
int service_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (service_sock < 0) {
printf("Error creating service socket. Error number: %d. Error message: %s\n", errno, strerror(errno));
exit(0);
}
int dest_addr_len = sizeof(dest_addr);
if (connect(service_sock, (struct sockaddr *)&dest_addr, dest_addr_len) < 0) { // stuck here
printf("Error connection. Error number: %d. Error message: %s\n", errno, strerror(errno));
exit(0);
}
To clarify this question, I need to show an example. I just figured out a method to verify HTTP service. That is to send a string "GET / HTTP\n\n" to destination address. Then call recv() function to read the returned message. I can get something like this.
HTTP/1.0 400 Bad Request
Content-Type: text/html; charset=UTF-8
Content-Length: 1419
Date: Tue, 02 Dec 2014 05:56:25 GMT
Server: GFE/2.0
...
I can read the HTTP version is 1.0 from the first line.
Here I want to check many services on the remote host including but not limited to SSH, HTTP. I don't think guessing is a good idea. So I need a general method to retrieve those information from dest_addr.
OK, finally I need to answer my own question. I just got this verification work and wrote a post for it.

How to close() server connection with key-press? (Simple networking with Socket)

So I am very new to networking and the Socket module in Python. So I watched some Youtube tutorials and found one on how to write the code for a simple server. My problem is right when the server receives data from the client, the server close() and loses connection to the client right when it receives the data. I want the server to automatically lose connection to the client but not "shutdown" or close(). I want to set it (if its possible) so that while the server is running in my Python Shell, if I want to close() the connection I use hot keys like for example "Control+E"? Here is my code so far:
#!/usr/bin/python
import socket
import sys
# Create a TCP/IP socket to listen on
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Prevent from "adress already in use" upon server restart
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind the socket to port 8081 on all interfaces
server_address = ('localhost',8081)
print ('starting up on %s port %s')%(server_address)
server.bind(server_address)
#Listen for connections
server.listen(5)
#Wait for one incoming connection
connection, client_address = server.accept()
print 'connection from', connection.getpeername()
# Let's recieve something
data = connection.recv(4096)
if data:
print "Recived ", repr(data)
#send the data back nicely formatted
data = data.rstrip()
connection.send("%s\n%s\n%s\n"%('-'*80, data.center(80),'-'*80))
# lose the connection from our side (the Server side)
connection.shutdown(socket.SHUT_RD | socket.SHUT_WR)
connection.close()
print 'Connection closed'
# And stop listening
server.close()
==================================================================================
Here is the code I am using (on the server side):
#!/usr/bin/python
import socket, sys
import select
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Let's set the socket option reuse to 1, so that our server terminates quicker
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("localhost", 8081))
srv.listen(5)
while True:
print "Waiting for a client to connect"
(client, c_address) = srv.accept() #blocking wait for client
print "Client connected"
# Client has connected, add him to a list which we can poll for data
client_list = [client]
while close_socket_condition == 0:
ready_to_read, ready_to_write, in_error = select.select(client_list, [], [] , 1) #timeout 1 second
for s in ready_to_read: #Check if there is any socket that has data ready for us
data = client.recv(1024) # blocks until some data is read
if data:
client.send("echo:" + data)
client.close()
close_socket_condition = 1
And here is the error it is giving me when I try to send a string to the server:
data = s.recv(1024)
File "C:\Python27\lib\socket.py", line 170, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: [Errno 9] Bad file descriptor
Here is example on a non-blocking socket read with similar structure as yours.
The server will establish a socket in localhost, and wait for a client to connect. After that it will start polling the socket for data, and also keep checking the exit condition close_socket_condition. Handling ctrl-e or other exit events will be left as an exercise :)
First we start socket, very much the same way as you:
#!/usr/bin/python
import socket, sys
import select
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Let's set the socket option reuse to 1, so that our server terminates quicker
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("localhost", 8081))
srv.listen(5)
Then we declare our external exit condition close_socket_condition, and start eternal while loop that will always welcome new clients:
close_socket_condition = 0
while True:
print "Waiting for a client to connect"
(client, c_address) = srv.accept() #blocking wait for client
print "Client connected"
Now a client has connected, and we should start our service loop:
# Client has connected, add him to a list which we can poll for data
client_list = [client]
while close_socket_condition == 0:
Inside the service loop we will keep polling his socket for data and if nothing has arrived, we check for exit condition:
ready_to_read, ready_to_write, in_error = select.select(client_list, [], [] , 1) #timeout 1 second
for s in ready_to_read: #Check if there is any socket that has data ready for us
data = client.recv(1024) # blocks until some data is read
if data:
client.send("echo:" + data)
client.close()
close_socket_condition = 1
This code is simplified example, but the server will keep accepting new clients, and always reuse the connection. It does not handle client side terminations etc.
Hope it helps

Python Server Client WinError 10057

I'm making a server and client in Python 3.3 using the socket module. My server code is working fine, but this client code is returning an error. Here's the code:
import socket
import sys
import os
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = ('192.168.1.5', 4242)
sock.bind(server_address)
while True:
command = sock.recv(1024)
try:
os.system(command)
sock.send("yes")
except:
sock.send("no")
And here's the error:
Error in line: command = sock.recv(1024)
OSError: [WinError 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
What on earth is going on?
It looks like you're confused about when you actually connect to the server, so just remember that you always bind to a local port first. Therefore, this line:
server_address = ('192.168.1.5', 4242)
Should actually read:
server_address = ('', 4242)
Then, before your infinite loop, put in the following line of code:
sock.connect(('192.168.1.5', 4242))
And you should be good to go. Good luck!
EDIT: I suppose I should be more careful with the term "always." In this case, you want to bind to a local socket first.
You didn't accept any requests, and you can only recv and/or send on the accepted socket in order to communicate with client.
Does your server only need one client to be connected? If so, try this solution:
Try adding the following before the while loop
sock.listen(1) # 1 Pending connections at most
client=sock.accept() # Accept a connection request
In the while loop, you need to change all sock to client because server socket cannot
be either written or read (all it does is listening at 192.168.1.5:4242).