Android客户端与Python服务端通过SOCKET进行通信和图片传输

由于网上关于这两个语言间的代码较少而且混乱不好看懂,我就把自己这次项目的过程和问题在这里记录下来,希望能帮到大家吧。

首先,在进行编码之前我们应该找好服务器的IP地址和可用的端口号。

以我自己的电脑为例,win10系统,打开CMD窗口,输入ipconfig就可以查看电脑本机所使用的IP地址

选择手机与电脑同在的一个局域网的IP作为socket绑定的IP地址。端口号一般选择1024-65535之间的一个空闲号码就好。

这里需要注意的有两点:

1.电脑防火墙:

Win 10 系统默认打开防火墙阻止一切外部应用与本机进行数据传输,当然我们的应用也包含在内。可以选择添加信任的IP地址和端口号进行通信,不过我为了省事直接关掉了防火墙。

2.IP地址选择:

有时候电脑可能同时存在多个局域网IP地址,如何判断哪个是手机所在的IP地址也是一个问题。这里我是用手机安装一款名为Ping的手机应用

输入电脑的IP地址点击Start便可以查看网络是否通畅。如果这部分成功那么通信问题也就解决了一半。

我在之前尝试的过程中使用电脑打开Wifi共享工具或者手机和电脑连接一起连接学校的wifi但是都没有办法Ping通,一直误导了我很久,后来选择使用手机给电脑开热点总算是把问题解决了。不过原因目前还没有找到,有知道原因或者知道其他解决方法的大佬们可以劳烦在评论中分享下吗?

现在来介绍代码部分:

服务端:



print("正在创建SOCKET...")
sk = socket.socket()
print("创建成功,绑定端口...")
# 获取本地主机名
host ="你选择的IP地址"
prot = 12321
sk.bind((host,prot))
print("绑定成功,正在监听....")
sk.listen(5)
print(sk)
while True:
    print("wating for connect...")
    conn, address = sk.accept()  # 新创建的套接字
    now = time.time()
    print("conn:" ,conn,"address:",address)
    size = conn.recv(10)  # 以10为单位进行接收图片大小信息
    size_str = str(size, encoding="utf-8")  #将Byte流转string
    size_str = size_str.strip()    
    file_size = int(size_str)      #获取图片文件大小
    print(size_str)
    if file_size != 0:
        print("接收到数据", file_size, "int")
        picpath = "img/"+str(now) + ".png"
        f = open(picpath, "wb")#打开文件准备写入
    has_size = 0     #已接收数据大小
    while True:
        if file_size == has_size:#如果接收的数据足够
            break
        res_data = file_size - has_size
        if res_data >= 1024:
            data = conn.recv(1024)
        else:
            data = conn.recv(res_data)
        f.write(data)#写入文件
        data_size = len(data)
        has_size = has_size + data_size
        print("接收",has_size)
    f.close()#关闭文件
    print("成功接收")
    print("pic in path:",picpath)

    print("begin to send back")
    size = os.stat(picpath).st_size
    print("pic size:",size)
    ss = str(size)
    while(len(ss)!=10):
        ss = ss + " "
    conn.sendall(bytes(ss,encoding='utf-8'))
    print("send pic size")
    print("wait for signal...")
    # #返回图片测试*****************************
    sum = 0
    with open(picpath, "rb") as f:
        for line in f:
            sum = len(line) + sum
            if(sum >1024):
                time.sleep(0.1)#等待客户端接收数据
                sum = sum - 1024
            print(sum)
            conn.sendall(line)
    # #***********************
    # 等待客户端接收数据
    time.sleep(1)
    conn.close()
sk.close()

代码的具体使用方法可以参考注释,函数的意思可以去网上找,这里不详细说了。我挑几个问题记录下

1:图片文件大小问题:

可能有的朋友不清楚为什么需要首先传送文件大小信息。这里是因为socket接收数据信息函数每次接收的数据量无法确定。由于网络的原因可能会有一定时间接收不到信息。这时候接收的数据一直为0。这就导致无法确定文件传输是否完成,所以需要提前接收文件大小,用于判断文件传输是否完成。

2:客户端等待时间问题:

因为手机端接收文件可能因为网络或者设备的原因,无法将发送的数据及时接收,导致丢包。出现客户端接收到的实际数据少于实际发送的数据量

客户端代码:

