2012-01-15 4 views
1

TCP를 통해 통신하는 클라이언트와 서버를 모두 가졌습니다. 클라이언트는 NetworkStream을 사용하여 정보를 다시 읽는 서버로 정보를 보내면 사용자가 종료하고 연결을 닫을 때까지 프로세스가 계속됩니다. 문제는 NetworkStream이 이전 쓰기로 더티라는 것입니다. 따라서 클라이언트가 처음에는 "aa"문자열을, 두 번째 문자열은 "b"문자열을 전송한다고 가정 해 봅시다. 두 번째 읽기에서는 서버에 "ba"가 표시됩니다. 여기서 누락 된 것이 무엇입니까? 서버가 읽는 동안 NetworkStream을 소비하면 안됩니까? Here`s 관련 코드 ...클라이언트/서버 TCP 통신 중에 NetworkStream이 더럽습니다.

서버

 while (true) 
     { 
      try 
      { 
       NetworkStream clientStream = tcpClient.GetStream(); 
       bytesRead = clientStream.Read(messageBytes, 0, messageBytes.Length); 
      } 
      catch (Exception ex) 
      { 
       LogToConsole(clientEndPoint, String.Format("[ERROR] Exception: {0}", ex.Message)); 
       break; 
      } 

      if (bytesRead == 0) 
      { 
       LogToConsole(clientEndPoint, "Client has disconnected"); 
       break; 
      } 

      messageCounter++; 
      string message = Encoding.ASCII.GetString(messageBytes); 
      message = message.Substring(0, message.IndexOf('\0')); 
      LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message)); 
     } 

CLIENT

  string infoToSend = null; 
      do 
      { 
       Console.Write(" >> Info to send: "); 
       infoToSend = Console.ReadLine(); 

       if (!String.IsNullOrEmpty(infoToSend)) 
       { 
        NetworkStream serverStream = client.GetStream(); 
        byte[] buffer = Encoding.ASCII.GetBytes(infoToSend); 
        serverStream.Write(buffer, 0, buffer.Length); 
        serverStream.Flush(); 
       } 
      } while (!String.IsNullOrEmpty(infoToSend)); 

SOLUTION

더글라스 발견 된 바와 같이, 버퍼 (messageBytes)은 이전 판독 더러운이었다. 나는 서버에 대해이 코드로 끝났다 (그것이 다른 사람을 위해 유용 할 수 있습니다 나는 전체 코드를 게시) : 각 메시지에도 불구하고,

namespace Gateway 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int requestCount = 0; 

      TcpListener serverSocket = new TcpListener(IPAddress.Any, 8888); 
      serverSocket.Start(); 
      LogToConsole("Server Started. Waiting for clients ..."); 

      while ((true)) 
      { 
       try 
       { 
        TcpClient client = serverSocket.AcceptTcpClient(); 
        requestCount++; 
        LogToConsole(String.Format("Connection from {0} accepted. Request #{1}", client.Client.RemoteEndPoint.ToString(), requestCount)); 

        Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientConnection)); 
        clientThread.IsBackground = true; 
        clientThread.Start(client); 
        LogToConsole(String.Format("Thread #{0} created to handle connection from {1}", clientThread.ManagedThreadId, client.Client.RemoteEndPoint.ToString())); 
        LogToConsole("Waiting for next client ..."); 
       } 
       catch (Exception ex) 
       { 
        LogToConsole(ex.ToString()); 
        break; 
       } 
      } 
     } 

     static void HandleClientConnection(object client) 
     { 
      TcpClient tcpClient = (TcpClient)client; 

      byte[] messageBytes = new byte[1024]; 
      int bytesRead; 
      int messageCounter = 0; 

      string clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString(); 

      while (true) 
      { 
       try 
       { 
        NetworkStream clientStream = tcpClient.GetStream(); 
        bytesRead = clientStream.Read(messageBytes, 0, messageBytes.Length); 
       } 
       catch (Exception ex) 
       { 
        LogToConsole(clientEndPoint, String.Format("[ERROR] Exception: {0}", ex.Message)); 
        break; 
       } 

       if (bytesRead == 0) 
       { 
        LogToConsole(clientEndPoint, "Client has disconnected"); 
        break; 
       } 

       messageCounter++; 
       string message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead); 
       LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message)); 
      } 

      LogToConsole(clientEndPoint, "Closed connection to client"); 
      tcpClient.Close(); 
     } 

     static void LogToConsole(string clientEndPoint, string message) 
     { 
      int threadId = Thread.CurrentThread.ManagedThreadId; 
      string time = DateTime.Now.ToString("HH:mm:ss"); 
      Console.WriteLine("{0} [{1}, {2}] {3}", time, threadId, clientEndPoint, message); 
     } 

     static void LogToConsole(string message) 
     { 
      int threadId = Thread.CurrentThread.ManagedThreadId; 
      string time = DateTime.Now.ToString("HH:mm:ss"); 
      Console.WriteLine("{0} [{1}] {2}", time, threadId, message); 
     } 
    } 
} 

답변

2
string message = Encoding.ASCII.GetString(messageBytes); 

위의 호출은 전체 버퍼마다 디코딩 것 처음에는 n 바이트 (여기서 n은 메시지 길이 임)로 기록됩니다. 첫 번째 메시지 인 "aa"는 버퍼의 처음 2 바이트에 기록됩니다. 두 번째 메시지 "b"는 첫 번째 바이트에만 쓰여지고 첫 번째 'a'문자는 덮어 쓰고 두 번째 'a'는 그대로 두십시오. 이것이 버퍼에 두 번째 메시지 뒤에 "ba"가 포함 된 것처럼 보이는 이유입니다.

당신은 하찮게 위의 호출을 변경하여이 문제를 해결 할 수 있습니다

string message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead); 

그러나, 코드가 또 다른 문제에 감염 될 것이다 : NetworkStream.Read는 현재 사용할 수있는만큼 데이터를 읽습니다. 클라이언트가 여전히 전송 중이면 부분 메시지가 리턴 될 수 있습니다. 따라서 서버는 두 메시지를 "a"및 "ab"로 읽을 수 있습니다.

한 줄로 된 문자 메시지 만 전송하는 것 같기 때문에 서버의 StreamReader과 클라이언트의 StreamWriterNetworkStream을 넣을 수 있습니다. 그런 다음 서버에서 StreamReader.ReadLine으로 전화를 걸고 클라이언트에서는 StreamWriter.WriteLine으로 전화하면됩니다. ReadLine은 개행이 발생할 때까지 계속 읽으며 스트림의 끝에 도달하면 null을 반환합니다.

서버 :

using (NetworkStream clientStream = tcpClient.GetStream()) 
using (StreamReader reader = new StreamReader(clientStream)) 
{ 
    while (true) 
    { 
     message = reader.ReadLine(); 

     if (message == null) 
     { 
      LogToConsole(clientEndPoint, "Client has disconnected"); 
      break; 
     } 

     messageCounter++; 
     LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message)); 
    } 
} 

클라이언트 : 클라이언트가 메시지 내에서 줄 바꿈 를 보낼 수있는 경우

using (NetworkStream serverStream = client.GetStream()) 
using (StreamWriter writer = new StreamWriter(serverStream)) 
{ 
    do 
    { 
     Console.Write(" >> Info to send: "); 
     infoToSend = Console.ReadLine(); 

     if (!String.IsNullOrEmpty(infoToSend)) 
      writer.WriteLine(infoToSend); 

    } while (!String.IsNullOrEmpty(infoToSend)); 
} 

이 솔루션은 작동하지 않습니다.

+0

아하. 당신이 그걸 발견 했기에 좋았습니다. 완충 대는 참으로 더러 웠습니다. 나는 원래 질문에서 내가 구현 한 것을 게시 할 것이다. 감사! – gsb

관련 문제