C#Socket异步通信在处理批量并发时有时出现数据重叠

Jeson_Yang 2013-12-12 01:50:15
代码如下:当并发数量较大时,会出现数据冲突,求改进方案
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Collections.Specialized;
using System.Threading;

namespace SocketServer
{
public partial class frmMain : Form
{
static int count = 0;
static int recvCount = 0;

public frmMain()
{
InitializeComponent();
}

private delegate void ShowMsgHandler(string msg);

IList<Socket> clientList = new List<Socket>();
Dictionary<Socket, System.Threading.Timer> clientArray = new Dictionary<Socket, System.Threading.Timer>();

private void ShowMsg(string msg)
{
if (!txtContent.InvokeRequired)
{
txtContent.Text = msg + Environment.NewLine + txtContent.Text;
}
else
{
ShowMsgHandler handler = new ShowMsgHandler(ShowMsg);
BeginInvoke(handler, new object[] { msg });
}

}

private void btnStartServer_Click(object sender, EventArgs e)
{
IPAddress[] localIPs;
localIPs = Dns.GetHostAddresses(Dns.GetHostName());
StringCollection IpCollection = new StringCollection();
foreach (IPAddress ip in localIPs)
{
if (ip.AddressFamily == AddressFamily.InterNetwork) //如果为IPv4
{
if (Int32.Parse(txtPort.Text.ToString()) != 0)
{
TcpListener listener = new TcpListener(ip, Int32.Parse(txtPort.Text.ToString()));
listener.Start();
listener.BeginAcceptTcpClient(new AsyncCallback(OnAcceptedTcpClient), listener);
}
else
{
TcpListener listener = new TcpListener(ip, 6000);
listener.Start();
listener.BeginAcceptTcpClient(new AsyncCallback(OnAcceptedTcpClient), listener);
}


txtContent.Text = "Start Listiner...";
btnStartServer.Enabled = false;
ShowMsg(ip.ToString());
break;
}
}
}

byte[] buffer = new byte[1024];

public void TimerCallback(object state)
{

Socket sock = (Socket)state;
if (clientArray.Remove(sock))
{
ShowMsg("TimerCallback");
}
sock.Close(100);
}

private void OnAcceptedTcpClient(IAsyncResult ar)
{
if (!ar.IsCompleted)
{
return;
}
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
System.Threading.Timer time = new System.Threading.Timer(TimerCallback, client.Client, 20000, 10000);
clientArray.Add(client.Client, time);

ShowMsg("连接成功 ");
count++;
listener.BeginAcceptTcpClient(new AsyncCallback(OnAcceptedTcpClient), listener);
{
client.Client.BeginReceive(buffer, 0, 1024, SocketFlags.None, new AsyncCallback(OnReceivedData), client.Client);
}

}

private void OnReceivedData(IAsyncResult ar)
{


if (!ar.IsCompleted)
{
return;
}
Socket s = (Socket)ar.AsyncState;
try
{
int size = s.EndReceive(ar);
if (size > 0)
{
{
string str = System.Text.Encoding.Default.GetString(buffer, 0, size);
ShowMsg(str);
}

recvCount++;
clientArray.Remove(s);
s.Shutdown(SocketShutdown.Both);
s.Close(100);
}
else
{
clientArray.Remove(s);
s.Close(100);

return;
}
// s.BeginReceive(buffer, 0, 1024, SocketFlags.None, new AsyncCallback(OnReceivedData), s);
}
catch (SocketException )
{
clientArray.Remove(s);

s.Close(100);
}

}

private void btnSend_Click(object sender, EventArgs e)
{
byte[] msg = System.Text.Encoding.Unicode.GetBytes(txtMsg.Text.Trim());
foreach (Socket s in clientList)
{
s.BeginSend(msg, 0, msg.Length, SocketFlags.None, new AsyncCallback(OnSendMsg), s);
}
}

private void OnSendMsg(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
return;
}
Socket s = (Socket)ar.AsyncState;
s.EndSend(ar);
}

private void TotleCount_Click(object sender, EventArgs e)
{
ShowMsg("连接数:" + count.ToString() +" 收包数:" + recvCount + " 现连接: " + /*clientList.Count*/clientArray.Count);
}
}
}
...全文
353 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
cheery_an 2013-12-12
  • 打赏
  • 举报
