2016-08-12 90 views
1

我是网络编程的新手,我一直在寻找解决方案来解决我的问题,但找不到一个。我想要的是有一台服务器可以同时接收来自多个套接字的文件。当服务器接受新的连接套接字时,它会用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(); 
      } 
     } 
    } 

} 

我不知道如果接受这样的数据,因为所有这些客户端套接字在此服务器上发送数据到同一个端口,甚至有可能。如果客户端同时发送数据,服务器需要知道哪个数据是针对哪个客户端的。这已经被照顾了,或者我需要完全不同的方法吗?

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

+0

去年我添加了一个类似的请求,我最终使用Netty http://netty.io/,并且他们有一个wiki,其中有几个例子http://netty.io/wiki/,用于socket检查telnet或者可能UDP。 – Deceiver

+1

“我不确定是否可以接收这样的数据,因为所有这些客户端套接字都将数据发送到此服务器上的同一端口(我猜?)” - 客户端连接到一个单一端口,是的。但他们有一个独特的**本地**端口。所以,是的,您实际上可以同时从连接到同一个(公共服务器)端口的许多客户端接收数据。 – Fildor

+1

好,清楚的是,如果它们都在同一个客户端主机中,它们将被赋予一个唯一的本地端口,这是唯一需要唯一本地端口的时间。 – EJP

回答

1

首先它不坏:) 您可以稍后改进使用Selector但这是另一个话题。

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

这一切都与如何形成TCP headersIP headers做:一个TCP数据包与包含源和目的地端口,在包含源和目的地IP IP首部的开头其标头中发送。这些用于区分不同的套接字。


关于“播出”你想这样做(按在@ Rajesh的回答您的评论),您可以选择:

做I/O的东西,你在学习调查框架,我建议你做的是,在上述顺序。使用框架是很好的,但通过编码自己会教你更多。

+0

谢谢你的回答!现在我读到这一切都是有道理的。很高兴这可以工作。 :) – pavlee

+0

@pavlee,网络编程现在将为您提供一个全新的可能性世界,尽情享受吧! :) – Matthieu

+0

谢谢。我现在不会使用框架,因为我想了解它是如何工作的。猜猜在实施之前我应该​​多了解一下它。 – pavlee

1

这将在功能上起作用。每个线程正在从连接到不同客户端(地址+端口)的单独套接字读取。它们是分开的流,因此在阅读这些内容时没有问题。

但是,使用异步套接字会更好。

,可以在当前的实现被照顾几件事情:

1)作为一个很好的做法,关闭流/插座传送完毕。

2)对于每个新连接,都会创建一个新线程。这不会扩展。甚至有人可以发送很多请求,并打开你的应用程序。会更好地使用线程池。 “ClientThread”只能实现“Runnable”,并且在收到新连接时,只需将新的“ClientThread”提交给线程池即可。 (在这种情况下,最好将其命名为ClientTask而不是ClientThread)

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

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

+0

使用线程池只会限制连接的数量。缩放会更糟。解决方案是使用'Selector'和一个线程池来处理“* atomic *”事件(连接,读取,写入...)。 – Matthieu

+0

那么,我认为吞吐量和可扩展性是两个不同的东西......考虑一下 - 服务一次获得1000个连接,它将产生1000个线程。直线型jvm将随着堆栈溢出而下降。核心大小为平均请求数的线程池将保持稳定,但吞吐量将在其设计的级别进行调节。 – Rajesh

+0

根据可用资源,任何系统都可以处理最大值。除此之外,它必须优雅地处理。如果系统中的某个组件不可预测地占用更多资源,则其他组件将会挨饿或系统将停止运行。毫无疑问,异步套接字会更好,但是当使用同步套接字时,最好是节流,以便以可预测的方式消耗资源。 U可以通过分配更多资源并允许更多线程来扩展/缩减,具体取决于预期的平行/最大并行请求数量;除了计划/设计之外,在没有系统停机的情况下处理优雅。 – Rajesh