如题:Tcp无保护消息边界...这个大家都知道,如果用Tcp协议连续发送多次数据,就会出现一次接收的问题...现在求一个最佳"接收"解决方案.......客户端连发3个数据包:
socket.Send("111");
socket.Send("222");
socket.Send("333数据乱发长度不等");有什么有效的方法让服务器端3次接收.....---------异步开发 能不能达到这个效果....如果能搞一个好例子看下高手们把你们的知道的链接...代码...新颖的思路,想法...贴出来 欣赏一下 不胜感激......
socket.Send("111");
socket.Send("222");
socket.Send("333数据乱发长度不等");有什么有效的方法让服务器端3次接收.....---------异步开发 能不能达到这个效果....如果能搞一个好例子看下高手们把你们的知道的链接...代码...新颖的思路,想法...贴出来 欣赏一下 不胜感激......
当你socket.Send频率过高,并且每次发送数据长度小于8K(SOCKET缓存),就很有可能会出现粘包,也就是说你发送端发送
socket.Send("111");
socket.Send("222");
socket.Send("333数据乱发长度不等");最后有可能在接收端接收到的是一个被连接在一起的字符串“111222333数据乱发长度不等”好像没有一个比较完美的办法可以解决,请参考: http://blog.csdn.net/binghuazh/archive/2009/05/28/4222516.aspx
一直接触Scoket代码比较少!
我给你提供一个最简单也最好理解的解决方案吧。
首先客户端是知道我当次会话传输数据的长度吧,其实这就是个相互规定的协议而已,你们做传输客户端和服务端应该先定好协议
例如:
socket.Send("111"); // 第一次(假设是第一个包)
socket.Send("222"); //第二次(假设是第二个包)
socket.Send("333数据乱发长度不等"); //第三次(假设是第三个包)
一般应该是这样做,发送每个包的时候在包头都要带上这个包的长度,其实就是你send(byte[])的时候,在byte[]前几个字节放入当前包的长度,
例如:当前发送的数据为"111"那么你先要取到“111” 的长度,然后放到待发送的byte[]的前2个字节里(不一定是前两个,这个要客户端和服务端相一致,到时候服务端只取前2个字节即可知道包的长度了),然后再把“111”序列化成byte[] 放到待发送的byte[]里去。然后发送即可,这样服务端得到数据之后首先要解析这个包的长度,然后再根据这个长度去取相同长度的数据. 我说到这够明白了么?
在给你解释一下吧
假设socket.Send("111");
socket.Send("222");
socket.Send("asdfsafd乱七八糟不定长");
这时候出现服务端一次性接收的情况按照我之前说的结构来做的话
那你服务端接受到的数据就是这样的
111的长度+111的byte[]数组+222的长度+222的byte[]数组+asdfsafd乱七八糟不定长的长度+asdfsafd乱七八糟不定长的byte[]数组
当然 111的长度 222的长度 这些都是在客户端 .Length 求出来的,然后序列化即可
在服务端只需取到第一个包的长度,按照长度取相应长度的数据,这个样能取到111,以此类推,这样每个包都可以分开了,只要你逻辑写的对,这样分包绝对没有问题
{
using System;
using System.Collections.Generic;
using System.Text; #region BufferedStream
/// <summary>
/// 缓存流
/// </summary>
public class BufferedStream : System.IO.Stream
{
System.IO.Stream stream;
/// <summary>
/// 构建一个内存缓冲流
/// </summary>
public BufferedStream() : base() { stream = new System.IO.MemoryStream(); }
/// <summary>
/// 从基础流构建流
/// </summary>
/// <param name="stream">源流</param>
public BufferedStream(System.IO.Stream stream) { this.stream = stream; }
/// <summary>
/// 从字节数组构建缓冲流
/// </summary>
/// <param name="bytes">字节数组</param>
public BufferedStream(byte[] bytes) { stream = new System.IO.MemoryStream(bytes); } /// <summary>
/// 将整个流内容写入字节数组,而与 System.IO.MemoryStream.Position 属性无关。
/// </summary>
/// <exception cref="System.InvalidOperationException">如果源流不是一个内存流,将引发此异常</exception>
/// <returns></returns>
public byte[] ToArray()
{
System.IO.MemoryStream ms = stream as System.IO.MemoryStream;
if (ms == null)
throw new InvalidOperationException();
return ms.ToArray();
}
public void Write(long value)
{
this.Write(BitConverter.GetBytes(value));
}
public void Write(ulong value)
{
this.Write(BitConverter.GetBytes(value));
}
public void Write(ushort value)
{
this.Write(BitConverter.GetBytes(value));
}
public void Write(int value)
{
this.Write(BitConverter.GetBytes(value));
}
public void Write(DateTime value)
{
this.Write(BitConverter.GetBytes(value.Ticks));
}
public void Write(uint value)
{
this.Write(BitConverter.GetBytes(value));
}
public void Write(byte[] bytes)
{
this.Write(bytes, 0, bytes.Length);
}
public void Write(string value)
{
byte[] buff = Encoding.UTF8.GetBytes(value);
this.Write(BitConverter.GetBytes((ushort)buff.Length));
this.Write(buff);
}
public void WriteObject(object obj)
{
byte[] bytes = null;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
using (System.IO.Compression.GZipStream gz = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress))
{
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(gz, obj);
bytes = ms.GetBuffer();
}
}
this.Write(bytes.Length);
this.Write(bytes);
}
public void WriteUserId(string userId)
{
byte[] buff = Encoding.UTF8.GetBytes(userId);
this.WriteByte((byte)buff.Length);
this.Write(buff);
} public object ReadObject()
{
int len = this.ReadInt();
byte[] bytes = this.Read(len);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes))
{
using (System.IO.Compression.GZipStream gz = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress))
{
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
return formatter.Deserialize(gz);
}
}
}
public uint ReadUInt()
{
return BitConverter.ToUInt32(Read(4), 0);
}
public int ReadInt()
{
return BitConverter.ToInt32(Read(4), 0);
}
public ushort ReadUShort()
{
byte[] bytes = Read(2);
return BitConverter.ToUInt16(bytes, 0);
}
public DateTime ReadDateTime()
{
byte[] bytes = Read(8);
return new DateTime(BitConverter.ToInt64(bytes, 0));
}
public string ReadString()
{
ushort len = ReadUShort();
byte[] buff = Read(len);
return Encoding.UTF8.GetString(buff);
}
public string ReadUserId()
{
byte len = (byte)ReadByte();
byte[] buff = Read(len);
return Encoding.UTF8.GetString(buff);
}
public byte[] Read(int len)
{
byte[] buff = new byte[len];
this.Read(buff, 0, len);
return buff;
} public override bool CanRead
{
get { return stream.CanRead; }
} public override bool CanSeek
{
get { return stream.CanSeek; }
} public override bool CanWrite
{
get { return stream.CanWrite; }
} public override void Flush()
{
stream.Flush();
} public override long Length
{
get { return stream.Length; }
} public override long Position
{
get
{
return stream.Position;
}
set
{
stream.Position = Position;
}
} public override int Read(byte[] buffer, int offset, int count)
{
return stream.Read(buffer, offset, count);
} public override long Seek(long offset, System.IO.SeekOrigin origin)
{
return stream.Seek(offset, origin);
} public override void SetLength(long value)
{
stream.SetLength(value);
} public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
} public override void Close()
{
stream.Close();
base.Close();
}
}
#endregion
}Send:
BufferedStream bs = new BufferedStream(tcpClient.GetStream());
bs.Write("111");
bs.Write("222");
bs.Write("333");
bs.Write(DateTime.Now);Receive:
BufferedStream bs = new BufferedStream(tcpClient.GetStream());
string str1=bs.ReadString();
string str2=bs.ReadString();
string str3=bs.ReadString();
DateTime dt = bs.ReadDateTime();
然后用NetWorkStream或许连接的网络流。
用networkstream 对远程主机进行读数据和写数据。
例如NetWorkStream ns=new NetWorkStream ();
ns.write();
ns.flush();
ns.write();
ns.flush()
就可以一个一个发了
通用的做法是, 使用特殊标记, 以及长度字段;
我想知道你是怎么在接收端----得到到第一个包的长度的
是用btye[] by = new byte[1];接收 还是用 btye[] by = new byte[10]; 比较大一点的数组接收
我认为缺陷就是----要用个一个固定的长度的byte[]数组去接收 发送字符长度比如 第一次发送 "111" 长度为 3 ,3是一个字节
第二次发送 "222" 长度为 3 ,3是一个字节
第三次发送 "3333333333333" 长度为 13 ,13是2个字节
第四次发送 "4444444444444444.....更长" ,比如长度为 1111 ,1111是4个字节
...更多那么接收端最少要声明一个 >=4的数据,每次接收都固定截取前>=4个字节 就以最大发4位数大小的字节为例那么第一次发送就不能发:"3111" 接收的时候截取前4个字节 就会 接到长度为3111了而是要发3***111-------接收时先接前4位 3*** 去掉*** 得到字符3 在转换成数字3
在根据这个3 又截取3位---得到"111"第二次发送 同理 发3***222 接收同理第三次发 13**3333333333333 接收同理第四次发 11114444444444444444..... 先接收前4位 转换成数字1111 在截取1111个字节 ----------------------------------
如果发送数据更大了....一个5位数 发3****111
----------------------------------呵呵就这么说一说 不知道是不是这个样的
客户端:
string a1="111";
Socket.Send(GetPacket(string a1));
string a2 = "222";
Socket.Send(GetPacket(string a2));
string a3 = "123sdaf乱七八糟不定长";
Socket.Send(GetPacket(string a2)); private byte[] GetPacket(string context)
{
byte[] contextByte = Encoding.ASCII.GetBytes(a1);
byte[] lengthByte = BitConverter.GetBytes(contextByte.Length);
//假设前2个字节为长度 OK?我们规定好的
byte[] sendByte = new byte[contextByte.Length + lengthByte.Length];
int offset = 0;
Buffer.BlockCopy(lengthByte, offset, sendByte, 0, lengthByte.Length);
offset += lengthByte.Length;
Buffer.BlockCopy(contextByte, offset, sendByte, 0, contextByte.Length);
}
服务端: //假设recvByte是你收到的数据
byte[] recvByte = new byte[1024];
byte[] lengthByte = new byte[2];
int offset = 0;
int length;
//第一个包
Buffer.BlockCopy(recvByte, offset, lengthByte, 0, 2);
offset += 2;
length=BitConverter.ToInt32(lengthByte, 0);
byte[] firstPacket = new byte[length];
Buffer.BlockCopy(recvByte, offset, firstPacket, 0, length);
offset+=length;
//第二个包
Buffer.BlockCopy(recvByte, offset, lengthByte, 0, 2);
offset += 2;
length = BitConverter.ToInt32(lengthByte, 0);
byte[] secondPacket = new byte[length];
Buffer.BlockCopy(recvByte, offset, secondPacket, 0, length);
offset += length;
//第三个包
Buffer.BlockCopy(recvByte, offset, lengthByte, 0, 2);
offset += 2;
length = BitConverter.ToInt32(lengthByte, 0);
byte[] thridPacket = new byte[length];
Buffer.BlockCopy(recvByte, offset, thridPacket, 0, length);
offset += length;
这时候firstPacket secondPacket thridPacket 分别是你得到的3个数据
直接
string a1=Encoding.ASCII.GetString(firstPacket);
string a2=Encoding.ASCII.GetString(secondPacket);
string a3=Encoding.ASCII.GetString(thridPacket);
这样就回来了
额……楼主你要是不给我多分我都亏死了
private byte[] GetPacket(string context)
{
byte[] contextByte = Encoding.ASCII.GetBytes(context); //这里应该是context
byte[] lengthByte = BitConverter.GetBytes(contextByte.Length);
//假设前2个字节为长度 OK?我们规定好的
byte[] sendByte = new byte[contextByte.Length + lengthByte.Length];
int offset = 0;
Buffer.BlockCopy(lengthByte, offset, sendByte, 0, lengthByte.Length);
offset += lengthByte.Length;
Buffer.BlockCopy(contextByte, offset, sendByte, 0, contextByte.Length);
}
2 个字节是一个 ushort
4 个字节是一个 uint我想,用 4 个字节已经绰绰有余了吧, 你该不会发大于 uint.MaxValue 长度的数据包吧...
刚好要学习Socket
如果是 点 --对 -- 点 的话 用短连接 还是行吧(如 客户端只是向服务器上传和下载文件)如果是 客户---服务器---客户 (公司做一个聊天程序) 用短连接就不行了吧现在对于Socket的一些基本知识,基本用法 现在掌握的差不多了---
掌握这些 基础的还是容易,要是 达到 sp1234 你对Socket,对编程的理解...我们现在难啊我们公司不是软件公司啊,作出来的东西无法体会到成千上万的人用...我们公司就那么100吧号人,最多就是100个线程......(不是很清楚100个线程是什么概率...有啥负面影响)如果我做的东西 100个人用不出现啥问题...不出问题,找不到问题,找不到错误...我就无法优化程序,就学不到更多的知识..----------------这个贴现在没结,看回复的人蛮多的----可能对新手有点作用,我开始学的时候,怎么都想不通 发3次,为啥一次就接收了.....后来买了本网络编程的书看才看明白-----这里有人提到 "粘包"---发送的时候可能粘包 -------接收的时候也可能粘包----------------先放这里 等几天接吧
int BufListLen = Pro->CreateBufList(m_Rx, m_RxLen);
if (!BufListLen)
{
return 0;
}
...
粘包时,将接收到的数据按照协议产生一个报文队列。
具体代码:可以利用IoBuffer的prefixedDataAvailable方法得到消息头,可惜看了MINA2的源代码之后,prefixedDataAvailable方法的参数只是是1,2,4,也就是说只能设定1,2,4位长度的消息头,这样如果你是自定义长度的报文头,那可就不支持了,不过可以修改MINA2的源码来实现自己特殊的要求。
请高手提一些宝贵意见哦