简体   繁体   中英

Sending an Image from Client to Server using Sockets in Python

I would like to send a captured image from my mobile app via socket to a desktop server. I currently implemented a server/client application. My problem is that currently the mobile app is the server that sends the frame after each capture. Then, there isn't time for the Desktop that is the client to connect to the server that is the mobile app and receive the image, because the socket is closed right after.

What I want to do is setup the desktop side as a server that receives an image, instead of sending and keep the socket open, so every time a frame is sent from the mobile app as a client the server will read and display it.

This is my current attempt which has the mobile app as a server that sends an image:

Java class:

private final OnClickListener mOnClickListener = new OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.save_button:
                    setValue(UVCCamera.CTRL_ZOOM_ABS, 0x80ff);
                    Toast.makeText(getApplicationContext(), "保存成功", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.button_camera:
                    if (!Check.isFastClick()) {
                        return;
                    }
                    if (mCameraHandler != null) {
                        if (mCameraHandler.isOpened()) {
                            if (checkPermissionWriteExternalStorage()) {
                                Python py = Python.getInstance();
                                Bitmap bitmap = Bitmap.createBitmap(mImageView .getWidth(), mImageView .getHeight(), Bitmap.Config.ARGB_8888);
                                imageString = getStringImage(bitmap);
                                PyObject pyo = py.getModule("script");
                                PyObject obj = pyo.callAttr("__main__", imageString);
                                mCameraHandler.captureStill(MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".png").toString());

                            }
                        }
                    }
                    break;
      }
}

Server in mobile app:

import numpy as np
import cv2
from PIL import Image
import base64
import io
import socket
import pickle
import struct
import imutils

def main(data):

    server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    host_name  = socket.gethostname()
    host_ip = socket.gethostbyname(host_name)
    port = 9999
    socket_address = (host_ip, port)
    server_socket.bind(socket_address)
    server_socket.listen(5)
    while True:
        client_socket, addr = server_socket.accept()
        if client_socket:
            decoded_data = base64.b64decode(data)
            frame_decoded = np.fromstring(decoded_data, np.uint8)
            frame = cv2.imdecode(frame_decoded , cv2.IMREAD_UNCHANGED)
            // The image captured is initially received as a string of bytes
            a = pickle.dumps(frame)
            message = struct.pack("Q",len(a))+a
            client_socket.sendall(message)

Client code that is currently in desktop:

import socket,cv2, pickle,struct

client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host_ip = '192.168.0.12' 
port = 9999
client_socket.connect((host_ip,port))
data = b""
payload_size = struct.calcsize("Q")
while True:
    while len(data) < payload_size:
        packet = client_socket.recv(4*1024) 
        if not packet: break
        data+=packet
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack("Q",packed_msg_size)[0]
    
    while len(data) < msg_size:
        data += client_socket.recv(4*1024)
    frame_data = data[:msg_size]
    data  = data[msg_size:]
    frame = pickle.loads(frame_data)
    cv2.imshow("Image Captured",frame)
    key = cv2.waitKey(1) & 0xFF
    if key  == ord('q'):
        break
client_socket.close()

 

Basically, I'd like to change who's sending and who's receiving from my current approach. I'd like to send the image from the mobile app as a client and receive in the desktop as a server, I tried searching around but most examples show how to send image from the server. However, I need to have to socket open on the server side, so for every capture the image is received immediately.

Consider an architecture like this. The server class creates a new RequestHandler every time there is a new connection from a client. Here, I just store the socket on a global list, then do a read. I don't expect any data, so the read will block, but if the connection drops, the read will fail and we can return. When there is new data, we just scan through the waiting sockets list and send the data to all of them.

import socketserver

open_connections = []

def newFrame(data):
    # If there are no open connections, there's nothing to do.
    if not open_connections:
        return

    decoded_data = base64.b64decode(data)
    frame_decoded = np.fromstring(decoded_data, np.uint8)
    frame = cv2.imdecode(frame_decoded , cv2.IMREAD_UNCHANGED)
    # The image captured is initially received as a string of bytes
    a = pickle.dumps(frame)
    message = struct.pack("Q",len(a))+a

    for sock in open_connections:
        sock.sendall(message)

class RequestHandler(socketserver.BaseRequestHandler):
    # Called for each new connection.
    def handle(self):
        open_connections.append( self.request )
        # We don't ever expect data, but if this returns then the 
        # connection has been lost.
        self.request.read(10)
        open_connections.remove( sock )

if __name__ == '__main__':
    print( "Listening on 9999..." )
    socketserver.TCPServer.allow_reuse_address = True
    tcpserver = socketserver.ThreadingTCPServer(('localhost', 9999), RequestHandler)
    tcpserver.serve_forever()

I managed to setup a working client code that sends an image to a server, while the server awaits for the image to be sent.

The client that sends an image:

import numpy as np
import cv2
from PIL import Image
import base64
import io
import socket
import pickle
import struct
import io

    def main(data):
        decoded_data = base64.b64decode(data)
        np_data = np.fromstring(decoded_data, np.uint8)
        img = cv2.imdecode(np_data, cv2.IMREAD_UNCHANGED)
    
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client_socket.connect(('192.168.0.14', 9999))
        connection = client_socket.makefile('wb')
    
    
        result, frame = cv2.imencode('.png', img)
    
        data = pickle.dumps(frame, 0)
    
        size = len(data)
        client_socket.sendall(struct.pack(">L", size) + data)

Server receives an image:

import socket
import sys
import cv2
import pickle
import numpy as np
import struct ## new

HOST='192.168.0.14'
PORT=9999

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print('Socket created')

s.bind((HOST,PORT))
print('Socket bind complete')
s.listen(10)
print('Socket now listening')

conn,addr=s.accept()

data = b""
payload_size = struct.calcsize(">L")
print("payload_size: {}".format(payload_size))
while True:
    while len(data) < payload_size:
        print("Recv: {}".format(len(data)))
        data += conn.recv(4096)

    print("Done Recv: {}".format(len(data)))
    packed_msg_size = data[:payload_size]
    data = data[payload_size:]
    msg_size = struct.unpack(">L", packed_msg_size)[0]
    print("msg_size: {}".format(msg_size))
    while len(data) < msg_size:
        data += conn.recv(4096)
    frame_data = data[:msg_size]
    data = data[msg_size:]

    frame=pickle.loads(frame_data, fix_imports=True, encoding="bytes")
    frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
    filename = '/home/server/Desktop/frames_savedsaved/Image.jpg'
    cv2.imwrite(filename, frame)
    cv2.imshow('ImageWindow',frame)
    cv2.waitKey(1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM