I'm building a Python web server and need it to render HTML elements. As of right now the program is implemented via aj#ubuntu: ./server.py --base=home/website/html/ --port=8080 and then in the web browser, connecting to the URL http://localhost:8080/index.html. The only thing that displays on that web page however is HTTP/1.1 200 OK which is good since it means I am able to connect to the server. All I need now is the browser to be able to render the contents of the HTML, CSS, and corresponding Javascript components. Any suggestions? I've been scratching my brain trying to figure out how to do this.
I have included my code below:
import socket
import sys
if not sys.version_info[:2] == (3,4):
print("Error: need Python 3.4 to run program")
sys.exit(1)
else:
print("Using Python 3.4 to run program")
import argparse
def main():
parser = argparse.ArgumentParser(description='Project 1 Web Server for COMP/ECPE 177')
parser.add_argument('--version', help='Show program\'s version number and exit')
parser.add_argument('--base', action='store', help='Base directory containing website', metavar='/path/to/directory')
parser.add_argument('--port', action='store', type=int, help='Port number to listen on', metavar='####')
args = parser.parse_args()
#parser.print_help()
#print(args.port, args.base)
# Create TCP socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error as msg:
print("Error: could not create socket")
print("Description: " + str(msg))
sys.exit()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind to listening port
try:
host='' # Bind to all interfaces
s.bind((host,args.port))
except socket.error as msg:
print("Error: unable to bind on port %d" % args.port)
print("Description: " + str(msg))
sys.exit()
# Listen
try:
backlog=10 # Number of incoming connections that can wait
# to be accept()'ed before being turned away
s.listen(backlog)
except socket.error as msg:
print("Error: unable to listen()")
print("Description: " + str(msg))
sys.exit()
print("Listening socket bound to port %d" % args.port)
while 1:
# Accept an incoming request
try:
(client_s, client_addr) = s.accept()
# If successful, we now have TWO sockets
# (1) The original listening socket, still active
# (2) The new socket connected to the client
except socket.error as msg:
print("Error: unable to accept()")
print("Description: " + str(msg))
sys.exit()
print("Accepted incoming connection from client")
print("Client IP, Port = %s" % str(client_addr))
# Receive data
try:
buffer_size=4096
raw_bytes = client_s.recv(buffer_size)
except socket.error as msg:
print("Error: unable to recv()")
print("Description: " + str(msg))
sys.exit()
string_unicode = raw_bytes.decode('ascii')
print("Received %d bytes from client" % len(raw_bytes))
print("Message contents: %s" % string_unicode)
request = str.split(string_unicode)
#print(request)
hcmd = request[0]
filep = request[1]
protocol = request[2]
print(filep)
if filep == '/':
filep = '/index.html'
if hcmd == 'GET':
dest_file = None
try:
try:
dest_file = open(args.base + filep, 'rb')
except (OSError, IOError) as msg:
msg = 'HTTP/1.1 404 Request Not Found\r\n\r\n'
statcode = 1 #404 request not found
rb1 = bytes(msg, 'ascii')
client_s.sendall(rb1)
message_send = 'HTTP/1.1 200 OK\r\n\r\n'
statcode = 0 #200 OK
rb2 = bytes(message_send, 'ascii')
client_s.sendall(rb2)
if dest_file is not None:
datasend = dest_file.read()
client_s.sendall(datasend)
dest_file.close()
print(dest_file)
print(statcode)
except socket.error as msg:
msg2 = "Error: "
sys.exit()
else:
message_send = 'HTTP/1.1 501 Not Implemented\r\n\r\n'
statuscode = 2 #501 not implemented
rb3 = bytes(message_send, 'ascii')
client_s.sendall(rb3)
client_s.close()
#Close both sockets
try:
s.close()
except socket.error as msg:
print("Error: unable to close() socket")
print("Description: " + str(msg))
sys.exit()
print("Sockets closed, now exiting")
if __name__ == "__main__":
sys.exit(main())
Does "home/website/html/index.html" exist in your server? I tried your codes. When accessing an existing file, it works fine. But for serving non-existing files, there's a bug in your codes. The codes for sending file content should be inside the inner-most 'try' block:
try:
dest_file = open(args.base + filep, 'rb')
#---------Should be put here-----------
message_send = 'HTTP/1.1 200 OK\r\n\r\n'
statcode = 0 #200 OK
rb2 = bytes(message_send, 'ascii')
client_s.sendall(rb2)
if dest_file is not None:
datasend = dest_file.read()
client_s.sendall(datasend)
dest_file.close()
print(dest_file)
print(statcode)
#--------------------------------------
except (OSError, IOError) as msg:
msg = 'HTTP/1.1 404 Request Not Found\r\n\r\n'
statcode = 1 #404 request not found
rb1 = bytes(msg, 'ascii')
client_s.sendall(rb1)
Besides, if it's not for learning purpose, using socket to build a web server is really too low level. You have so much extra work to do, e.g. manually compose the common http headers. Try some higher level libraries/frameworks, e.g.
Built-in http server: https://docs.python.org/3.4/library/http.server.html
The popular web framework Django: https://docs.djangoproject.com/en/dev/intro/
Related
I have been trying to figure out this error for a few days. When I try to emit a socket from the client side (JS) to the server (Python - flask) I get the error of connection failed. However, this error isn't always consistent. If the error does happen, it will normally happen on the first time I try to emit the socket and it normally works after that. Also, I am having some trouble with mobile devices receiving events as well. I also have an nginx reverse proxy on my server as well.
When I run this on local host I do not get the error of WebSocket connection failing.
Client side:
function formsubmitted(){
var name = document.getElementById("startexampleInputEmail1").value
var pin = document.getElementById("startexampleInputGamePin").value
var predictions = document.getElementById("startexampleInputPredictions").value
var joinmsg = document.getElementById("startexampleInputJoinMsg").value
socket.emit("AccountCreation",[socket.id,name,pin,predictions,joinmsg])
}
Server side:
#socketio.on("AccountCreation")
def handle_new_acc(data):
sid = data[0]
name = data[1]
pin = data[2]
predictions = data[3]
joinmsg = data[4]
canCreateAcc = True
gameExists = False
for user in User.query.all():
if user.name == name:
canCreateAcc = False
emit("UsernameTaken",to=sid)
return
for game in Game.query.all():
if game.pin == pin:
gameExists = True
if gameExists == False:
emit("GameNotFound",to=sid)
if gameExists == True and canCreateAcc == True:
usertoadd = User(name,pin,predictions,joinmsg,1,"0 seconds","0 seconds",0,"")
db.session.add(usertoadd)
db.session.commit()
game = Game.query.filter_by(pin=pin).first()
player_list = json.loads(game.players)
player_list.append(name)
game.players = json.dumps(player_list)
db.session.commit()
emit("AccountReady",[name,pin,predictions,joinmsg],to=sid)
There may be a problem with your nginx configuration. Just recently I had a similar issue (although the connections failed all the time, not just sometimes), see if some of it helps: Websocket failed: flask app with nginx and gunicorn, react frontend with socket.io
I have a small server in Python that I'm trying to get to terminate based on a message it gets from a client. This is the server:
async def receiver(websocket, path):
received_data = await websocket.recv()
print("< {}".format(received_data))
if received_data == "Order66":
websocket.keep_running = False
asyncio.get_event_loop().stop()
else:
print("< {}".format(received_data))
def start_the_server():
start_server = websockets.serve(receiver, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
def main():
start_the_server()
if __name__ == "__main__":
main()
The "client" is a larger app which also has a Javascript API and I am trying to this:
// send the info and disconnect
const socket2 = new WebSocket('ws://localhost:8765');
socket2.addEventListener('open', function (event) {
socket2.send(stats);
socket2.send("Order66");
});
However, it only ever sends "stats" (in this case it's a string), and the second send(...) never seems to get sent.
I want to refresh my web page every time a file is uploaded to the folder.
I have a web service written in flask having the following handler
#app.route('/getlatest/')
def getlatest():
import os
import glob
newset = max(glob.iglob('static/*'),key=os.path.getctime)
Return newest;
This gives me the name of the latest file in the folder.
I have an Ajax call in my JS (client side) to constantly get data from above function.
function GetLatest()
{
$.ajax({
url: "http://localhost:5000/getlatest",
success: function(result)
{
if(previousName != result){
previousName = result;
$("#image").attr("src","/"+previousName);
}
}
});
}
function calling server every second.
(function myLoop (i) {
setTimeout(function () {
GetLatest();
if (--i) myLoop(i);
}, 1000)
})(100);
This Works fine [well almost].
My Question is: Is there any better way to do it[ there must be ]?
i'm open to technology choices what every they may be [node, angualr etc.]
yea you can use websockets(flask-socketio).That will let you to have an open connection between you and server and every time in the folder is a new photo will be displayed on a selected div
http://flask-socketio.readthedocs.io/en/latest/
https://pypi.python.org/pypi/Flask-SocketIO/2.9.1
So here is how i did it.
first of all thanks to
https://blog.miguelgrinberg.com/post/easy-websockets-with-flask-and-gevent
for explaining it perfectly.
What i have learnt reading a couple of blogs.
All communication initiated using http protocol is client server communication, and client is always the initiator. So in this case we have to use a different protocol: Web-sockets which allow you to create a full-duplex (two way) connection.
Here is the server code;
socketio = SocketIO(app, async_mode=async_mode)
thread = None
prevFileName = ""
def background_thread():
prevFileName = ""
while True:
socketio.sleep(0.5)
if(isNewImageFileAdded(prevFileName) == "true"):
prevFileName = getLatestFileName()
socketio.emit('my_response',
{'data': prevFileName, 'count': 0},
namespace='/test');
def getLatestFileName():
return max(glob.iglob('static/*'),key=os.path.getctime)
def isNewImageFileAdded(prevFileName):
latestFileName = max(glob.iglob('static/*'),key=os.path.getctime)
if(prevFileName == latestFileName):
return "false"
else:
return "true"
Creating separate thread to keep the socket open. emit sends the msg to the client from server...
#socketio.on('connect', namespace='/test')
def test_connect():
global thread
if thread is None:
thread = socketio.start_background_task(target=background_thread)
emit('my_response', {'data': 'Connected', 'count': 0})
And here is Client Side.[replaced ajax call with the following code]
var socket = io.connect(location.protocol + '//' + document.domain + ':' +
location.port + namespace);
socket.on('my_response', function(msg) {
setTimeout(function(){ $("#image").attr("src","/"+msg.data)},1000)
});
Please correct me if i am wrong.
I am currently trying to use Tornado's web-socket handlers to update a dashboard every time a certain function is called. here is the handler:
class WebSocketHandler(websocket.WebSocketHandler):
clients = []
def open(self):
logging.info("WEBSOCKET OPEN")
WebSocketHandler.clients.append(self)
def on_message(self, message):
logging.info("message from websocket recieved")
self.write_message("WebSocket connected")
def on_close(self):
logging.info("WEBSOCKET closed")
and here is the client-side script which connects the WebSocket on load:
function WebSocketTest()
{ var ws = 0;
ws = new WebSocket("ws://localhost:8008/WEB");
ws.onopen = function()
{
ws.send("initial connect")
}
ws.onmessage = function (evt)
{
console.log(evt.data)
};
ws.onclose = function()
{
console.log("closed ");
};
}
The websockets connect sucessfully.
i need to call write_message from WebSocketHandler but i'm very confused what the instance of it is? the error i keep running into is that self isn't defined but im not sure what self is exactly? i know that WebSocketHandler gets run whenever the client tries to load ^/WEB$
EDIT: here is my server.py file, i need to call write_message right after the spawn callback call in itercheckers
class Server():
#classmethod
def run(cls):
options.parse_command_line()
# Start web
template_path = os.path.join(os.path.dirname(__file__), 'templates')
jinja2loader = Jinja2Loader(template_path)
kwargs = dict(
template_loader=jinja2loader,
static_path=os.path.join(os.path.dirname(__file__), 'static'),
debug=True,
login_url="/auth/login",
cookie_secret="dn470h8yedWF9j61BJH2aY701i6UUexx"
)
app = web.Application(handlers, **kwargs).listen(
configuration['server']['port'],)
# Reset events
#gen.coroutine
def reset(parent=None):
if parent is None:
parent = configuration
# Reset event happyness
yield events.reset_happy(parent)
# Read last status
data = yield events.get(parent)
# Read and set happy from the last status
happy = (data or {}).get('status', events.STATUS_OK) \
in events.HAPPY
yield events.set_happy(parent, happy)
# Iterate sub-events
for event in parent['events']:
yield reset(event)
ioloop.IOLoop.current().run_sync(reset)
# Start checkers
def itercheckers(parent):
index = 0
for event in parent.get('events', []):
if 'checker' in event:
checker = event['checker']
p, m = checker['class'].rsplit('.', 1)
ioloop.IOLoop.current().spawn_callback(
getattr(importlib.import_module(p), m)(
event=event,
frequency=checker.get('frequency', 1),
params=checker['params']
).run)
index += 1
itercheckers(event)
itercheckers(configuration)
# Start alerts
ioloop.IOLoop.current().run_sync(alerts.reset)
for alert in configuration['alerts']:
p, m = alert['class'].rsplit('.', 1)
ioloop.IOLoop.current().spawn_callback(
getattr(importlib.import_module(p), m)(
alert=alert
).run
)
# Start loop
ioloop.IOLoop.current().start()
First thing first, self keyword is pointed to current websocket client that is handled in the moment. To use tornado websockets you must initialize tornado app
app = web.Application([
(r'/ws', WSHandler), #tells it to redirect ws:// to websocket handler
#Choose different names from defaults because of clarity
])
if __name__ == '__main__':
app.listen(5000) #listen on what port
ioloop.IOLoop.instance().start()
Then you must have WSHandler class you're pointing websockets trafic to
class WSHandler(websocket.WebSocketHandler):
#crossdomain connections allowed
def check_origin(self, origin):
return True
#when websocket connection is opened
def open(self):
print("Client connected ")
def on_close(self):
print("Client disconnected")
def on_message(self,message):
self.write_message(message) #echo back whatever client sent you
So complete app would look like
from tornado import websocket, web, ioloop
clients = []
#whenever you want to broadcast to all connected call this function
def broadcast_message(msg):
global clients
for client in clients:
client.write_message(msg)
class WSHandler(websocket.WebSocketHandler):
#crossdomain connections allowed
def check_origin(self, origin):
return True
#when websocket connection is opened
def open(self):
#here you can add clients to your client list if you want
clients.append(self)
print("Client connected ")
def on_close(self):
clients.remove(self)
print("Client disconnected")
def on_message(self,message):
self.write_message(message) #echo back whatever client sent you
app = web.Application([
(r'/ws', WSHandler), #tells it to redirect ws:// to websocket handler
#Choose different names from defaults because of clarity
])
if __name__ == '__main__':
app.listen(5000) #listen on what port
ioloop.IOLoop.instance().start()
And now to connect to it with js
function WebSocketTest()
{
var ws = new WebSocket("ws://localhost:5000/ws");
ws.onopen = function()
{
ws.send("initial connect")
}
ws.onmessage = function (evt)
{
console.log(evt.data)
};
ws.onclose = function()
{
console.log("closed ");
};
}
I haven't tested it but should work
I am trying to build a WebSocket session using Python 3.4, Django, Autobahn and JS. I have successfully run the websocket server on the python side, but i cannot subscribe or receive any data published by the server
My code is fairly simple:
class TestAppWS(ApplicationSession):
"""
An application component that publishes an event every second.
"""
def onConnect(self):
self.join(u"realm1")
#asyncio.coroutine
def onJoin(self, details):
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
def start_ws():
print("Running")
session_factory = ApplicationSessionFactory()
session_factory.session = TestAppWS
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# factory = WebSocketServerFactory("ws://localhost:8090", debug=False)
# factory.protocol = MyServerProtocol
server = None
try:
transport_factory = WampWebSocketServerFactory(session_factory, debug_wamp=True)
loop = asyncio.get_event_loop()
coro = loop.create_server(transport_factory, 'localhost', 8090)
server = loop.run_until_complete(coro)
loop.run_forever()
except OSError:
print("WS server already running")
except KeyboardInterrupt:
pass
finally:
if server:
server.close()
loop.close()
start_ws() is run inside a separate Thread object. If I access localhost:8090 on my browser I can see the Autobahn welcome message.
On the frontend I have
var connection = new autobahn.Connection({
url: 'ws://localhost:8090/',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();
It does not seem to work, when I try to connect the frontend I get the following error on the backend side:
Failing WAMP-over-WebSocket transport: code = 1002, reason = 'WAMP Protocol Error (Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established)'
WAMP-over-WebSocket transport lost: wasClean = False, code = 1006, reason = 'connection was closed uncleanly (I failed the WebSocket connection by dropping the TCP connection)'
TX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RolePublisherFeatures object at 0x04710270>, <autobahn.wamp.role.RoleSubscriberFeatures object at 0x047102B0>, <autobahn.wamp.role.RoleCallerFeatures object at 0x047102D0>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x047102F0>], authmethods = None, authid = None)
RX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RoleSubscriberFeatures object at 0x04710350>, <autobahn.wamp.role.RoleCallerFeatures object at 0x04710330>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x04710390>, <autobahn.wamp.role.RolePublisherFeatures object at 0x04710370>], authmethods = None, authid = None)
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\autobahn\wamp\websocket.py", line 91, in onMessage
self._session.onMessage(msg)
File "C:\Python34\lib\site-packages\autobahn\wamp\protocol.py", line 429, in onMessage
raise ProtocolError("Received {0} message, and session is not yet established".format(msg.__class__))
autobahn.wamp.exception.ProtocolError: Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established
on the javascript console I see:
Uncaught InvalidAccessError: Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999. 1002 is neither.
Any idea? It looks like the session is not started, honestly it is not clear how this session work. Should not the session be initialized once a connection from the client is made?
Your TestAppWs and your browser code are both WAMP application components. Both of these need to connect to a WAMP router. Then they can talk freely to each other (as if there were no router in between .. transparently).
Here is how to run.
Run a WAMP Router.
Using Crossbar.io (but you can use other WAMP routers as well), that's trivial. First install Crossbar.io:
pip install crossbar
Crossbar.io (currently) runs on Python 2, but that's irrelevant as your app components can run on Python 3 or any other WAMP supported language/run-time. Think of Crossbar.io like a black-box, an external infrastructure, like a database system.
Then create and start a Crossbar.io default router:
cd $HOME
mkdir mynode
cd mynode
crossbar init
crossbar start
Run your Python 3 / asyncio component
import asyncio
from autobahn.asyncio.wamp import ApplicationSession
class MyComponent(ApplicationSession):
#asyncio.coroutine
def onJoin(self, details):
print("session ready")
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
if __name__ == '__main__':
from autobahn.asyncio.wamp import ApplicationRunner
runner = ApplicationRunner(url = "ws://localhost:8080/ws", realm = "realm1")
runner.run(MyComponent)
Run your browser component
var connection = new autobahn.Connection({
url: 'ws://localhost:8080/ws',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();