问题
I am trying to create a simple chat server program using socket in python ( my pc ) to communicate with my Android client code ( my Android phone ) .
I have a simple server code which receives messages but it blocks the client app and crashes when I try to send messages from server to client.
The client code is based on this tutorial: Simple Android Chat Application, client side.
Client code:
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;
}
}
Server code:
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)
When add following two lines to send messages then it doesn't work.
# Halts
print '[Waiting for response...]'
print c.recv(1024)
q = raw_input() c.send(q)
Any ideas on how to fix it?
回答1:
The DataOutput.writeUTF() and DataInput.readUTF() methods in Java do not have any direct equivalents in python. As the Javadocs for DataOutput.writeUTF() state:
Writes two bytes of length information to the output stream, followed by the modified UTF-8 representation of every character in the string s. If s is null, a NullPointerException is thrown. Each character in the string s is converted to a group of one, two, or three bytes, depending on the value of the character.
The two length bytes are in big-endian order. Thus, at a minimum, a python program reading in this information must first read in those two length bytes to determine the length of the subsequent data, then read in that many bytes of specially-encoded character data, and finally decode it. Decoding it on the python side appears to be non-trivial based on the discussion here on the encoding used, called 'modified UTF-8':
The differences between this format and the standard UTF-8 format are the following:
- The null byte '\u0000' is encoded in 2-byte format rather than 1-byte, so that the encoded strings never have embedded nulls.
- Only the 1-byte, 2-byte, and 3-byte formats are used.
- Supplementary characters are represented in the form of surrogate pairs.
As an alternative that I think would be much easier, on the Java side consider abandoning the readUTf()
and writeUTF()
methods and replacing them with your own versions like the following:
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);
}
And then, on the python side, the equivalent code could be:
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)
来源:https://stackoverflow.com/questions/45851162/create-a-python-server-to-send-data-to-android-app-using-socket