使用异步套接字编程发送二进制数据并读取其值

Posted

技术标签:

【中文标题】使用异步套接字编程发送二进制数据并读取其值【英文标题】:Send binary data and read the values of that using async socket programming 【发布时间】:2014-07-05 12:53:43 【问题描述】:

我正在尝试从我的客户端向服务器发送数据。客户端向服务器发送消息,每条消息都是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 字节发送并从服务器中获取和分离?

每四个字节是一个字段,我应该能够得到这个值。

【问题讨论】:

更多信息请看这个:***.com/questions/24349052/… 【参考方案1】:

你做错了。如果要传输二进制数据,请不要将其编码为字符串。有多种方法可以做到这一点:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Msg

    public int A, B, C, D, E, F, G, H, M;

然后使用Marshal 类来获取字节。不过,这将使您在有线数据上保持小端序。

另一种方法是使用BinaryWriter。同样的,little-endian 数据,除非您自己转换或使用 BinaryWriter 的替代版本。

然后,使用NetworkStream 会容易得多,因为这个类会为您处理数据包碎片。这是使用BinaryWriter 方法的发送代码:

using (var stream = new NetworkStream(stocket))

    var writer = new BinaryWriter(stream);
    writer.Write(21);
    writer.Write(32);
    // etc

在客户端使用NetworkStreamBinaryReader 进行同样的操作。

注意:您可以通过 async/await 功能将异步 I/O 与 NetworkStream 一起使用。

【讨论】:

所以我认为第二种方法更好,但我不知道如何将它结合到我的应用程序中?【参考方案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;

【讨论】:

谢谢你让我在我的项目中写你的代码,我会告诉你结果 但是我遇到了一个问题,这些值没有转换为二进制,它们是整数,例如 000200010003 .....为什么? 好的,我创建这条消息 CreateMessage(1, 2, 1, 1, 2, 1, 1, 1, 0);但输出是 000100020001000100020001000100010000 @EA 我认为你对endianness 感到困惑,如果你愿意,可以使用 IPAddress.HostToNetworkOrderIPAddress.NetworkToHostOrder

以上是关于使用异步套接字编程发送二进制数据并读取其值的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中通过套接字发送图像

通过套接字编程发送图像文件(jpeg、png)的提示/示例?

如何通过 C 中的 HTTP POST 请求发送图像或二进制数据

java串口通信中怎样以十六进制数发送

如何使用C / C ++套接字从HTTP读取二进制文件

如何通过套接字发送/接收二进制数据?