【问题标题】:Why are my client threads/listeners not receiving broadcasted messages from the server?为什么我的客户端线程/侦听器没有收到来自服务器的广播消息?
【发布时间】:2014-01-06 04:08:19
【问题描述】:

我在 Android 上制作了一个简单的客户端-服务器应用程序原型

我设法将两个客户端连接到服务器,服务器可以接收他们的消息。现在的问题是我似乎无法向其他客户端广播/接收消息。

我尝试通过 Server 类中的 for 循环广播接收到的消息:

private void broadcastMessage(String message) {

        for (int i = 0, j = clients.size(); i <= j; i++) {
            PrintWriter out = null;
            Socket socket = clients.get(i);
            try {
                out = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())), true);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // WHERE YOU ISSUE THE COMMANDS
            out.println(message);
            Log.d("SERVER Loop", "Broadcasting messages...");
            out.close();
        }
        Log.d("SERVER", "Message Brodcasted");
    }

然后我尝试通过客户端类中的侦听器接收:

    public class ClientThreadListener implements Runnable {

    protected Socket serverSocket = null;
    protected String mMsgFromServer;

    public ClientThreadListener(Socket serverSocket) {
        this.serverSocket = serverSocket;
    }

    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    serverSocket.getInputStream()));

            while ((mMsgFromServer = in.readLine()) != null) {
                Log.d("MESSAGE FROM SERVER: ", mMsgFromServer);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        msgFromOtherClients.append('\n'
                                + "Message From Server: " + mMsgFromServer);
                    }
                });
            }
        } catch (Exception e) {
            Log.e("ClientListener", "C: Error", e);
            connected = false;
        }
    }
}

我没有收到任何错误或强制关闭。原谅我,我知道这很混乱,但请耐心等待,请专注于手头的问题:D

这是 Server 类的完整代码

public class Server extends Activity {

private TextView serverStatus;

// DEFAULT IP
public static String SERVERIP = "10.0.2.15";

// DESIGNATE A PORT
public static final int SERVERPORT = 8080;

private Handler handler = new Handler();

private ServerSocket serverSocket;

private String mMsgFromClient;

private MultiThreadedServer server;

private ArrayList<Socket> clients = new ArrayList<Socket>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.server);
    serverStatus = (TextView) findViewById(R.id.server_status);

    // SERVERIP = getLocalIpAddress();

    server = new MultiThreadedServer(8080);
    new Thread(server).start();

}

public class MultiThreadedServer implements Runnable {

    protected int serverPort = 8080;
    protected ServerSocket serverSocket = null;
    protected boolean isStopped = false;
    protected Thread runningThread = null;

    public MultiThreadedServer(int port) {
        this.serverPort = port;
    }

    public void run() {
        synchronized (this) {
            this.runningThread = Thread.currentThread();
        }
        openServerSocket();
        while (!isStopped()) {
            Socket clientSocket = null;
            try {
                clientSocket = this.serverSocket.accept();
                clients.add(clientSocket);
            } catch (IOException e) {
                if (isStopped()) {
                    Log.d("SERVER TEXT", "Server Stopped.");
                    return;
                }
                throw new RuntimeException(
                        "Error accepting client connection", e);
            }
            new Thread(new WorkerRunnable(clientSocket, this)).start();

        }
        Log.d("SERVER TEXT", "Server Stopped.");
    }

    private synchronized boolean isStopped() {
        return this.isStopped;
    }

    public synchronized void stop() {
        this.isStopped = true;
        try {
            this.serverSocket.close();
        } catch (IOException e) {
            throw new RuntimeException("Error closing server", e);
        }
    }

    private void openServerSocket() {
        try {
            this.serverSocket = new ServerSocket(this.serverPort);
        } catch (IOException e) {
            throw new RuntimeException("Cannot open port 8080", e);
        }
    }

