2017-09-27 5 views
1

내 응용 프로그램에 TCP 클라이언트와 TCP 서버 클래스가 있습니다. 클라이언트가 "1 | 2 |"와 같은 작은 문자열을 보냅니다. 또는 "1 | 11 |"TCP 클라이언트가 데이터를 전송하지만 TCP 서버가 null을 수신합니다.

클라이언트 클래스

public class TcpClient { 


private static final int MAX_DATA_RETRY = 1; 
private static final int PING_TIMEOUT = 100; 

private ClientThread thread; 
private boolean mRun = true; 
private PrintWriter mBufferOut; 

private String mIPAdress; 


private ArrayList<BufferDataItem> messageBuffer = new ArrayList<BufferDataItem>(); 
private Socket mSocket; 

public TcpClient() 
{ 
    thread = new ClientThread(); 
    thread.start(); 
} 

private class ClientThread extends Thread { 

    @Override 
    public void run() { 

     while(mRun) 
     { 
      if(messageBuffer.size() <= 0) 
       continue; 

      BufferDataItem currMessage = messageBuffer.get(0); 
      currMessage.retryCount++; 
      if(currMessage.retryCount > MAX_DATA_RETRY) 
      { 
       messageBuffer.remove(0); 
       continue; 
      } 

      try { 
       //here you must put your computer's IP address. 
       InetAddress serverAddr = InetAddress.getByName(currMessage.ip); 



       //Log.e("TCP Client", "C: Connecting..."); 

       try { 
        if(!serverAddr.isReachable(PING_TIMEOUT)) 
        { 
         //only attempt to connect to devices that are reachable 
         messageBuffer.remove(0); 
         continue; 
        } 


        //create a socket to make the connection with the server 
        mSocket = new Socket(serverAddr, TcpManager.SERVER_PORT); 

        //Log.i("TCP Debug", "inside try catch"); 
        //sends the message to the server 

        mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream())), true); 

        String message = currMessage.message; 
        if (mBufferOut != null && !mBufferOut.checkError()) { 
         Log.d("TCP SEND", "PUTTING IN BUFFER! " + message); 
         mBufferOut.println(message); 
         listener.messageSent(message, currMessage.ip); 
         messageBuffer.remove(0); 
        } 
        mBufferOut.flush(); 

       } 
       catch (ConnectException e) { 
        //Connection refused by found device! 
        //Log.e("TCP", "C: ConnectException ip = "+currMessage.ip, e); 
        listener.hostUnreachable(currMessage.ip); 
        continue; 
       } 
       catch (Exception e) { 
        Log.e("TCP", "S: Error", e); 
        listener.messageSendError(e); 
       } 
       finally { 
        if(mSocket != null) 
         mSocket.close(); 
       } 

      } 
      catch (Exception e) { 
       Log.e("TCP", "C: Error", e); 
       listener.messageSendError(e); 
       continue; 
      } 
     } 
    } 
} 



/** 
* Sends the message entered by client to the server 
* 
* @param message text entered by client 
*/ 
public void sendMessage(String message) { 

    BufferDataItem data = new BufferDataItem(); 
    data.message = message; 
    data.ip = mIPAdress; 
    messageBuffer.add(data); 
} 

public void sendMessage(String message, String ip) { 
    mIPAdress = ip; 
    BufferDataItem data = new BufferDataItem(); 
    data.message = message; 
    data.ip = mIPAdress; 
    messageBuffer.add(data); 
} 


/** 
* Close the connection and release the members 
*/ 
public void stopClient() { 
    Log.i("Debug", "stopClient"); 

    mRun = false; 

    if (mBufferOut != null) { 
     mBufferOut.flush(); 
     mBufferOut.close(); 
    } 
    mBufferOut = null; 
} 

private class BufferDataItem 
{ 
    public String message = ""; 
    public int retryCount = 0; 
    public String ip = ""; 
} 

private OnMessageSent listener = null; 

public interface OnMessageSent { 
    public void messageSent(String message, String ip); 

