Socket 客户端异步收取 如何确保数据完整性

.Net技术 码拜 9年前 (2015-02-11) 1568次浏览 0个评论

我拿到一台可以发送数据的设备,作为服务端.看网上的例子,使用C# Socket编写了一个简单的异步客户端获取数据.
由于设备一次发送的数据过长,所以无法一次接收完成,在异步函数中该如何处理 确保数据的完整性呢?
代码中的 var length = socket.EndReceive(ar);这个长度每次最大获取1024,我查阅了CSDN和网上资料,都没有明确的资料。
麻烦各位大神指点一下。我发现CSDN每天关于Socket的问题很多,真心希望有好心大神能够整理,或者版主可以整理一下。便于新手参考,谢谢~祝大家工作愉快
以下是完整网上例子代码.
pre class=”brush: csharp”>
      static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect(“192.168.2.80”, 40012);
                Console.WriteLine(“{0}:{1}:Conneted….” + “\r\n”, “192.168.2.80”, 40012);
                //实现接受消息的方法
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
       /// <summary>
        /// 异步接收
        /// </summary>
        /// <param name=”ar”></param>
        public static void ReceiveMessage(IAsyncResult ar)
        {
            try
            {
                var socket = ar.AsyncState as Socket;
                socket.ReceiveTimeout = 40000;
                var length = socket.EndReceive(ar);
                byte[] reallData = new byte[length];
                Array.Copy(buffer, reallData, reallData.Length);
                string data = BitConverter.ToString(reallData);
                Console.WriteLine(data);
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
/pre>

 
Socket 客户端异步收取 如何确保数据完整性
一次能接收的数据长度取决于buffer.Length和socket.Available,后者是协议缓冲区收到的未读取数据。注意,当发送方发过来的数据量较大或者网络不太好时,缓冲区很可能一次收不完所有数据,所以通常是收发双方协定一个接收完的认定模式。比如让发送方把数据总长度(long)放在最前面的8字节,接收方先读取首8字节,转成long,然后在循环receive时判断已收到的长度
Socket 客户端异步收取 如何确保数据完整性
头+包体+校验,头部信息可能包括总长度,然后自己接收计算,是否达到总长度
Socket 客户端异步收取 如何确保数据完整性
一般是自定义一个类,比如:

 public class StateObject
        {
            public TcpClient Client = null;           
            public byte[] buffer = new byte[1024];        
            public MemoryStream mb = new MemoryStream();
        }

开始 的时候:

static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect("192.168.2.80", 40012);
                Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", "192.168.2.80", 40012);
                //实现接受消息的方法
StateObject o=bew StateObject();
o.Client=socket;
                socket.BeginReceive(o.buffer , 0,o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

接收到数据的时候:
pre class=”brush: csharp”> 
TcpClient client=null;
            StateObject state = (StateObject)ar.AsyncState;   
                // 从输入参数异步state对象中获取state和socket对象                  
                client = state.Client;
                //从远程设备读取数据                
                int bytesRead = client.Client.EndReceive(ar);  
                if (bytesRead > 0)
                {
                    // 有数据,存储.
                        state.mb.Write(state.buffer,0, state.buffer.Length);           
                    // 继续读取.  
                    if (client.Client.Available > 0)
                    {
                        client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                    }
                    else
                    {//接收完成,做处理}

 

大概是这么个意思

Socket 客户端异步收取 如何确保数据完整性
引用 楼主 u012074823 的回复:

我拿到一台可以发送数据的设备,作为服务端.看网上的例子,使用C# Socket编写了一个简单的异步客户端获取数据.
由于设备一次发送的数据过长,所以无法一次接收完成,在异步函数中该如何处理 确保数据的完整性呢?
代码中的 var length = socket.EndReceive(ar);这个长度每次最大获取1024,我查阅了CSDN和网上资料,都没有明确的资料。
麻烦各位大神指点一下。我发现CSDN每天关于Socket的问题很多,真心希望有好心大神能够整理,或者版主可以整理一下。便于新手参考,谢谢~祝大家工作愉快

不是“每天这类问题很多”,实在是这种问题是最基本的概念,却每天就好像发现自称小学毕业的人被发现“1+2=3等于都没有理解”的人。
假设对方连续发送了1000、200个字节的两块数据,接收端可能一次接收到1200字节,也可能分900字节、200字节、100字节三次接收到,因为tcp本来就是“慢启动、自动拆包、自动粘包”的。这完全看发送端机器的忙闲程度、网络通讯状况而定。所以在本地、单机上的简单测试不算数。
有个这个基本概念,再来看一个人的程序写的是否正确。不能理解这个基本概念,多说无益。

Socket 客户端异步收取 如何确保数据完整性
我们不厌其烦地说“要循环多地Revceive/Read”,这其实就等于给自称小学毕业的人重新讲数字加减法,是不得已的。
我真的不愿意看到csdn整天讨论这个问题,但是许多问题、许多“数据过长,所以无法一次接收完成”之类的话都是这个陷阱而引起的。
Socket 客户端异步收取 如何确保数据完整性
引用 3 楼 clxcxx 的回复:

一般是自定义一个类,比如:

 public class StateObject
        {
            public TcpClient Client = null;           
            public byte[] buffer = new byte[1024];        
            public MemoryStream mb = new MemoryStream();
        }

开始 的时候:

static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect("192.168.2.80", 40012);
                Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", "192.168.2.80", 40012);
                //实现接受消息的方法
StateObject o=bew StateObject();
o.Client=socket;
                socket.BeginReceive(o.buffer , 0,o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

接收到数据的时候:
pre class=”brush: csharp”> 
TcpClient client=null;
            StateObject state = (StateObject)ar.AsyncState;   
                // 从输入参数异步state对象中获取state和socket对象                  
                client = state.Client;
                //从远程设备读取数据                
                int bytesRead = client.Client.EndReceive(ar);  
                if (bytesRead > 0)
                {
                    // 有数据,存储.
                        state.mb.Write(state.buffer,0, state.buffer.Length);           
                    // 继续读取.  
                    if (client.Client.Available > 0)
                    {
                        client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                    }
                    else
                    {//接收完成,做处理}

 

大概是这么个意思

谢谢回复,你这个类里面是装的什么?自己定于的??StateObject类

Socket 客户端异步收取 如何确保数据完整性
引用 3 楼 clxcxx 的回复:

一般是自定义一个类,比如:

 public class StateObject
        {
            public TcpClient Client = null;           
            public byte[] buffer = new byte[1024];        
            public MemoryStream mb = new MemoryStream();
        }

开始 的时候:

static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect("192.168.2.80", 40012);
                Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", "192.168.2.80", 40012);
                //实现接受消息的方法
StateObject o=bew StateObject();
o.Client=socket;
                socket.BeginReceive(o.buffer , 0,o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

接收到数据的时候:
pre class=”brush: csharp”> 
TcpClient client=null;
            StateObject state = (StateObject)ar.AsyncState;   
                // 从输入参数异步state对象中获取state和socket对象                  
                client = state.Client;
                //从远程设备读取数据                
                int bytesRead = client.Client.EndReceive(ar);  
                if (bytesRead > 0)
                {
                    // 有数据,存储.
                        state.mb.Write(state.buffer,0, state.buffer.Length);           
                    // 继续读取.  
                    if (client.Client.Available > 0)
                    {
                        client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                    }
                    else
                    {//接收完成,做处理}

 

大概是这么个意思

我把你的代码整理修改了一下,但是有一个地方转换不了 //实现接受消息的方法
                StateObject o = new StateObject();
                o.Client = socket;这个地方 TCPclint和Socket之间?是不是哪里有误?还是
pre class=”brush: csharp”>
public class StateObject
    {
        public TcpClient Client = null;
        public byte[] buffer = new byte[1024];
        public MemoryStream mb = new MemoryStream();
        public const int BufferSize = 1024;
    }
    class Program
    {
        static byte[] buffer = new byte[2048];
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect(“192.168.0.80”, 4001);
                Console.WriteLine(“{0}:{1}:Conneted….” + “\r\n”, “192.168.0.80”, 4001);
                //实现接受消息的方法
                StateObject o = new StateObject();
                o.Client = socket;
                socket.BeginReceive(o.buffer, 0, o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// 异步接收数据
        /// </summary>
        /// <param name=”ar”></param>
        public static void ReceiveMessage(IAsyncResult ar)
        {
            TcpClient client = null;
            StateObject state = (StateObject)ar.AsyncState;
            // 从输入参数异步state对象中获取state和socket对象                  
            client = state.Client;
            //从远程设备读取数据                
            int bytesRead = client.Client.EndReceive(ar);
            if (bytesRead > 0)
            {
                // 有数据,存储.
                state.mb.Write(state.buffer, 0, state.buffer.Length);
                // 继续读取.  
                if (client.Client.Available > 0)
                {
                    client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveMessage), state);
                }
                else
                {
                    //接收完成,做处理
                }
            }
        }
/pre>

Socket 客户端异步收取 如何确保数据完整性
引用 6 楼 u012074823 的回复:

谢谢回复,你这个类里面是装的什么?自己定于的??StateObject类

你自己写代码 var socket = ar.AsyncState as Socket; 时就没想过 AsyncState 属性可以保存自己愿意传送的任意类型的参数?

Socket 客户端异步收取 如何确保数据完整性
90分
引用 7 楼 u012074823 的回复:
Quote: 引用 3 楼 clxcxx 的回复:

一般是自定义一个类,比如:

 public class StateObject
        {
            public TcpClient Client = null;           
            public byte[] buffer = new byte[1024];        
            public MemoryStream mb = new MemoryStream();
        }

开始 的时候:

static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect("192.168.2.80", 40012);
                Console.WriteLine("{0}:{1}:Conneted...." + "\r\n", "192.168.2.80", 40012);
                //实现接受消息的方法
StateObject o=bew StateObject();
o.Client=socket;
                socket.BeginReceive(o.buffer , 0,o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

接收到数据的时候:
pre class=”brush: csharp”> 
TcpClient client=null;
            StateObject state = (StateObject)ar.AsyncState;   
                // 从输入参数异步state对象中获取state和socket对象                  
                client = state.Client;
                //从远程设备读取数据                
                int bytesRead = client.Client.EndReceive(ar);  
                if (bytesRead > 0)
                {
                    // 有数据,存储.
                        state.mb.Write(state.buffer,0, state.buffer.Length);           
                    // 继续读取.  
                    if (client.Client.Available > 0)
                    {
                        client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                    }
                    else
                    {//接收完成,做处理}

 

大概是这么个意思

我把你的代码整理修改了一下,但是有一个地方转换不了 //实现接受消息的方法
                StateObject o = new StateObject();
                o.Client = socket;这个地方 TCPclint和Socket之间?是不是哪里有误?还是
pre class=”brush: csharp”>
public class StateObject
    {
        public TcpClient Client = null;
        public byte[] buffer = new byte[1024];
        public MemoryStream mb = new MemoryStream();
        public const int BufferSize = 1024;
    }
    class Program
    {
        static byte[] buffer = new byte[2048];
        static void Main(string[] args)
        {
            //创建一个Socket
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {   //连接到指定服务器的指定端口
                socket.Connect(“192.168.0.80”, 4001);
                Console.WriteLine(“{0}:{1}:Conneted….” + “\r\n”, “192.168.0.80”, 4001);
                //实现接受消息的方法
                StateObject o = new StateObject();
                o.Client = socket;
                socket.BeginReceive(o.buffer, 0, o.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), o);
                Console.Read();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// 异步接收数据
        /// </summary>
        /// <param name=”ar”></param>
        public static void ReceiveMessage(IAsyncResult ar)
        {
            TcpClient client = null;
            StateObject state = (StateObject)ar.AsyncState;
            // 从输入参数异步state对象中获取state和socket对象                  
            client = state.Client;
            //从远程设备读取数据                
            int bytesRead = client.Client.EndReceive(ar);
            if (bytesRead > 0)
            {
                // 有数据,存储.
                state.mb.Write(state.buffer, 0, state.buffer.Length);
                // 继续读取.  
                if (client.Client.Available > 0)
                {
                    client.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveMessage), state);
                }
                else
                {
                    //接收完成,做处理
                }
            }
        }
/pre>

其实意思到了,你改下就行了。不管是TcpClient还是Socket,都一样,类型不对,你改下不好了,思路是这样的。我只能帮你到这里了


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Socket 客户端异步收取 如何确保数据完整性
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!