回复
学习学习下!
请叫我卷福 2013-12-12
  • 打赏
  • 举报
回复
引用 8 楼 yc7369 的回复:
[quote=引用 7 楼 xiaozhi_5638 的回复:] 如果前后两个连接数据同时到达 写入缓冲区后 就会覆盖 总有一个连接的EndReceive()不会返回 或者返回了 size也为零 你不要以为客户端按顺序先后登录发送数据 它们到达服务器的顺序就是一定是先后顺序的 所以buffer用同一个没事儿 不一定
到达服务器的这个顺序确实是未知的, 看样子C# OnDatarecv回调 + EndReceive对于批量并发共用一段缓冲区还是不靠谱的,那我还是改成每个Socket自带缓冲区的吧,这样这个问题确实是没了[/quote] 从来都不应该这样用
hunter103 2013-12-12
  • 打赏
  • 举报
回复
clientArray add remove 时 lock试试
hunter103 2013-12-12
  • 打赏
  • 举报
回复
对clientArray 操作不是多线程安全的
Jeson_Yang 2013-12-12
  • 打赏
  • 举报
回复
引用 7 楼 xiaozhi_5638 的回复:
如果前后两个连接数据同时到达 写入缓冲区后 就会覆盖 总有一个连接的EndReceive()不会返回 或者返回了 size也为零 你不要以为客户端按顺序先后登录发送数据 它们到达服务器的顺序就是一定是先后顺序的 所以buffer用同一个没事儿 不一定
到达服务器的这个顺序确实是未知的, 看样子C# OnDatarecv回调 + EndReceive对于批量并发共用一段缓冲区还是不靠谱的,那我还是改成每个Socket自带缓冲区的吧,这样这个问题确实是没了
请叫我卷福 2013-12-12
  • 打赏
  • 举报
回复
如果前后两个连接数据同时到达 写入缓冲区后 就会覆盖 总有一个连接的EndReceive()不会返回 或者返回了 size也为零 你不要以为客户端按顺序先后登录发送数据 它们到达服务器的顺序就是一定是先后顺序的 所以buffer用同一个没事儿 不一定
请叫我卷福 2013-12-12
  • 打赏
  • 举报
回复
引用 3 楼 yc7369 的回复:
[quote=引用 2 楼 xiaozhi_5638 的回复:] 1.服务端accept一个client后 不能使用同一个buffer接收数据 2.数据接收返回后,buffer不一定是完整的数据
对于1这个我这是想简单的测试下,避免紊乱我可以为每个连接上的对象new一个buff,用完销毁丢到Socket字典中的, 2现在只假设一下数据流小于1024,这样数据会是完整的,但是有的数据出现多次,有的数据不出现这个问题实在不知道到底是为什么出现的[/quote] 1这是为了测试什么? 所有的连接往一个缓冲区中写数据 什么结果
Jeson_Yang 2013-12-12
  • 打赏
  • 举报
回复
引用 4 楼 huanggreat 的回复:
协议要订好 约好头跟尾
这个应该不是 协议相关的问题吧,比如100个客户并发,确实是收到了100个数据包,就代表应该是没有掉包的,但是在显示时候一个包显示了两次,另外一个包没显示
  • 打赏
  • 举报
回复
协议要订好 约好头跟尾
Jeson_Yang 2013-12-12
  • 打赏
  • 举报
回复
引用 2 楼 xiaozhi_5638 的回复:
1.服务端accept一个client后 不能使用同一个buffer接收数据 2.数据接收返回后,buffer不一定是完整的数据
对于1这个我这是想简单的测试下,避免紊乱我可以为每个连接上的对象new一个buff,用完销毁丢到Socket字典中的, 2现在只假设一下数据流小于1024,这样数据会是完整的,但是有的数据出现多次,有的数据不出现这个问题实在不知道到底是为什么出现的
请叫我卷福 2013-12-12
  • 打赏
  • 举报
回复
1.服务端accept一个client后 不能使用同一个buffer接收数据 2.数据接收返回后,buffer不一定是完整的数据
Jeson_Yang 2013-12-12
  • 打赏
  • 举报
回复
有时候100个客户端同时发送数据,收到的数据有时显示正常,有时候将95号客户端的显示两次,96号客户端的没显示,这个只是只要连接成功了发送数据后就直接断开的的测试程序,虽然说buff是同一个,但是我想知道原因到底在哪

110,537

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