2012-04-25 4 views
2

우선 무엇보다 혼란스럽지 않고 설명하는 방법이 너무 명확하지 않으므로 간단하게하려고 노력할 것입니다. 2 명의 플레이어 게임의 일부로 2 명의 클라이언트를 "쌍으로"묶는 서버 응용 프로그램이 있습니다. 첫 번째 클라이언트가 서버에 연결될 때 소켓 연결이 수락되면 해당 클라이언트와 함께 새 ClientProtocol 스레드가 만들어 지지만 시작되지는 않습니다. 다른 클라이언트가 서버에 연결하면 이전 스레드에 추가되고 스레드가 시작됩니다. 스레드는 두 개의 입력 스트림과 두 개의 출력 스트림 (각 클라이언트에 하나씩)을 알고 있습니다. 그러나 클라이언트 또는 서버 측에서 읽으려고하면 아무 일도 일어나지 않습니다. 이것은 단일 스레드에서 단일 클라이언트에 대해 잘 작동합니다.Java 클라이언트 - 서버/하나의 스레드 다중 클라이언트

서버 측 (메인 스레드)

public static Queue<ContestProtocol> contesters; 

public static void main(String[] args) throws IOException { 
    ServerSocket serverSocket = null; 
    contesters = new LinkedList<ContestProtocol>(); 

    try { 
     serverSocket = new ServerSocket(4444); 
    } catch (IOException e) { 
     System.err.println("Could not listen on port: 4444."); 
     System.exit(-1); 
    } 

    while (true) { 
     Socket socket = serverSocket.accept(); 

     ObjectInputStream ois; 
     ObjectOutputStream oos; 

     try { 
      ois = new ObjectInputStream(socket.getInputStream()); 
      oos = new ObjectOutputStream(socket.getOutputStream()); 

      synchronized (contesters) { 
       if (contesters.size() == 0) { 
        Contester contester1 = new contester(ois, oos); 
        contesters.add(new ContestProtocol(contester1)); 
       } else { 
        Contester contester2 = new Contester(ois, oos); 
        contesters.poll().hook(contester2).start(); 
       } 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

서버 측 (ContestProtocol)

private Contester contester1, contester2; 

public ContestProtocol(Contester contester1) { 
    super("ContestProtocol"); 
} 

public ContestProtocol hook(Contester contester2) { 
    this.contester2 = contester2; 
    return this; 
} 

public void run() { 
    try { 
     contester1.getOOS().writeInt(-1); 
     contester2.getOOS().writeInt(-2); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

서버 측 (contester) :

public Contester(ObjectInputStream ois, ObjectOutputStream oos) { 
    this.ois = ois; 
    this.oos = oos; 
} 

public ObjectInputStream getOIS() { 
    return ois; 
} 

public ObjectOutputStream getOOS() { 
    return oos; 
} 

클라이언트 측 여기에 몇 가지 코드는 다음과 같습니다

try { 
      socket = new Socket(serverAddr, 4444); 
      oos = new ObjectOutputStream(socket.getOutputStream()); 
      ois = new ObjectInputStream(socket.getInputStream()); 
      int selectedChampion = ois.readInt(); 
} catch (Exception e) { 
       e.printStackTrace(); 
} 

ContestProtocol 스레드는 문제없이 실행을 완료하지만 두 클라이언트는 모두 readInt()에서 중단됩니다. 나는 왜 그런지 이해하지 못한다.

+0

내가 접근하는 방식이 마음에 드는지 모르겠지만 그럼에도 불구하고 두 고객이 그 int를 얻어야합니다. Wireshark는 무엇이라고 말합니까? 서버에서 데이터가 빠져 나오고 있습니까? –

+0

일부 테스트 후에 일부 스레드가 서로를 차단해야하는 것으로 보입니다. ois.available()에 대한 호출이 디버거에서 중단됩니다. – Renaud

+0

오, 나는 작동하지 않도록 디자인을 exepcted하지만 네드웨어없이 클라이언트가 서버를 읽기 전에 잠글 때까지 int를 가져야합니다. –

답변

0

자원에 액세스 할 때 뮤텍스 또는 세마포어 문제가 발생해야합니다. 사용중인 스트림 (이 경우 출력 스트림과 입력 스트림)은 동일한 리소스를 읽고 쓰는 것이 좋습니다. 이 모든 프로세스는 하나의 스레드가 계속 될 때 발생하기 때문에 스레드가 서로 인터럽트하고 서로 대기 프로세스를 작성합니다.
하나의 솔루션은 스트림 객체에 이벤트 라이저를 등록하여 하나가 완료 될 때마다 다른 객체 만 시작되도록하는 것입니다.

+0

스트림은 스레드의 처음부터 끝까지 살아 있어야합니다. 스트림을 닫으면 소켓을 닫습니다. – Renaud

+0

내가 이해하는 한 "읽고 쓰는 리소스가 같아서"말하고있는 것을 이해하는지 모르겠다. objectInputStream과 ObjectOutputStream은 서로 다른 두 개의 소켓에 쓰고 읽는 중이다. 그것은 하나의 클라이언트가 서버에 읽고 쓰는 경우에 작동합니다. 나는 틀릴지도 모른다. – Renaud

+0

여기 flush()를 호출하는 것을 잊어 버렸습니다. public void run() { try { contester1.getOOS(). writeInt (-1); contester1.getOOS(). flush(); contester2.getOOS(). writeInt (-2); contester2.getOOS(). flush(); } catch (예외 e) { e.printStackTrace(); } } – Renaud

0

클라이언트 측 입력 스트림을 루프해야합니다.

while(true){ 
if(ois.avaible() > 0) 
ois.readInt(); 
} 

팁 : 더 나은 성능을 얻으려면 netty로 비동기 네트워킹을 살펴보십시오.

관련 문제