    public void hostUnreachable(String ip); 

    public void messageSendError(Exception e); 

} 

public void setMessageSentListener(OnMessageSent listener) 
{ 
    this.listener = listener; 
} 

public void removeMessageSentListener() 
{ 
    this.listener = null; 
} 

}

서버 클래스

public class TcpServer { 

private ServerThread thread; 
private boolean mRun = true; 
private boolean mEnd = false; 


public TcpServer() 
{ 
    thread = new ServerThread(); 
    thread.start(); 
} 

private class ServerThread extends Thread { 

    @Override 
    public void run() { 

     try { 
      Boolean end = false; 
      ServerSocket ss = new ServerSocket(TcpManager.SERVER_PORT); 
      while (mRun) { 
       //Server is waiting for client here, if needed 
       Socket s = ss.accept(); 
       BufferedReader input = new BufferedReader(new InputStreamReader(s.getInputStream())); 
       //PrintWriter output = new PrintWriter(s.getOutputStream(), true); //Autoflush 
       String st = input.readLine(); 
       String remoteIP = s.getRemoteSocketAddress().toString(); 
       int index = remoteIP.indexOf(":"); 
       remoteIP = remoteIP.substring(1,index); 

       Log.d("TCP READ", "TCP READ: " + st); 
       if(st != null) 
        listener.messageReceived(st, remoteIP); 
       //output.println("Good bye and thanks for all the fish :)"); 

       if(mEnd) 
       { 
        s.close(); 
        mRun = false; 
       } 
      } 

      ss.close(); 


     } catch (UnknownHostException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity 
//class at on asynckTask doInBackground 
public interface OnMessageReceived { 
    public void messageReceived(String message, String ip); 
} 

private OnMessageReceived listener = null; 

public void SetMessageReceivedListener(OnMessageReceived listener) 
{ 
    this.listener = listener; 
} 

public void RemoveMessageReceivedListener() 
{ 
    this.listener = null; 
} 

}

이 후 처음 몇은 실행 시간과에 대해 잘 작동하지만 클라이언트가 보내는 " 1 | 11 | " 서버는 readLine 중에 st를 null로 설정합니다. String st = input.readLine();

누구에게 의견이 있습니까?

+0

ArrayList는 스레드 안전 컨테이너가 아닙니다. 클라이언트 코드는 동기화없이 다른 스레드에서'messageBuffer'를 수정합니다. –

+0

또한 서버 코드에서'mEnd'가 true로 설정되지 않습니다. 따라서 서버는 결코 클라이언트 소켓과 관련 스트림을 닫지 않습니다. –

+0

Zaboj에게 감사드립니다. 나는 멀티 쓰레딩이 내 머리를 맡는다는 것을 인정해야만한다. ArrayList에 대한 thread safe 대안을 제안 할 수 있겠는가? 서버에서는 계속 진행중인 데이터를 수신 할 수 있도록 소켓을 열려 있어야한다고 생각했습니다. 그렇지 않은가요? – Dan

답변

0

잠시 후 서버가 유효한 데이터를받지 못하는 두 가지 이유가 있습니다. mEnd가 true로 설정하지 않기 때문에

  1. 서버 소켓을 s 닫히지 않습니다. 클라이언트는 각 메시지에 대해 새 TCP 연결을 엽니 다. 서버는 연결을위한 소켓을 생성하지만 소켓을 닫지 않으며 연결의 서버 측이 열린 상태로 유지됩니다. 리소스가 누출되어 문제가 발생할 수 있습니다.

  2. 클라이언트는 ArrayList<BufferDataItem> messageBuffer을 사용합니다. ArrayList은 스레드로부터 안전한 컬렉션이 아니며 둘 이상의 스레드에서 messageBuffer이 사용되었습니다. 여기 synchronizedList을 사용하는 것이 안전합니다. How do I make my ArrayList Thread-Safe? Another approach to problem in Java? 또는 Concurrent threads adding to ArrayList at same time - what happens?