    private void broadcastMessage(String message) {

        for (int i = 0, j = clients.size(); i <= j; i++) {
            PrintWriter out = null;
            Socket socket = clients.get(i);
            try {
                out = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())), true);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // WHERE YOU ISSUE THE COMMANDS
            out.println(message);
            Log.d("SERVER Loop", "Broadcasting messages...");
            out.close();
        }
        Log.d("SERVER", "Message Brodcasted");
    }

}

public class WorkerRunnable implements Runnable {

    protected Socket clientSocket = null;
    protected String mMsgFromClient = null;

    private UUID id;

    public WorkerRunnable(Socket clientSocket, MultiThreadedServer server) {
        this.clientSocket = clientSocket;
        id = UUID.randomUUID();
    }

    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    clientSocket.getInputStream()));

            while ((mMsgFromClient = in.readLine()) != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        serverStatus.append('\n'
                                + "Message From Client ID " + getID()
                                + ": " + mMsgFromClient);
                    }
                });
            }
            Log.d("SERVERTEXT", "Proceed to broadcast");
            server.broadcastMessage(mMsgFromClient);

        } catch (IOException e) {
            Handler handler = new Handler();
            handler.post(new Runnable() {
                @Override
                public void run() {
                    serverStatus
                            .append('\n'
                                    + "Message From Client ID "
                                    + getID()
                                    + ": "
                                    + "Oops. Connection interrupted. Please reconnect your phones.");
                }
            });
            e.printStackTrace();
        }

    }

    private String getID() {
        return id.toString();
    }
}
}

这是客户端类的完整代码

public class Client extends Activity {

private EditText serverIp;
private EditText chatMsg;
private Button connectPhones;
private Button sendMsg;
private TextView msgFromOtherClients;

private String serverIpAddress = "";

private boolean connected = false;
private boolean willSendMsg = false;

private Handler handler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.client);

    serverIp = (EditText) findViewById(R.id.server_ip);
    connectPhones = (Button) findViewById(R.id.connect_phones);
    connectPhones.setOnClickListener(connectListener);

    chatMsg = (EditText) findViewById(R.id.chat_msg);
    sendMsg = (Button) findViewById(R.id.send_msg);
    sendMsg.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            willSendMsg = true;
        }
    });

    msgFromOtherClients = (TextView) findViewById(R.id.msg_from_other_clients);
}

private OnClickListener connectListener = new OnClickListener() {

    @Override
    public void onClick(View v) {
        if (!connected) {
            serverIpAddress = serverIp.getText().toString();
            if (!serverIpAddress.equals("")) {
                Thread cThread = new Thread(new ClientThread());
                cThread.start();
            }
        }
    }
};

public class ClientThread implements Runnable {

    public void run() {
        try {
            InetAddress serverAddr = InetAddress.getByName(serverIpAddress);

            Log.d("ClientActivity", "C: Connecting...");

            Socket socket = new Socket(serverAddr, Server.SERVERPORT);
            connected = true;

            Thread listener = new Thread(new ClientThreadListener(new Socket(serverAddr, Server.SERVERPORT)));
            listener.start();

            while (connected) {
                if (willSendMsg) {
                    willSendMsg = false;
                    try {
                        Log.d("ClientActivity", "C: Sending command.");
                        PrintWriter out = new PrintWriter(
                                new BufferedWriter(new OutputStreamWriter(
                                        socket.getOutputStream())), true);
                        // WHERE YOU ISSUE THE COMMANDS
                        out.println(chatMsg.getText().toString());
                        Log.d("ClientActivity", "C: Sent.");
                    } catch (Exception e) {
                        Log.e("ClientActivity", "S: Error", e);
                    }
                }
            }
            socket.close();
            Log.d("ClientActivity", "C: Closed.");
        } catch (Exception e) {
            Log.e("ClientActivity", "C: Error", e);
            connected = false;
        }
    }
}

public class ClientThreadListener implements Runnable {

    protected Socket serverSocket = null;
    protected String mMsgFromServer;

