2016-08-12 2 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/를 사용하여 끝내 었으며 소켓이 telnet인지 또는 몇 가지 예제가있는 http://netty.io/wiki/가 있습니다. udp. – Deceiver

+1

"이 모든 클라이언트 소켓이이 서버의 동일한 포트에 데이터를 보내고 있기 때문에 이와 같은 데이터를 수신하는 것이 가능할 지 모르겠습니다."- 클라이언트가 하나의 단일 포트에 연결됩니다. 그러나 그들은 ** 고유 한 ** local ** 포트를 제공받습니다. 그래서 예, 실제로 동일한 (공개 서버) 포트에 연결된 많은 클라이언트로부터 동시에받을 수 있습니다. – Fildor

+1

분명히 말하자면, 그들이 모두 동일한 클라이언트 호스트에 있다면 고유 한 로컬 포트가 주어집니다. 유일한 로컬 포트가 필요한 유일한 시간입니다. – EJP

답변

1

처음에는 나쁘지 않다. :) Selector을 사용하여 나중에 향상시킬 수 있지만 다른 주제입니다.

몇 가지 설명 : ServerSocket은 특정 포트에서 수신 대기합니다. 원격 클라이언트가 그것에 접속하면, 통신 채널 (즉, 소켓)이 생성된다. 다른 클라이언트가 연결되면 다른 소켓이 만들어집니다. 두 소켓은 서로 다른 채널이며 서로 다른 원격 IP 포트에 연결되어 있기 때문에 서로 간섭하지 않습니다.

그것은 모든 TCP headersIP headers이 형성되는 방식과 관련이있다 :의 TCP 데이터 패킷의 헤더는 소스 및 대상 IP를 포함하는 IP 헤더의 상단에 포트 소스와 대상을 포함하여 전송됩니다. 그것들은 다른 소켓을 구별하기 위해 사용됩니다. 당신이 시작처럼

  • ServerSocket 순수 TCP에서 직접 수행하고 Socket 다음, 당신은 옵션이 있습니다 (@ 인 Rajesh의 대답에 귀하의 코멘트에 따라)하고 싶은 "방송"에 관한


  • UDP로 전환하고 MulticastSocket을 사용하십시오. 단일 송신을 발행 할 수있는 이점이 있지만 클라이언트 코드에서 누락되거나 정렬되지 않은 데이터 그램을 처리해야합니다 (UDP는 TCP와 같이 전달 또는 주문을 보장하지 않습니다)
  • jGroups 또는 배우고로서 당신

의 I/O 물건을 Netty 같은 SelectorSocketChannel

  • 조사 프레임 워크 NIO를 확인, 내가 제안 당신이 할 위의 순서로 그. 프레임 워크를 사용하는 것은 좋지만 직접 코드를 작성하면 훨씬 많은 것을 가르쳐줍니다.

  • +0

    답변 해 주셔서 감사합니다! 내가 이것을 읽었을 때 모두 의미가 있습니다. 다행 일이 다행. :) – pavlee

    +0

    @pavlee, 네트워크 프로그래밍은 이제 새로운 가능성을 제공합니다. :) – Matthieu

    +0

    감사. 내가 어떻게 작동하는지 이해하고 싶기 때문에 지금은 프레임 워크를 사용하지 않을 것이다. 내가 구현하기 전에 그것에 대해 더 배워야한다고 생각해. – pavlee

    1

    이것은 기능적으로 작동합니다. 각 스레드는 다른 클라이언트 (주소 + 포트)에 연결된 별도의 소켓에서 읽는 중입니다. 그것들은 별도의 스트림이므로, 이와 같은 것을 읽을 때 아무런 문제가 없습니다.

    그러나 비동기 소켓을 사용하는 것이 훨씬 더 좋습니다. 현재 구현에서 처리 될 수

    몇 가지 : 전송이 완료되면

    1) 좋은 방법으로는, 스트림/소켓을 닫습니다.

    2) 모든 새 연결마다 새 스레드가 만들어집니다. 그것은 규모가 조정되지 않습니다. 심지어 어떤 사람은 많은 요청을 보내고 앱을 내릴 수 있습니다. 스레드 풀을 사용하는 것이 좋습니다. "ClientThread"는 "Runnable"을 구현할 수 있으며 새로운 연결이 수신되면 새 "ClientThread"를 스레드 풀에 제출하십시오. (이 경우 ClientThread 대신 ClientTask로 이름을 지정하는 것이 더 낫습니다.)

    앞서 언급했듯이 비동기 소켓을 사용하는 것이 훨씬 효율적이고 확장 성이 뛰어나지 만 마스터 링에는 다소 시간이 걸립니다. 이를 통해 하나의 스레드 만 사용하여 모든 소켓을 병렬로 읽을 수 있으며로드에 따라 동일한 스레드 또는 스레드 풀을 사용하여 모든 소켓에서 수신 한 데이터를 처리 할 수 ​​있습니다. 풀을 사용하더라도 각 소켓을 처리하기 위해 별도의 스레드가 필요하지 않습니다 ... 다중 CPU 코어를 최대한 활용하려면 다중 스레드를 사용하여 데이터를 처리 할 수 ​​있습니다.

    java nio (Selector + SocketChannels) 또는 netty 라이브러리를 시도 할 수 있습니다. Netio는 nio에 비해 훨씬 사용하기 쉽습니다.

    +0

    스레드 풀을 사용하면 연결 수가 제한됩니다. 스케일링은 더욱 악화 될 것입니다. 해결책은'Selector'와 스레드 풀을 사용하여 * atomic * 이벤트 (연결, 읽기, 쓰기, ...)를 처리하는 것입니다. – Matthieu

    +0

    글쎄, 나는 처리량과 확장 성을 두 가지 다른 것으로 생각한다. 이것을 고려해 보라. - 서비스는 1000 개의 연결을 한꺼번에 가져오고, 1000 개의 스레드를 생성 할 것이다. 스트레이트 웨이 jvm은 스택 오버플로와 함께 중단됩니다. 평균 요청 수가 코어 크기 인 스레드 풀은 안정적 일 수 있지만 처리량은 설계된 수준에서 조절됩니다. – Rajesh

    +0

    모든 시스템은 사용 가능한 리소스에 따라 처리 할 수있는 최대 용량을 갖습니다. 그 이상으로는 정상적으로 처리해야합니다. 시스템의 한 구성 요소가 예기치 않게 더 많은 자원을 사용하면 다른 구성 요소가 굶어 죽거나 시스템이 다운됩니다. 의심 할 여지없이 비동기 소켓이 더 잘되지만 싱크 소켓을 사용할 때 예측 가능한 방식으로 리소스를 소비하도록 스로틀하는 것이 좋습니다. U는 더 많은 자원을 할당하고 예상되는 병렬 요청의 평균/최대 수에 따라 더 많은 스레드를 허용하여 위/아래로 확장 할 수 있습니다. 계획/설계된 것 이외에는 시스템이 다운되지 않아도 정상적으로 처리됩니다. – Rajesh