我拿到一台可以发送数据的设备,作为服务端.看网上的例子,使用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>
|
|
一次能接收的数据长度取决于buffer.Length和socket.Available,后者是协议缓冲区收到的未读取数据。注意,当发送方发过来的数据量较大或者网络不太好时,缓冲区很可能一次收不完所有数据,所以通常是收发双方协定一个接收完的认定模式。比如让发送方把数据总长度(long)放在最前面的8字节,接收方先读取首8字节,转成long,然后在循环receive时判断已收到的长度
|
|
头+包体+校验,头部信息可能包括总长度,然后自己接收计算,是否达到总长度
|
|
一般是自定义一个类,比如:
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
{//接收完成,做处理}
大概是这么个意思
|
|
不是“每天这类问题很多”,实在是这种问题是最基本的概念,却每天就好像发现自称小学毕业的人被发现“1+2=3等于都没有理解”的人。
假设对方连续发送了1000、200个字节的两块数据,接收端可能一次接收到1200字节,也可能分900字节、200字节、100字节三次接收到,因为tcp本来就是“慢启动、自动拆包、自动粘包”的。这完全看发送端机器的忙闲程度、网络通讯状况而定。所以在本地、单机上的简单测试不算数。
有个这个基本概念,再来看一个人的程序写的是否正确。不能理解这个基本概念,多说无益。
|
|
我们不厌其烦地说“要循环多地Revceive/Read”,这其实就等于给自称小学毕业的人重新讲数字加减法,是不得已的。
我真的不愿意看到csdn整天讨论这个问题,但是许多问题、许多“数据过长,所以无法一次接收完成”之类的话都是这个陷阱而引起的。
|
|
谢谢回复,你这个类里面是装的什么?自己定于的??StateObject类
|
|
我把你的代码整理修改了一下,但是有一个地方转换不了 //实现接受消息的方法
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>
|
|
你自己写代码 var socket = ar.AsyncState as Socket; 时就没想过 AsyncState 属性可以保存自己愿意传送的任意类型的参数?
|
90分 |
其实意思到了,你改下就行了。不管是TcpClient还是Socket,都一样,类型不对,你改下不好了,思路是这样的。我只能帮你到这里了
|