2013-07-02 4 views
0

서버가 은행이고 클라이언트가 그 은행의 bracnh이기 때문에 고전적인 멀티 쓰레드 서버/클라이언트 애플리케이션 인 애플리케이션을 구현하려고합니다. 첫 번째 단계에서는 은행이 연결되는 모든 지점을 기록하기를 원합니다. 그래서 은행이 객체를 추출하여 기록 할 수 있도록 소켓의 객체 스트림에 객체로 branck를 보내야합니다. 여기 자바에서 소켓을 사용하여 클라이언트와 서버간에 객체 교환하기

내가 지금까지 한 일이다

import java.io.IOException; 
import java.net.ServerSocket; 
import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.List; 


public class Banque { 
private List<Succursale> listSucc = new ArrayList<Succursale>(); 
private int sommeTotale; 
private int nbSuccInit = 4; 

public void ajouteSucc(Succursale suc){ 

} 
public Banque(){ 
    initialiserServeur();  
} 
private void initialiserServeur() { 
    ServerSocket serverSocket = null; 
    try { 
     serverSocket = new ServerSocket(10118); 
    } 
    catch (IOException e) 
    { 
     System.err.println("On ne peut pas ecouter au port: 10118."); 
     System.exit(1); 
    } 
    System.out.println ("Le serveur est en marche, Attente de la connexion....."); 
    int i = 0; 
    while(i<5){ 
     try { 
      UtilMultiTh mt = new UtilMultiTh(serverSocket.accept()); 
      Thread t = new Thread(mt); 
      t.start(); 
      listSucc.add(mt.getSuc()); 
      System.out.println(listSucc.size()); 

      for(int j =0; j<listSucc.size();j++){ 
       System.out.println("La succursale "+(j+1)+" est:"+listSucc.get(j).getName()); 
      } 
      i++; 
      System.out.println("FLAGPOSTban"); 
     } 
     catch (IOException e) 
     { 
      System.err.println("Accept a echoue."); 
      System.exit(1); 
     } 
    } 


    System.out.println ("connexion reussie"); 
    System.out.println ("Attente de l'entree....."); 

} 
public static void main (String [] args){ 
    Banque banK = new Banque(); 
} 

}

여기 branchs

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.PrintWriter; 
import java.net.*; 

