2013-04-14 4 views
0

Теперь я пишу крошечный фреймворк на основе SocketAsyncEventArgs, этот класс создан на основе IOCP, который намного эффективнее, чем режим APM. , но здесь у меня возникли проблемы при запуске теста. здесь код сервера:Почему, похоже, мое TCP-соединение потеряет пакеты на стороне сервера?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Net.Sockets; 
using System.Net; 
using System.Threading; 
using System.Windows.Forms; 

namespace SocketServer 
{ 
public class Server 
{ 
    Socket serverSocket; 
    SocketAsyncEventArgs socketAsyncEventArgs; 
    SocketAsyncEventArgsPool readWritePool; 
    HandleMessage handleMessage; 
    BufferManager buffeManager; 

    const int PrefixSize = 11; 

    public void Init(int port,int connections,int receiveBufferSize) 
    { 
     buffeManager = new BufferManager(receiveBufferSize * connections * 2, receiveBufferSize); 

     buffeManager.InitBuffer(); 

     readWritePool = new SocketAsyncEventArgsPool(connections); 

     SocketAsyncEventArgs socketAsyncEventArgsPooling; 
     for (int i = 0; i < connections; i++) 
     { 
      socketAsyncEventArgsPooling = new SocketAsyncEventArgs(); 
      socketAsyncEventArgsPooling.Completed += readEventArgsIO_Completed; 

      buffeManager.SetBuffer(socketAsyncEventArgsPooling); 
      readWritePool.Push(socketAsyncEventArgsPooling); 
     } 

     handleMessage = new HandleMessage(); 

     IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList; 
     IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port); 

     this.serverSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 


     if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6) 
     { 
      this.serverSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); 
      this.serverSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port)); 
     } 
     else 
     { 
      this.serverSocket.Bind(localEndPoint); 
     } 

     this.serverSocket.Listen(100); 

     StartAccept(null); 
    } 

    private void StartAccept(SocketAsyncEventArgs acceptSocketAsyncEventArgs) 
    { 
     if (acceptSocketAsyncEventArgs == null) 
     { 
      acceptSocketAsyncEventArgs = new SocketAsyncEventArgs(); 
      acceptSocketAsyncEventArgs.Completed += socketAsyncEventArgs_Completed; 
     } 
     else 
     { 
      acceptSocketAsyncEventArgs.AcceptSocket = null; 
     } 

     Boolean willRaiseEvent = this.serverSocket.AcceptAsync(acceptSocketAsyncEventArgs); 
     if (!willRaiseEvent) 
     { 
      this.ProcessAccept(acceptSocketAsyncEventArgs); 
     } 
    } 

    private void socketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) 
    { 
     ProcessAccept(e); 
    } 

    private void readEventArgsIO_Completed(object sender, SocketAsyncEventArgs e) 
    { 
     switch (e.LastOperation) 
     { 
      case SocketAsyncOperation.Receive: 
       this.ProcessReceive(e); 
       break; 
      case SocketAsyncOperation.Send: 
       //this.ProcessSend(e); 
       break; 
      default: 
       throw new ArgumentException("The last operation completed on the socket was not a receive or send"); 
     } 
    } 


    private void ProcessAccept(SocketAsyncEventArgs e) 
    { 

     SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop(); 
     //SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs(); 
     readEventArgs.UserToken = e.AcceptSocket; 

     Console.WriteLine("---------------------------------------------------"); 
     Console.WriteLine("Client Connected {0}",e.AcceptSocket.RemoteEndPoint); 

     Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); 

     if (!willRaiseEvent) 
     { 
      this.ProcessReceive(readEventArgs); 
     } 

     this.StartAccept(e); 
    } 

    private void ProcessReceive(SocketAsyncEventArgs e) 
    { 
     if (e.BytesTransferred > 0) 
     { 
      if (e.SocketError == SocketError.Success) 
      { 
       Console.WriteLine("receiving data, {0} bytes", e.BytesTransferred); 
       Socket socket = e.UserToken as Socket; 

       int bytesTransferred = e.BytesTransferred; 

       string received = Encoding.ASCII.GetString(e.Buffer, e.Offset, bytesTransferred); 


       Console.WriteLine("Received:{0}", received); 

       string[] msgArray = handleMessage.GetActualString(received); 

       foreach (var msg in msgArray) 
       { 
        Console.WriteLine("After Split:{0}", msg); 
       } 

       // Array.Clear(e.Buffer, e.Offset, bytesTransferred); 

       Boolean willRaiseEvent = socket.SendAsync(e); 
       if (!willRaiseEvent) 
       { 
        this.ProcessSend(e); 
       } 

       readWritePool.Push(e); 
      } 
     } 

    } 

    private void ProcessSend(SocketAsyncEventArgs e) 
    { 

    } 
} 
} 

