C# Socket 发送消息和接受消息。
开始只是发送文字,定义100*1024字节数组,一次性发送不会丢失,现在需求变了,要发送的有图片,如果内容过多会出现 "一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小"
现在我分批发送 在后一次发送的时候标识一下.我测试 10次基本有5次接不到信息,原因是客户端发送10或者100次,而服务段老是接受4次,如果发消息和图片发送成功了, 服务端接受的次数和客服端发送的次数一样.什么情况会出现这样问题呢?我在虚拟机里面断电调试服务端,在自己的电脑调试客户端。 Sock
开始只是发送文字,定义100*1024字节数组,一次性发送不会丢失,现在需求变了,要发送的有图片,如果内容过多会出现 "一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小"
现在我分批发送 在后一次发送的时候标识一下.我测试 10次基本有5次接不到信息,原因是客户端发送10或者100次,而服务段老是接受4次,如果发消息和图片发送成功了, 服务端接受的次数和客服端发送的次数一样.什么情况会出现这样问题呢?我在虚拟机里面断电调试服务端,在自己的电脑调试客户端。 Sock
实现类
/// <summary>
/// 消息传输
/// </summary>
public class TransferMessage
{ public TransferMessage()
{ }
/// <summary>
/// 给某具体一个IP发送
/// </summary>
/// <param name="info">发送消息内容</param>
/// <param name="remoteIp">要发送的IP</param>
public static void Send(MessageCell info, IPEndPoint remoteIP)
{
//只能用UDP协议发送广播,所以ProtocolType设置为UDP
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//将发送内容转换为字节数组
byte[] bytes = SerializeHelper.ObjToByte(info);
//向子网发送信息
socket.SendTo(bytes, remoteIP);
socket.Close();
} /// <summary>
/// 接受消息
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static MessageCell Receive(Socket s)
{
byte[] bytes = new byte[GlobalInfo.PacketSize * 10];
EndPoint remoteIP = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
// s.ReceiveFrom(bytes, ref remoteIP);
s.Receive(bytes, 0, bytes.Length, SocketFlags.None);
MessageCell cell = SerializeHelper.BytesToObject<MessageCell>(bytes);
return cell;
}
}客户端发送: /// <summary>
/// 给某具体一个IP发送
/// </summary>
/// <param name="info">发送消息内容</param>
/// <param name="remoteIp">要发送的IP</param>
public static void SendMeaasge(MessageInfo info, string remoteIp)
{
//只能用UDP协议发送广播,所以ProtocolType设置为UDP
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//让其自动提供子网中的IP地址
IPEndPoint iep = new IPEndPoint(IPAddress.Parse(remoteIp), GlobalInfo.Port);
//socket.Connect(iep); //将发送内容转换为字节数组
byte[] bytes = SerializeHelper.ObjToByte(info); //自己改写 MemoryStream ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
int PacketSize = GlobalInfo.PacketSize;
int PacketCount = bytes.Length / PacketSize;
//最后一个包的大小
int LastDataPacket = (int)(ms.Length - ((long)(PacketSize * PacketCount)));
//数据包
byte[] data = new byte[PacketSize];
int totalCell = PacketCount;
if (LastDataPacket != 0)
{
totalCell++;
} //开始循环发送数据包
for (int i = 0; i < PacketCount; i++)
{
ms.Position = i * PacketSize;
//从文件流读取数据并填充数据包
ms.Read(data, 0, data.Length);
MessageCell cell = new MessageCell(i, data);
cell.TotalCellCount = totalCell;
cell.IsLastMessage = (i == PacketCount - 1 && LastDataPacket == 0);
TransferMessage.Send(cell, iep);
} //如果还有多余的数据包,则应该发送完毕!
if (LastDataPacket != 0)
{
if (PacketCount > 0)
{
ms.Position = PacketSize * PacketCount;
}
else
{
ms.Position = 0;
}
data = new byte[LastDataPacket];
ms.Read(data, 0, data.Length);
MessageCell cell = new MessageCell(PacketCount, data);
cell.TotalCellCount = totalCell;
cell.IsLastMessage = true;
TransferMessage.Send(cell, iep);
}
socket.Close();
}服务端接受: private void ReceiveMessage3()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, GlobalInfo.Port);
try
{
socket.Bind(iep);
EndPoint ep = (EndPoint)iep;
MemoryStream ms = new MemoryStream();
int TotalCellCount = 0;
while (true)
{
MessageCell cell = TransferMessage.Receive(socket);
TotalCellCount++;
if (cell.IsLastMessage)
{
ms.Position = cell.Index * cell.PacketSize;
ms.Write(cell.Data, 0, cell.Data.Length);
AcceptMessage(ms.GetBuffer());
ms.Flush();
ms.Close();
ms = new MemoryStream();
}
else
{
ms.Position = cell.Index * cell.Data.Length;
ms.Write(cell.Data, 0, cell.Data.Length);
} //if (cell.TotalCellCount != TotalCellCount)
//{
// MessageBox.Show(string.Format("包有丢失,包的总个数是{0},当前最后的包{1}",
// cell.TotalCellCount, cell.Index));
//}
}
} catch (Exception ex)
{
if (ex.Message.Contains("通常每个套接字地址(协议/网络地址/端口)只允许使用一次"))
{
DialogResult result = MessageBox.Show("端口被占用了", "是否关闭之前程序",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
SystemTool.KillSamePort();
// AcceptMessage();
}
}
else
{
MessageBox.Show(ex.Message + "错误方法:" + ex.StackTrace);
}
}
socket.Close();
}
}
如果数据量不大,改用TCP吧.如果不能改协议,在接收端做一个验证.
发送第一个->对方接收->收到已收到回馈包->再发送第二个->。这样一有个交互过程。
作一短暂休息,时间越长越好最短随你网络环境定。效率可想是多么低。还有udp(在多线程并发时更严重)要注意你分的包不一定依次按你发的次序收到
我是把要发送的消息对象MessageInfo变成byte[] 由于这个很大 ,所以我分开发送, 分开发送的时候 我又构建一个对象MessageCell 对象里面包括TotalCellCount、IsLastMessage。
在接收端,分别 反发序列化成MessageCell对象,然后根据MessageCell里面的数据byte[]构建成原始的btye[] ,然后在反序列化成真正的MessageInfo。
比如我在客户端发生100次,在服务端能接受到100次, 因为我不清楚TCP和Udp的特点,只知道Upd传输速度快,就选择了, 总之 只要能保证数据不丢失就行,不然反序列化就会报异常。什么传输方式无所谓,就怕换成TCP也出现这样的问题。
你是把一次发送的较大数据分组发送出去,然后接收的时候再组合好。那么这里就有问题里,你怎么分组,你怎又么组合?比如你一幅图片是 AA BB CC DD EE FF,你分成AA BB CC 某某标志位,DD EE FF 某某标志位,那么当连续发送这些图片时,你服务器接收时怎么组合呢,你接收上来的包可以无序的?别告诉我去找头标志位和尾标位置然后去拼接成一个整图,那你如何去找同一幅图片的头包和尾包呢?如果你真的是问题出在这里,个人建议标志位要设定的唯一且有规律,比如CRC校验,每一副图片的CRC一定不同(相同你就中奖了),然后把CRC在放头包和尾包,这样就找到了。