public class UtilMultiTh implements Runnable { 

private Socket soc; 
private Succursale suc; 
public UtilMultiTh(Socket s){ 
    System.out.println("FLAGconsmth"); 
    this.soc = s; 
    } 
public void run() { 
    System.out.println("FLAGPOSrun"); 
    ObjectOutputStream oos; 
    ObjectInputStream ois; 
    try{    
     oos = new ObjectOutputStream(soc.getOutputStream()); 
     ois = new ObjectInputStream(soc.getInputStream()); 

     //System.out.println("La succ est"); 
     try { 
      Object o = ois.readObject(); 
      if(o!=null){ 
       suc = (Succursale)o; 
       //System.out.println("La succ est"+suc.getName()); 
      } 
      /*while(o!=null){ 
       suc = (Succursale)o; 
       System.out.println("La succ est"+suc.getName()); 
      }*/ 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
     oos.close(); 
     ois.close(); 
     soc.close(); 
    } catch (IOException e1) { 
     e1.printStackTrace(); 
    } 
} 
public synchronized Succursale getSuc() { 
    return suc; 
} 
public void setSuc(Succursale suc) { 
    this.suc = suc; 
} 

} 

을의 멀티 스레드 연결을 관리하고 클래스 MultiTh가 branchs의 Succursale 클래스의

import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 
import java.net.Socket; 
import java.net.UnknownHostException; 
import java.util.ArrayList; 
import java.util.List; 

public class Succursale implements Serializable { 
private String coordonnees; 
private String name; 
private int sommeDepart; 
private int sommeRecue; 
private int sommeEnvoyee; 
private List<Succursale> listSuccAc = new ArrayList<Succursale>(); 
private GuiSuccursale succView; 

public Succursale(){ 
    succView = new GuiSuccursale(Constantes.sommeDepart,1); 
    this.sommeDepart=Constantes.sommeDepart; 
    this.name="Succursale: "+(1); 
    connexionBanque(); 
} 
public void connexionBanque(){ 
    String host = Constantes.adrBanque[0]; 
    int port = Constantes.portBanque[0]; 
    Socket echoSocket = null; 
    try { 
     echoSocket = new Socket(host, port); 
     ObjectOutputStream oos = new ObjectOutputStream(echoSocket.getOutputStream()); 
     oos.writeObject(this); 
     System.out.println("FLAGPOSTSUcc"); 
    } catch (UnknownHostException e) { 
     System.err.println("Hôte inconnu: " + host); 
     System.exit(1); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     System.err.println("Ne pas se connecter au serveur: " + host); 
     System.exit(1); 
    } 
} 
public void connexionSuccursales(){ 

} 
public void envoiManuel(){ 

} 
public String getName() { 
    return name; 
} 
public void envoiPeriodique(){ 

} 
public static void main (String[] args){ 
    Succursale suc = new Succursale(); 
} 
} 

두 가지 질문이 있습니다. 어떻게 UtilMultuTh가 Succursale을 Banque로 리턴 할 수 있습니까? 그 전에 UtilMultiTh 클래스의 readObject 반환 값은 null입니까? succursale 클래스에서 연결을 etablishing 한 직후 소켓에 클래스를 넣었습니까? 여기에 무한 루프를 넣어야합니까?

편집 : 코드가 바뀌 었습니다. 이제 다중 스레드가 소켓에서 Succursale을 제대로 가져오고 있습니다. 문제는 이제 스레드가 동기화되지 않는다는 것입니다. 왜냐하면 UtilMultiTh는 Banque가 그것을 얻고 싶어하기 때문에 Succursale을 가져 오기 때문에 synchrnosation에 익숙하지 않습니다. utilMultiTh가 실행 된 후에 만 ​​getSuccursale을 수행하도록 Banque에게 어떻게 말할 수 있습니까?

+2

내 의견으로는, 자바의 직렬화는 끔찍하며, 전선을 통해 많은 방법으로 보냅니다. 그래서 전선에 객체의 자체 맞춤 마샬링을 수행하고 전선의 자체 역 마샬링을 수행합니다. 이렇게하면 전송되는 내용을 정확히 알 수 있으므로 객체 스트림을 사용할 필요가 없습니다. – nook

+0

@ publ1c_stat1c JSON은 매우 컴팩트 한 IMO입니다. [Gson] (https://code.google.com/p/google-gson/)을 사용하여 Java 직렬화에서 JSON으로 전환 할 때 I/O 시간이 80 % 감소했습니다. 그 덕분에 우리 고객이 더 반응하게되었습니다. – Brian

+0

어떤 이유로 든 널 (null)을 전송할 계획이 아니라면'readObject()'의 결과를 null로 테스트하는 것은 의미가 없습니다. 그렇지 않은 경우, 메소드는 null을 리턴하지 않습니다. EOS 테스트를 잘못 사용했다면 대신'EOFException'을 잡아야합니다. – EJP

답변

0

Google의 protobufs가 완벽합니다. 나는 그들을 사용하여 클라이언트와 서버간에 바이트 출력을 보내는 것이 좋습니다. TCP를 사용할 계획이라면 출력물의 프레임을 만들어야합니다.

자바의 직렬화 메커니즘은 다른 런타임 버전간에 항상 깨질 수 있습니다. 또한 서버 또는 클라이언트를 다른 언어로 구현하기로 결정한 경우 어떻게해야합니까? 자바의 전체 직렬화 로직을 복제해야한다. Protobuf는 객체를 마샬링 및 언 마샬링하는 데 필요한 지루한 프로세스를 바이트 단위로 처리합니다. 그래서 꽤 많이, 자바의 더 나은 형태는 언어 독립적 인 직렬화에 내장되어 있습니다.

개체 스트림을 포기하는 것이 좋습니다. 나는 이것이 당신이 바라는 대답이 아니라는 것을 알고 있습니다. 그러나 그것은 장기적으로 당신을 위해 더 좋은 것을 만들 것입니다. 이 질문에

ProtoBuffers

+0

자바로해야만합니다. – user2133558

+0

내가 말했듯이, ProtoBuffers는 언어 독립적입니다. 그것은 Java와 함께 작동하며 Java의 직렬화에 대한 좋은 대안입니다. –

0

이 답변은 아니지만 코멘트에 맞지 않을 것이고, 나는 그것이라고 할 필요가있다 생각합니다.

각 쓰기 후에 반드시 출력 스트림을 재설정하십시오! ObjecdtOutputStream은 개체를 한 번만 씁니다. 다시 쓰려고하면, "잠시 전에 내가 입력 한 물체를이 시점에서 다시 입력 스트림에 넣으십시오."라는 작은 노트를 보냅니다. 공간을 절약하지만 개체가 변경된 경우 변경 사항이 적용되지 않습니다. 또한 전송 된 원본 객체는 양측의 메모리에 보관되어 성능을 저하시킵니다. 다시 설정하면 모든 것을 지우고 새로운 시작을 제공합니다.

또한 Serializable 대신 Externalizable을 사용합니다. 이렇게하면 보내지는 것을 제어 할 수 있습니다 (코드를 작성해야 함). 다른 객체를 참조하는 객체를 작성하면 다른 객체도 모두 작성하게되는 위험이 있습니다. 또한 Externalizable 클래스를 변경하는 경우에도 동일한, 이전 형식으로 쓸 수 있습니다.또한 버전 번호를 입력 할 수 있습니다. 이러한 경우 때때로 최신 버전에서 이전 버전으로 작성된 스트림을 읽을 수 있지만 형식이 변경되었다는 경고가 항상 표시됩니다.

ObjectOutputStream의 의도는 모든 데이터를 포함하는 하나의 객체를 작성할 수 있다는 것입니다. writeObject를 한 번 호출하면 정말 큰 그래프를 보낼 수 있습니다. 이것은 독창적입니다. 컴퓨터 게임의 완전한 상태를 디스크 파일에 하나의 writeObject(this)으로 쓰는 것은 나에게 잘 돌아 갔지만 소켓에 쓰는 것은 대개 재앙입니다.

저는 ObjectOutput을 사용하지 않고 기본 요소 만 작성하려고합니다. 더 간단하고 빠르며 많은 제어 권한이 있습니다. 그러나 Input 끝에서 어떤 객체를 만들지는 알지 못합니다. 가장 좋은 방법은 개체를 읽고 쓰는 것이지만 Externalizable 인터페이스에 대한 고유 한 메서드를 작성하고 각 writeObject 다음에 reset()을 호출하여 I/O를 간단하게 유지하는 것이 가장 좋습니다.