一次从多个套接字接收数据(多线程)

时间:2016-08-12 11:50:34

标签: java multithreading sockets tcp network-programming

我是网络编程新手,我一直在寻找解决问题的方法,但找不到。我想要的是拥有一台可以同时从多个套接字接收文件的服务器。当服务器接受新的连接套接字时,它使用ClientThread类包装该套接字。这是代码:

public class Server extends Thread {
    private ServerSocket server;
    private Vector<ClientThread> clients;

    @Override
    public void run() {
        listen();
    }

    private void listen() {

    new Thread("Listening Thread") {

        @Override
        public void run() {
            while (true) {
                try {
                    Socket socket = server.accept();

                    ClientThread newClient = new ClientThread(socket);
                    newClient.start();
                    clients.addElement(newClient);

                } catch (IOException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}

ClientThread是Server类中的私有类。它始终从ObjectInputStream中侦听一个Object,但我希望能够在该对象之后接收一个大文件。这就是为什么我认为我应该使用多线程。这是代码:

private class ClientThread extends Thread {

    public Socket socket;
    private boolean loggedIn;
    private ObjectInputStream ois;
    private BufferedInputStream bis;

    public ClientThread(Socket socket) {
        this.socket = socket;
        loggedIn = true;

        InputStream is = socket.getInputStream();
        ois = new ObjectInputStream(is);
        bis = new BufferedInputStream(is);
    }

    @Override
    public void run() {
        receive();
    }

    private void receive() {

        while (loggedIn) {
            try {
                // this method blocks i guess
                Object object = ois.readObject();

                // after the object comes the large file
                byte[] bytes = new byte[SOME_SIZE];

                int bytesRead;
                int totalRead = 0;

                // reading the large file into memory
                while ((bytesRead = bis.read(bytes, totalRead, bytes.length - totalRead)) > -1) {
                    totalRead += bytesRead;
                }

                // rest of the code for handling received bytes.......

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

}

我不确定是否可以接收这样的数据,因为所有这些客户端套接字都将数据发送到此服务器上的同一端口(我猜?)。如果客户端同时发送数据,则服务器需要知道哪个客户端的数据。这已经得到了解决,或者我需要完全不同的方法吗?

我不知道这是不是一个愚蠢的问题,但就像我说我刚开始学习这些东西一样。此外,我无法测试我的程序,因为我甚至没有客户端的代码。只是想确保我一开始就不会出错。如果这是错误的,请随意发表一些想法。 :)谢谢!

2 个答案:

答案 0 :(得分:1)

一开始它还不错:) 您可以稍后使用Selector进行改进,但这是另一个主题。

但有些澄清:ServerSocket侦听特定端口。当远程客户端连接到它时,创建通信信道(即套接字)。如果另一个客户端连接,则创建另一个套接字两个插槽都是不同的通道,因为它们连接到不同的远程IP 端口而不会相互干扰。

这一切都与TCP headersIP headers的形成有关:TCP数据包的发送包含源头和目的地端口的标头,包含源和目标 IP 的IP标头。这些用于区分不同的套接字。

关于&#34;广播&#34;你想做什么(根据你在@ Rajesh的回答中的评论),你有选择:

  • 使用ServerSocketSocket开始
  • ,在纯TCP中自行完成
  • 切换到UDP并使用MulticastSocket,它具有发出单个发送的优势,但您必须处理客户端代码中的丢失/无序数据报(UDP不保证交付或订购,就像TCP一样)
  • 使用SelectorSocketChannel
  • 检查NIO
  • 调查jGroupsNetty等为您执行I / O操作的框架

在您学习的过程中,我建议您按上述顺序进行操作。使用框架很不错,但通过编码自己会教你更多。

答案 1 :(得分:1)

这将在功能上起作用。每个线程都从连接到不同客户端(地址+端口)的单独套接字读取。它们是独立的流,因此从这样的读取中没有任何问题。

然而,使用异步套接字要好得多。

目前的实施中很少有事情可以照顾:

1)作为一种好的做法,在传输完成后关闭流/套接字。

2)对于每个新连接,都会创建一个新线程。那不会扩大规模。甚至有些人可以发送许多请求并关闭您的应用程序。最好使用一个线程池。 “ClientThread”可以只实现“Runnable”,当收到新连接时,只需将新的“ClientThread”提交给线程池即可。 (在这种情况下,最好将其命名为ClientTask而不是ClientThread)

如前所述,使用异步套接字会更有效率和可扩展性,但需要一些时间来掌握它。有了这个,您可以只使用一个线程并行读取所有套接字,并根据负载,可以使用相同的线程或线程池来处理从所有套接字接收的数据。请注意,即使使用池,也不需要单独的线程来处理每个套接字......只是为了充分利用多个CPU核心,可以使用多个线程来处理数据。

您可以尝试使用java nio(Selector + SocketChannels)或netty库。与nio相比,Netty更容易使用。