    public ClientThreadListener(Socket serverSocket) {
        this.serverSocket = serverSocket;
    }

    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    serverSocket.getInputStream()));

            while ((mMsgFromServer = in.readLine()) != null) {
                Log.d("MESSAGE FROM SERVER: ", mMsgFromServer);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        msgFromOtherClients.append('\n'
                                + "Message From Server: " + mMsgFromServer);
                    }
                });
            }
        } catch (Exception e) {
            Log.e("ClientListener", "C: Error", e);
            connected = false;
        }
    }
}
}

【问题讨论】:

  • 在您的服务器代码中,写入套接字后不要关闭输出流。关闭输出流也会关闭套接字。
  • 我删除了它,它仍然不接受或发送消息。你认为也许我也不应该关闭客户端代码上的套接字吗?
  • 在您的 ClientThread 类中,您创建了两个套接字。您应该将第一个套接字对象传递给 ClientThreadListener 而不是创建一个新对象。
  • 在每条消息(在服务器和客户端)之后调用out.flush(),以确保数据通过套接字连接发送而不是缓冲。
  • 您的应用在 logcat 中产生什么输出?以及如何启动您的活动?

标签: java android client-server port


【解决方案1】:

您的代码存在一些问题,使其无法正常工作。

  • 正如在其他答案中已经说过的,在您的代码中,您将在将消息发送到客户端后立即关闭套接字输出流。仅在您的 for 消息循环中调用 close()。当然,在客户端关闭套接字与在服务器上关闭它的效果相同。只有在客户端和服务器完成通话后,您才必须关闭套接字。在传输数据时关闭它就像在通话中挂断电话一样。

  • 其次,在客户端创建一个新的套接字:

    Log.d("ClientActivity", "C: Connecting...");
    Socket socket = new Socket(serverAddr, Server.SERVERPORT);
    

    但随后您将另一个新创建的套接字传递给侦听器(我想这不是故意的):

    connected = true;
    Thread listener = new Thread(new ClientThreadListener(new Socket(serverAddr, Server.SERVERPORT)));
    listener.start();
    
  • 第三,始终在发送数据后立即在输出流上调用flush(),否则数据可能不会发送(发送方法只会将您的数据排入发送缓冲区)。

  • 最后(这可能对您没有用,因为我不知道您的最终目标),如果您需要在套接字上发送和接收,90% 的时间最好异步执行此操作,使用单独的线程进行监听和发送。

如果仍然不起作用,请在此处添加一些来自 logcat 的输出或日​​志跟踪。

【讨论】:

    【解决方案2】:

    你需要移动线:

    server.broadcastMessage(mMsgFromClient);
    

    while里面:

            while ((mMsgFromClient = in.readLine()) != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        serverStatus.append('\n'
                                + "Message From Client ID " + getID()
                                + ": " + mMsgFromClient);
                    }
                });
                // HERE
                Log.d("SERVERTEXT", "Proceed to broadcast");
                server.broadcastMessage(mMsgFromClient);
            }
    

    否则,你只会广播null

    编辑:您应该确保在发布新的Runnable 和它实际执行之间没有更改mMsgFromClient。最好的方法是用当前值初始化匿名类中的一个字段,然后记录该字段的值。

    EDIT2:除非您的服务器应该在向客户端发送广播消息后关闭其与客户端的连接,否则您应该在broadcastMessage 方法中使用out.flush() 而不是out.close()。最好在超时后关闭客户端连接,或者只是让客户端断开连接,再次超时。

    否则,您的测试在大多数情况下都会非常有限:客户端连接并发送消息;然后它收到自己的消息,服务器关闭连接。

    【讨论】:

      【解决方案3】:

      请尝试在 android 中使用 AsyncTask,这将创建单独的线程用于与服务器通信。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-02
        • 2021-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-24
        • 2018-10-10
        相关资源
        最近更新 更多