new Thread() {
                public void run() {
                    try {
//                        Toast.makeText(MainActivity.this, "开始上传", Toast.LENGTH_SHORT).show();
                        socket = new Socket(HOST, SPORT);
                        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                        FileInputStream fis = new FileInputStream(imgPaht);
                        //发送图片大小
                        int size = fis.available();
                        String s = String.valueOf(size);
                        while(s.length()<10){
                            s = s + " ";
                        }
                        byte[] bytes = s.getBytes();
                        out.write(bytes);
                        out.flush();
                        //发送图片
                        //读取图片到ByteArrayOutputStream
                        byte[] sendBytes = new byte[1024];
                        int length = 0;
                        while ((length = fis.read(sendBytes, 0, sendBytes.length)) > 0) {
                            out.write(sendBytes, 0, length);
                            out.flush();
                        }
                        fis.close();
                        String simg = imgPaht;
                        simg = simg.split("\\.")[0] + "D." + simg.split("\\.")[1];
                        //准备读入
                        DataInputStream dataInput = new DataInputStream(socket.getInputStream());

                        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        char[] num = new char[10];
                        int message;
                        message = br.read(num,0,10);
                        s = String.valueOf(num).trim();
                        size = Integer.parseInt(s);
                        byte[] data = new byte[size];
                        int len = 0;
//                        DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream(simg));
                        while (len < size) {
                            if(size - len <=1024){
                                message = dataInput.read(data, len, size - len);
                            }else{
                                message = dataInput.read(data, len, 1024);
                            }
                            len += message;
                            if(message == -1)
                            {
                                break;
                            }
//                            dataOutput.write(data);
//                            dataOutput.flush();
                        }
                        ByteArrayOutputStream outPut = new ByteArrayOutputStream();
                        bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
                        bmp.compress(Bitmap.CompressFormat.PNG, 100, outPut);
                        out.close();
                        dataInput.close();
//                        dataOutput.close();
                        socket.close();
                        mhandler.sendEmptyMessage(0);
//                            break;
//                        }
                    } catch (UnknownHostException e) {
//                        Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();

                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }

            }.start();
    //绑定控件
    public void init() {
        ivPic = findViewById(R.id.iv_bpicture);
        ivCPic = findViewById(R.id.iv_cpicture);
        btSelectPic = findViewById(R.id.bt_selectpic);
        btSendPic = findViewById(R.id.bt_sendpic);
        btSelectPic.setOnClickListener(MyListener);
        btSendPic.setOnClickListener(MyListener);
        mhandler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                super.handleMessage(msg);
                switch (msg.what){
                    case 0:
                        ivCPic.setImageBitmap(bmp);
                        break;
                }
            }
        };
    }

Android拒绝在主页面使用耗时工作,socket网络连接操作就是其中一种。所以需要新建线程运行网络传输工作。并且使用Handler来进行不同线程之间的消息传输工作。

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是一个使用Python socket进行图片传输的简单示例,包括客户端服务端的代码: 服务端代码(server.py): ```python import socket import cv2 import numpy as np # 创建一个TCP/IP套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 8888)) server_socket.listen(1) print("等待客户端连接...") client_socket, client_address = server_socket.accept() print("客户端已连接:", client_address) # 接收图片数据 data = b"" while True: packet = client_socket.recv(4096) if not packet: break data += packet # 将接收到的数据转换为图像 image_array = np.frombuffer(data, dtype=np.uint8) image = cv2.imdecode(image_array, cv2.IMREAD_COLOR) # 显示图像 cv2.imshow("Received Image", image) cv2.waitKey(0) # 关闭套接字和窗口 client_socket.close() server_socket.close() cv2.destroyAllWindows() ``` 客户端代码(client.py): ```python import socket import cv2 # 连接服务端 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('localhost', 8888)) # 读取图像文件 image = cv2.imread('image.jpg') # 将图像数据转换为字符串 _, image_data = cv2.imencode('.jpg', image) image_data = image_data.tobytes() # 发送图像数据 client_socket.sendall(image_data) # 关闭套接字 client_socket.close() ``` 在这个示例中,服务端监听本地8888端口,客户端连接到服务端并发送图片数据。服务端接收数据并将其转换为图像,然后显示出来。请注意,客户端需要将要发送的图片命名为"image.jpg"并与客户端代码处于同一目录下。 希望这个示例对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值