Sending image to server using socket programming in flutter - python

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();

Related

Android - Client closes connection an image is sent, before receiving a string back

I have an android app that opens a socket connection with the server and sends an image to a python server. The server receives that image and is supposed to send a string back to confirm the image has been received. However the socket closes down after I end the Output stream, therefore the server receives the image but the client can't receive a string from the server because the client closed the connection.
Therefore, what I want to do is return a string/text confirming the image has arrived to the user client before the socket closes.
This is my Python server that receives the image as bytes decodes and saves it to a directory then sends a message back:
from socket import *
import datetime
import cv2
import PIL.Image as Image
from PIL import ImageFile, Image
import io
import base64
import numpy as np
import pickle
import uuid
date_string = datetime.datetime.now().strftime("%Y-%m-%d-%H:%M")
port = 9999
s = socket(AF_INET, SOCK_STREAM)
s.bind(('', port))
s.listen(1)
while True:
conn, addr = s.accept()
img_dir = '/home/Desktop/frames_saved/'
img_format = '.png'
try:
print("Connected by the ",addr)
#date_string = datetime.datetime.now().strftime("%Y-%m-%d-%H:%M")
filename = str(uuid.uuid4())
with open(img_dir+filename+img_format, 'wb') as file:
while True:
data = conn.recv(1024*8)
if data:
print(data)
try:
file.write(data)
except:
s = socket(AF_INET, SOCK_STREAM)
s.bind(('', port))
s.listen(1)
conn.sendall(("Hello World"))
else:
print("no data")
break
finally:
conn.close()
What I am trying to do is receive the encoded string in my android client and print/show a toast.
Android client code:
public class SendImageClient extends AsyncTask<byte[], Void, Void> {
#Override
protected Void doInBackground(byte[]... voids) {
isSocketOpen = true;
try {
Socket socket = new Socket("192.168.0.14",9999);
OutputStream out=socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while (isSocketOpen){
Log.d("IMAGETRACK", "Wrote to the socket[1]");
dataOutputStream.write(voids[0],0,voids[0].length);
while ((line = input.readLine()) != null)
Log.d("IMAGETRACK3", "Wrote to the socket[3]");
response.append(line);
Message clientmessage = Message.obtain();
clientmessage.obj = response.toString();
Log.d("[MESSAGE]", String.valueOf(clientmessage));
// Tries to receive a message from the server
out.close();
input.close();
if(isSocketOpen == false){
Log.d("CLOSED", "CLOSED CONNECTION");
socket.close();
break;
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Additionally I noticed that doing out.close(); will close the stream socket aswell. Here's the log after an image is sent and received by the server:
D/IMAGETRACK: Wrote to the socket[1]
D/IMAGETRACK: Wrote to the socket[1]
W/System.err: java.net.SocketException: Socket closed
W/System.err: at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:124)
at java.net.SocketOutputStream.write(SocketOutputStream.java:161)
at java.io.DataOutputStream.write(DataOutputStream.java:107)
at MyApp.MainActivity$SendImageClient.doInBackground(MainActivity.java:2792)
at MyApp.MainActivity$SendImageClient.doInBackground(MainActivity.java:2780)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
If I try moving out.close(); after the while loop, the client will send an infinite amount of data to the server and when i close the server to end the infinite data being receive, instead of a 70Kb image, i will have a 10MB image or more depending on how log I received the bytes.
From the looks of it, I think I would need to somehow stop the image being sent without closing the server to listen to the image being sent back. How can I do that without closing the socket?
The reason that socket is closed is simple. I have sample program like this before. You have 2 options to do it. Your architecture for sending and receiving a file is not complete and correct.
One option is that you can define a protocol of sending and receiving by your own, which is not standard. For example, you can define special characters such as ##endoffile##, and send it after your image, then do not close the socket. Server understands that image is received and there is nothing more. Then in client side call receive method until the end of the socket, but before, you have to send data string back to confirm the image has been received, and client closes the socket when receives this string. Remember to add socket timeout wisely for the prevention of infinite socket waiting.
The second solution is that you can use standard protocols such as HTTP or FTP, but have to read standards of these protocols such as header and body values, and also send and receive files as multipart.

HTTP server on pure sockets in python

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.

How to send raw data through python requests connection

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.

How to send UDP message from UDP server without sending to itself?

I'd like to make a UDP server that sends a message to a UDP client soon after the server gets the message from the client. I'm using Python and Google Protobuffer as a message protocol.
Currently, the message receiving part seems working, but regarding the message sending part, it has an issue: the response message from the server doesn't arrive the client and even worse, the server shows that message (maybe it sends to itself? Right now, the console shows both the message from client and the message that should be sent to client). This issue didn't happen when I tried the similar code on C++ or C#.
The followings are some excerpt from my code:
def connect(self):
remote = ('x.x.x.x',xxxx) #ip and port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.settimeout(2.5)
self.sock.bind(remote)
self.sock.settimeout(None)
def start(self):
while not self.exit_thread:
# Get the message from client
data, address = self.sock.recvfrom(8192)
if data is not None:
# De-serialize inbound message from client
msg_client = xxx_pb2.msgClient()
msg_client.ParseFromString(data)
# Display message from client
self.display_inbound_message(msg_client)
# Create a new message from server
msg_serer = xxx_pb2.msgServer()
self.create_outbound_message(msg_serer)
# Send the Udp message to the client, return the number of bytes sent
bytes_sent = self.sock.sendto(msg_server.SerializeToString(), self.remote)
if (bytes_sent < 0):
print("Error send message")
I don't have enough experience for UDP programming on Python. Please let me know if you notice anything.
The issue with this code is that the server replies to itself, rather than the remote client. In here:
data, address = self.sock.recvfrom(8192)
# ...
bytes_sent = self.sock.sendto(msg_server.SerializeToString(), self.remote)
It should be:
bytes_sent = self.sock.sendto(msg_server.SerializeToString(), address)
It makes good sense to rename remote to server_address because it is this server's address.

Flask - how to read the raw input from non-HTTP request?

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()

Categories