вот мой код клиента:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Net.Sockets; 
using System.Threading; 
using System.Net; 

namespace SocketClient 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.2.129"), 1234); 

     SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs(); 

     connectArgs.RemoteEndPoint = ipEndPoint; 
     connectArgs.Completed += OnConnected; 

     socket.ConnectAsync(connectArgs); 

     socket.SendBufferSize = Int16.MaxValue; 

     //NetworkStream streamToServer = new NetworkStream(socket); 
     string text = "[length=12]Hello server"; 
     byte[] sendBuffer = Encoding.ASCII.GetBytes(text); 


     for (int i = 0; i < 5; i++) 
     { 
      SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs(); 

      sendArgs.UserToken = socket; 
      sendArgs.SetBuffer(sendBuffer,0,sendBuffer.Length); 
      sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend); 


      socket.SendAsync(sendArgs); 
     } 

     Console.ReadLine(); 
    } 

    private static void OnSend(object sender, SocketAsyncEventArgs e) 
    { 
     Console.WriteLine("SendOk: {0}", e.UserToken.ToString()); 
    } 

    private static void OnConnected(object sender, SocketAsyncEventArgs e) 
    { 
     Console.WriteLine("Conectioned"); 
    } 
} 
} 

Но когда я начинаю несколько клиентов, я обнаружил, что иногда сервер может правильно получать сообщения; но иногда сервер может получать только первое сообщение, кажущиеся сообщения кажутся «потерянными», каждый может советовать? спасибо.

Я слышал от кого-то, что я должен реализовать свой собственный протокол для передачи данных, но кто-нибудь может сказать мне, как определить? ТНХ

ниже является захват экрана отростка стороне сервера: enter image description here

+0

Определите «У меня возникли проблемы». – EJP

ответ

1

Проблема описана происходит потому, что в коде сервера метод ReceiveAsync вызывается только один раз независимо от того, если все сообщение было получено или не. Остальная часть просто не читается.

Как указано в документе «ReceiveAsync documentation on MSDN»: «Для сокетов с потоком байтового потока входящие данные помещаются в буфер до тех пор, пока буфер не будет заполнен, соединение не будет закрыто или внутренние данные будут исчерпаны». В вашем случае, если сообщение, отправленное клиентом, разбивается на несколько фрагментов, тогда, когда первая часть данных достигает сервера, она помещается системой во внутренний буфер сокета. Если у вас есть метод ReceiveAsync, ожидающий данных, он считывает внутренне буферизованные данные до тех пор, пока не исчерпается, а затем не вернется, даже это только первый фрагмент, и все еще есть данные. Для этого вам понадобится другая операция ReceiveAsync. Если вы хотите проверить, что это правда, вы можете попытаться поместить Thread.Sleep(200) в цикл for, который отправляет 5 сообщений от клиента. В этом случае сервер получает только первую часть сообщения, становится очень высоким, потому что TCP использует некоторые алгоритмы для эффективной отправки данных, и этот тайм-аут определит его для отправки 5 сообщений отдельно. Однако вы не можете контролировать, как сообщение фрагментировано в сети между клиентом и сервером. Может потребоваться несколько операций ReceiveAsync, даже если все сообщение было отправлено с использованием только одной операции SendAsync.

Чтобы решить проблему чтения частичных сообщений на сервере, вам нужно знать, сколько байтов вы ожидаете. Это можно сделать либо с использованием постоянной длины сообщения, либо с помощью определенного протокола для определения длины, например, префиксации каждого сообщения, отправленного от клиента, количеством байтов отправляемого сообщения. Серверу придется выполнять несколько вызовов ReceiveAsync до тех пор, пока не будет получена вся длина. Для этого серверу необходимо будет сохранить количество оставшихся байтов для приема. Вы можете найти полный и объясненный пример SocketAsyncEventArgs client-server application on CodeProject и понять, что это поможет вам в решении вашей проблемы.

+0

thx. Я решил это, добавив еще один метод getasync. – CharlieShi