Отправить двоичные данные и прочитать их значения с помощью асинхронного программирования сокетов

Я пытаюсь отправить данные на сервер с моего клиента. Клиент отправляет сообщения на сервер, каждое сообщение 36 bytes и в этом сообщении каждый 4 байт является полем, а в серверной части я должен быть в состоянии обнаружить это поле из сообщения, которое отправляет клиент. Предположим, у меня есть эти данные:

A=21
B=32
c=43
D=55
E=75
F=73
G=12
H=14
M=12

В клиенте я должен отправить эти значения как одно сообщение. Как вы можете видеть, мое сообщение имеет 9 полей, и каждое поле 4 byte integer и все сообщение 36 byte,

Таким образом, в серверной части, когда сообщение получено, я должен иметь возможность отделить сообщение и найти значение полей.

В клиентском приложении я использую эту структуру для отправки сообщения на мой сервер:

 m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // Cet the remote IP address
            IPAddress ip = IPAddress.Parse(GetIP());
            int iPortNo = System.Convert.ToInt16("12345");
            // Create the end point 
            IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);
            // Connect to the remote host
            m_clientSocket.Connect(ipEnd);
    if (m_clientSocket.Connected)
                {

                    Object objData = ?!!!!;//My message
                    byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString());
                    if (m_clientSocket != null)
                    {
                        m_clientSocket.Send(byData);
                    }
                    Thread.Sleep(4000);

            }
}

И в серверной части я использую этот код для получения данных:

public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                SocketPacket socketData = (SocketPacket)asyn.AsyncState;

                int iRx = 0;
                // Complete the BeginReceive() asynchronous call by EndReceive() method
                // which will return the number of characters written to the stream 
                // by the client
                iRx = socketData.m_currentSocket.EndReceive(asyn);
                char[] chars = new char[iRx + 1];
                System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
                int charLen = d.GetChars(socketData.dataBuffer,
                                         0, iRx, chars, 0);
                System.String szData = new System.String(chars);
                MessageBox.Show(szData);

                // Continue the waiting for data on the Socket
                WaitForData(socketData.m_currentSocket);
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
            }
            catch (SocketException se)
            {
            }
        }

Вот другая часть моего кода сервера:

 public void OnClientConnect(IAsyncResult asyn)
        {
            try
            {
                // Here we complete/end the BeginAccept() asynchronous call
                // by calling EndAccept() - which returns the reference to
                // a new Socket object
                m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
                // Let the worker Socket do the further processing for the 
                // just connected client
                WaitForData(m_workerSocket[m_clientCount]);
                // Now increment the client count
                ++m_clientCount;
                // Display this client connection as a status message on the GUI    
                String str = String.Format("Client # {0} connected", m_clientCount);


                // Since the main Socket is now free, it can go back and wait for
                // other clients who are attempting to connect
                m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
            }


        }
        public class SocketPacket
        {
            public System.Net.Sockets.Socket m_currentSocket;
            public byte[] dataBuffer = new byte[36];
        }

В моем form_load серверной части у меня есть этот код:

    ipaddress = GetIP();
    // Check the port value

    string portStr = "12345";
    int port = System.Convert.ToInt32(portStr);
    // Create the listening socket...
    m_mainSocket = new Socket(AddressFamily.InterNetwork,
                              SocketType.Stream,
                              ProtocolType.Tcp);
    IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
    // Bind to local IP Address...
    m_mainSocket.Bind(ipLocal);
    // Start listening...
    m_mainSocket.Listen(4);
    // Create the call back for any client connections...
    m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

на самом деле я пытаюсь реализовать эту ссылку:

http://www.codeguru.com/csharp/csharp/cs_misc/sampleprograms/article.php/c7695/Asynchronous-Socket-Programming-in-C-Part-I.htm

Моя проблема в том, как я могу отправить свое сообщение в виде 36 байтов через клиента и получить и отделить его от сервера?

Каждые четыре байта - это поле, и я должен иметь возможность получить это значение.

2 ответа

Решение

Похоже, все, что вам нужно, это 2 метода для преобразования целых чисел в байтовый массив и наоборот:

byte[] packet =  CreateMessage(21,32,43,55,75,73,12,14,12);
//send message
//recv message and get ints back
int[] ints =  GetParameters(packet);

....

public byte[] CreateMessage(params int[] parameters)
{
    var buf = new byte[parameters.Length * sizeof(int)];
    for (int i = 0; i < parameters.Length; i++) 
        Array.Copy(BitConverter.GetBytes(parameters[i]), 0, buf, i * sizeof(int), sizeof(int));
    return buf;
}

public int[] GetParameters(byte[] buf)
{
    var ints = new int[buf.Length / sizeof(int)];
    for (int i = 0; i < ints.Length; i++)
        ints[i] = BitConverter.ToInt32(buf, i * sizeof(int));
    return ints;
}

Ты делаешь это неправильно. Если вы хотите передать двоичные данные, не кодируйте их как строку. Есть несколько способов сделать это:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Msg
{
    public int A, B, C, D, E, F, G, H, M;
}

Затем используйте Marshal класс, чтобы получить байты. Тем не менее, вы получите прямой порядок передачи данных.

Другой метод заключается в использовании BinaryWriter, То же самое, данные с прямым порядком байтов, если вы не конвертируете их самостоятельно или не используете альтернативную версию BinaryWriter,

Тогда гораздо проще использовать NetworkStreamпотому что этот класс будет обрабатывать фрагментацию пакетов для вас. Вот код отправки с использованием BinaryWriter метод:

using (var stream = new NetworkStream(stocket))
{
    var writer = new BinaryWriter(stream);
    writer.Write(21);
    writer.Write(32);
    // etc
}

То же самое на стороне клиента, используя NetworkStream и BinaryReader,

NB. Вы можете использовать асинхронный ввод / вывод с NetworkStream используя функцию async/await.

Другие вопросы по тегам