【问题标题】:Create a python server to send data to Android app using socket创建一个 python 服务器以使用套接字将数据发送到 Android 应用程序
【发布时间】:2017-08-24 00:28:39
【问题描述】:

我正在尝试使用 python(我的电脑)中的套接字创建一个简单的聊天服务器程序,以与我的 Android 客户端代码(我的 Android 手机)进行通信。

我有一个简单的服务器代码,可以接收消息,但是当我尝试从服务器向客户端发送消息时,它会阻止客户端应用程序并崩溃。

客户端代码基于本教程:Simple Android Chat Application, client side.

客户端代码:

    private class ChatClientThread extends Thread {

    String name;
    String dstAddress;
    int dstPort;
    
    String msgToSend = "";
    boolean goOut = false;

    ChatClientThread(String name, String address, int port) {
        this.name = name;
        dstAddress = address;
        dstPort = port;
    }

    @Override
    public void run() {
        Socket socket = null;
        DataOutputStream dataOutputStream = null;
        DataInputStream dataInputStream = null;

        try {
            socket = new Socket(dstAddress, dstPort);
            dataOutputStream = new DataOutputStream(
                    socket.getOutputStream());
            dataInputStream = new DataInputStream(socket.getInputStream());
            dataOutputStream.writeUTF(name);
            dataOutputStream.flush();

            while (!goOut) {
                if (dataInputStream.available() > 0) {
                    msgLog += dataInputStream.readUTF();

                    MainActivity.this.runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            chatMsg.setText(msgLog);
                        }
                    });
                }
                
                if(!msgToSend.equals("")){
                    dataOutputStream.writeUTF(msgToSend);
                    dataOutputStream.flush();
                    msgToSend = "";
                }
            }

        } catch (UnknownHostException e) {
            e.printStackTrace();
            final String eString = e.toString();
            MainActivity.this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, eString, Toast.LENGTH_LONG).show();
                }
                
            });
        } catch (IOException e) {
            e.printStackTrace();
            final String eString = e.toString();
            MainActivity.this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, eString, Toast.LENGTH_LONG).show();
                }
                
            });
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            if (dataOutputStream != null) {
                try {
                    dataOutputStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            if (dataInputStream != null) {
                try {
                    dataInputStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            MainActivity.this.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    loginPanel.setVisibility(View.VISIBLE);
                    chatPanel.setVisibility(View.GONE);
                }
                
            });
        }

    }
    
    private void sendMsg(String msg){
        msgToSend = msg;
    }
    
    private void disconnect(){
        goOut = true;
    }
}

服务器代码:

import socket

s = socket.socket()
host = "192.168.1.82"
port = 8080

s.bind((host, port))
print host
s.listen(5)
c = None

while True:
   if c is None:
       # Halts
       print '[Waiting for connection...]'
       c, addr = s.accept()
       print 'Got connection from', addr
   else:
       # Halts
       print '[Waiting for response...]'
       print c.recv(1024)

当添加以下两行发送消息时,它不起作用。

   # Halts
   print '[Waiting for response...]'
   print c.recv(1024)
  q = raw_input()
  c.send(q)

关于如何解决它的任何想法?

【问题讨论】:

  • “它崩溃了……”。如何?崩溃的堆栈跟踪是什么?它在哪条线崩溃?你有没有努力确保你从一侧写的东西在另一侧被合理地读入?例如,Java 的 DataInput/DataOutput readUTF()/writeUTF() 与普通 python socket.send() 和 socket.recv() 不兼容。
  • 当我添加以下两行时,服务器代码变得无响应: q = raw_input() c.send(q) 我必须断开连接并重新连接。
  • @JamesKPolk 我做了一个 log.d,实际上我得到了最多 5 个字符的字节。字节是数字,所以我需要解码接收到的数据。

标签: android python sockets chat


【解决方案1】:

Java 中的 DataOutput.writeUTF()DataInput.readUTF() 方法在 python 中没有任何直接等效项。作为DataOutput.writeUTF() 状态的Javadocs:

将两个字节的长度信息写入输出流,紧随其后 通过字符串中每个字符的修改后的 UTF-8 表示 s。如果 s 为 null,则抛出 NullPointerException。中的每个字符 字符串 s 被转换为一组一个、两个或三个字节, 取决于字符的值。

两个长度字节按大端顺序排列。因此,读取该信息的python程序至少必须首先读取这两个长度字节以确定后续数据的长度,然后读取那么多字节的特殊编码字符数据,最后对其进行解码。根据here 所使用的编码(称为“修改的 UTF-8”)的讨论,在 python 端对其进行解码似乎并非易事:

此格式与标准 UTF-8 格式的区别在于 以下:

  • 空字节“\u0000”以 2 字节格式而不是 1 字节编码, 这样编码的字符串就永远不会嵌入空值。
  • 仅使用 1 字节、2 字节和 3 字节格式。
  • 补充字符以代理对的形式表示。

作为一种我认为更容易的替代方法,在 Java 方面考虑放弃 readUTf()writeUTF() 方法并将它们替换为您自己的版本,如下所示:

public void writeUTF8(String s, DataOutput out) throws IOException {
    byte [] encoded = s.getBytes(StandardCharsets.UTF_8);
    out.writeInt(encoded.length);
    out.write(encoded);
}

public String readUTF8(DataInput in) throws IOException {
    int length = in.readInt();
    byte [] encoded = new byte[length];
    in.readFully(encoded);
    return new String(encoded, StandardCharsets.UTF_8);
}

然后,在 python 端,等效代码可能是:

def recvall(sock, size):
    received_chunks = []
    buf_size = 4096
    remaining = size
    while remaining > 0:
        received = sock.recv(min(remaining, buf_size))
        if not received:
            raise Exception('unexcepted EOF')
        received_chunks.append(received)
        remaining -= len(received)
    return b''.join(received_chunks)


def read_utf8(sock):
    len_bytes = recvall(sock, 4)
    length = struct.unpack('>i', len_bytes)[0]
    encoded = recvall(sock, length)
    return str(encoded, encoding='utf-8')


def write_utf8(s: str, sock: socket.socket):
    encoded = s.encode(encoding='utf-8')
    sock.sendall(struct.pack('>i', len(encoded)))
    sock.sendall(encoded)

【讨论】:

    猜你喜欢
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    • 2018-11-23
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2020-06-01
    • 2016-12-15
    相关资源
    最近更新 更多