I am interfacing with a server that requires some http communication at the beginning (handshake with GET's/POST's), but later switches to raw data packets over tcp. Raw packets are sent and received using the connection established with last 'GET' packet with 'connection: keep-alive' header.
I have managed to read incoming data using response.raw._fp.read() and stream=True, but i can't find a way to send data back to server.
By raw packets I mean bytestream, without any method/url or headers.
resp = session.get(
'http://server.server/test',
stream=True
)
while True:
try:
header = resp.raw._fp._safe_read(8)
if header[3]>0:
data = resp.raw._fp._safe_read(header[3])
except http.client.IncompleteRead:
break
Want you want to do is get the underlying socket from the connection that requests create, and fortunately you can do it for streaming connections.
import requests
import socket
r = requests.get('http://server.server/test', stream=True)
s = socket.fromfd(r.raw.fileno(), socket.AF_INET, socket.SOCK_STREAM)
After that you can send data as with any other socket. Side note for non-streaming connections the file descriptor is cleaned up before the response object is returned so you could also implement this inside the "response" callback.
Related
I have a server-side program in python that is expecting an image and is working fine when tested with a client-side program in python.
I want to send image to this server using flutter and I'm failing to do so..
Here's my server-side code
import socket #server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET = IP, SOCK_STREAM = TCP
server.bind(('localhost', 1112)) # 127.0.0.1
server.listen()
client_socket, client_address = server.accept()
file = open('2.jpg', "wb")
image_chunk = client_socket.recv(1024) # stream-based protocol
while image_chunk:
file.write(image_chunk)
image_chunk = client_socket.recv(1024)
file.close()
client_socket.close()
I have tried using dio, http and MultiPart
Here are snippets from my failed attempts:
MultiPart
var uri = Uri.parse('https://10.0.2.2:1112');
var request = MultipartRequest('POST', uri)
..files.add(await MultipartFile.fromPath(
'picture', filePath,
contentType: MediaType('application', 'jpeg')));
var response = await request.send();
if (response.statusCode == 200) print('Uploaded!');
Dio
Dio dio = new Dio();
FormData formData = new FormData.fromMap({
"file": await MultipartFile.fromPath(filePath, filename: basename(filePath),
contentType: MediaType('application', 'jpeg'),)
});
await dio.post('https://10.0.2.2:1112', data: formData);
I'm able to create a connection but I'm not able to send the file.
P.S: I have almost no experience of working with sockets, so I'm stuck at this.
The problem is that you are trying to connect to a socket api (not websocket these are different) via HTTP request and on server-side expecting to get image bytes but that's not gonna happen because as you know HTTP has it's own specification RFC2616. so what you will get there is some http headers and body.
Actually you can send http request to a socket but on the server-side you must do the heavy lifting. i mean reading http header line by line and then reading the Transfer-Encoding and Content-Length headers to know how to read the remaining bytes of the request, and then parsing the body data.
The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.
The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.
The solution is either:
using dart socket library at the client side and then sending your image through socket instead of http request (this link might be helpful )
Or creating a RESTFUL API and send your image via http request like you did before.
hope i could help you:)
Since you are dealing with websocket, you have to checkout this package web_socket_channel.
You first need to make connection with your socket channel using
var channel = IOWebSocketChannel.connect(Uri.parse('ws://localhost:1234'));
To listen to changes from your websocket channel, you will use:
channel.stream.listen((message) {
print("NEW MESSAGE");
});
To send data to your websocket channel, you will use:
channel.sink.add("your data");
Finally, do not forget to close your websocket channel stream using:
channel.sink.close();
I`m trying to write very simple http server in python. Working version is like this:
def run(self, host='localhost',port=8000):
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(1)
while True:
connection, adress = s.accept()
with connection:
data = b''
while True:
recived = connection.recv(1024)
data += recived
if len(recived) < 1024:
break
if data != b'':
handle_request(data,connection)
It works , but i have some misunderstanding whats going on.
As i understand, socket "s" accept connection from the client -> and return new socket object "connection" from which i can read what client sends to me and send response. I read data from connection until client send empty line b''. After this point TCP part ends and I pass recived bytes to handler which parse recived data as HTTP.
Qestions: At this point i read all the data which client send to me, but if i want to limit max size of HTTP request, should i just do something like this:
..................................
with connection:
data = b''
request_size_limit=1024*100 # some desired http request max size
while True:
recived = connection.recv(1024)
data += recived
if len(recived) < 1024 or len(data) > request_size_limit:
break
if data != b'':
handle_request(data,connection)
If i do something like this how can I inform client, that for example i have at most 1024*1024 free bytes of RAM and I can`t handle requests larger than this?
If clients want to send more that this limit, he must send several separated requests which will contain 1 part of necessary data?
Or for example for big POST request i must parse each recv(1024) while i found \r\n\r\n sequence , check content length and recv() content length by parts 1024b into some file and proceed after?
A1) If you can't handle the request because it is too large consider just closing the connection. Alternatively you can read (and discard) everything they send and then respond with a 413 Request Took Large.
A2) You'll need to work out a protocol for sending just parts of a request at a time. HTTP doesn't do this natively.
A3) If you can read the whole request in chunks and save it to a file, then it sounds like you have a solution to the 1024*1024 RAM limit, doesn't it?
But fix the issues with reading chunked data off the socket.
I'm trying to understand how send and receive are working.
I was trying to send continuously data to a server and i noticed that the server would receive mixed bytes because i was sending to much data at a time. See my code:
Server:
import socket, struct
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("",1996))
server.listen(0)
c,d = server.accept()
while True:
data = c.recv(1024)
print( struct.unpack("i", data)[0] )
Client:
import socket, struct
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect(("192.168.1.4",1996))
while True:
data = 1
server.send( struct.pack("i", data) )
Then i change the while loops to this:
Server:
data = c.recv(1024)
print( struct.unpack("i", data)[0] )
c.send( str.encode("Server received your message. You now can continue
sending more data") )
Client:
data = 1
server.send( struct.pack("i", data) )
#Wait to secure the send.
server.recv(1024)
This is working. I'm making sure that the client won't send data before the
server already receive the previous send.
But what if i want to do the same for the server too? How can i make sure that the server will send bytes to the client in a safe way?
I already tried this and i notice that i created an infinity loop because(I used multi-threading in order to send and receive at the same time on the server):
client was sending some data and then waiting to get a signal from the server
that he can send again.
the server was getting some data then sending the signal and after that waiting for a signal from the user that he can send again.
But because the client was actually sending data again, the whole thing was going on again and this caused me an infinity talk-reply loop.
So what can i do to make a continuously conversation between two sockets without mixing the bytes together?
Your problem is caused by Nagle algorithm which works by combining a number of small outgoing messages, and sending them all at once as TCP is a stream protocol. You can enable TCP_NODELAY socket option by calling sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) to sent data as soon as possible, even if there is only a small amount of data. And on the receiver side, it isn't going to get one packet at a time either, you must implement message boundaries itself if you want "continuous conversation between two sockets without mixing the bytes together".
I have a device that sends commands to a webserver. I've redirected those commands to my own server, with the goal of use the device to run another part of my system. In short, the device sends commands, I intercept them and use them.
The commands are sent to my server,but are not valid HTTP requests. I'm trying to use flask to read them with python, because I'd like to have these commands go straight into another web app.
Note that I can't change how the commands are sent.
Using sockets, I can read the data. For instance, here is a version of the data sent via socket (data is meaningless, just for illustration):
b'#123456#A'
In constrast, as HTTP message looks like this:
b'POST / HTTP/1.1\r\nHost: 123.123.123.123:12345\r\nRequest info here'
I know how to filter these (they always start with a #). Can I hook flask to let me handle these request differently, before they are parsed as HTTP requests?
Update: The code I used to read the requests, in case it provides context:
import socket
host = ''
port = 5000
backlog = 5
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(backlog)
while 1:
client, address = s.accept()
data = client.recv(size)
print("Request:")
print(data)
print("\n\n")
if data:
client.send(data)
client.close()
Following is the code which listens on a port for HTTP requests and sends the request packet to the server running on port 80, gets the response and sends the data back to the client. Now, everything is executing fine but the following line of code :
data = req_soc.recv(1024)
is taking too much time to execute and I have observed that, it takes long time to execute when it is going to/has received the last packet. I have also tried the same code using select.select() but the results are the same. Since I want to handle the data (raw) that is coming from the client and the actual HTTP server, I have no other choice than using sockets.
import socket
import thread
def handle_client(client):
data = client.recv(512)
request = ''
request += data
print data
print '-'*20
spl = data.split("\r\n")
print spl[0]
print spl[1]
if len(request):
req_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
req_soc.connect(('localhost', 80))
req_soc.send(request)
response = ''
data = req_soc.recv(1024)
while data:
response += data
print 1
data = req_soc.recv(1024)
req_soc.close()
print response
if len(response):
client.send(response)
client.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 4422))
server.listen(5)
print("Server is running...\n")
MSGLEN = 1024
while 1:
client, address = server.accept()
thread.start_new_thread(handle_client, (client, ))
Clients can do multiple commands (eg: GET) within one connection. You cannot wait for the client to send all the commands because based on what you return it could request more (eg: images of a web page). You have to parse the parts (commands) of request, find the boundary, forward that request to the server and write back the answer to the client. All this in a way that doesn't block on reading the client.
I'm not sure what's the best way to do this in python, but if you spend 5 minutes of googling you'll find a perfect HTTP proxy library.