异步socket服务器端,如何实现多个客户端连接

dz_uc 2010-10-13 06:07:12
我有多个读卡器,不停的往服务器发送数据,如何通过Socket异步模式接受到读卡器的信息,我下面这段服务器代码只能接受一个读卡器,就是首先连接的那个读卡器的数据。如何实现多个连接?

相关代码


namespace WindowsApplication1
{
public partial class Form1 : Form
{


Socket serverSocket;
Socket clientSocket;
byte[] data = new byte[1300];

public Form1()
{
InitializeComponent();
}


private void button1_Click(object sender, EventArgs e)
{


Listen();

}

private void Listen()
{
int port;
this.button1.Enabled = false;

try
{
port = Convert.ToInt32(textBox1.Text.Trim());
}
catch
{
MessageBox.Show("您输入的端口号码格式不正确,请重新输入!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
textBox1.Text = "";
return;
}

//IPAddress addr = Dns.GetHostAddresses(Dns.GetHostName())[0];
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);

serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

serverSocket.Bind(ipEndPoint);
serverSocket.Listen(10);


serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);


}

private void AcceptCallBack(IAsyncResult ar)
{

try
{

listBox1.Items.Add("检测到一次设备访问接入");
Socket oldSocket = (Socket)ar.AsyncState;

clientSocket = oldSocket.EndAccept(ar);

listBox1.Items.Add("与访问的设备建立连接成功");

this.button1.Enabled = false;

clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);




}
catch
{
//clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);
listBox1.Items.Add("检测到一次设备访问接入,但是接入失败");
}

}

private void SendMessage(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;

int send = clientSocket.EndSend(ar);

clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);
}
catch
{
clientSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
}
}

private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
IPEndPoint ClientIp = client.RemoteEndPoint as IPEndPoint;
listBox1.Items.Add(ClientIp.Address);

int receiveDataLength = client.EndReceive(ar);


string str = System.Text.Encoding.ASCII.GetString(data);

if (!string.IsNullOrEmpty(str))
{
ConvertToData(data);


//serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);

clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);

}
}
catch
{
clientSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
}
}




private void ConvertToData(byte[] data)
{

int len = data.Length;
string str = "";
string s = "";


for(int i = 0;i<len;i++)
{
s = data[i].ToString("X").PadLeft(2, '0');
str = str + s;
}
listBox1.Items.Add(str);
Counting2(data,str);
//Listen();



}

private void Form1_Load(object sender, EventArgs e)
{
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
this.timer1.Enabled = true;
}

private void button2_Click(object sender, EventArgs e)
{

}

private void timer1_Tick(object sender, EventArgs e)
{
//this.dataGridView1.Rows.Clear();

//foreach (KeyValuePair<string, int> kvp in cardcount)
//{
// this.dataGridView1.Rows.Add(1);
// this.dataGridView1.Rows[this.dataGridView1.Rows.Count-1].Cells[0].Value=this.dataGridView1.Rows.Count.ToString().Trim();
// this.dataGridView1.Rows[this.dataGridView1.Rows.Count - 1].Cells[1].Value = kvp.Key.Trim();
// this.dataGridView1.Rows[this.dataGridView1.Rows.Count - 1].Cells[2].Value = kvp.Value.ToString().Trim();
//}

this.dataGridView1.Rows.Clear();
foreach (KeyValuePair<string,int> var in cardcount)
{
dataGridView1.Rows.Add(1);
dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells[0].Value = dataGridView1.Rows.Count.ToString();
dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells[1].Value = var.Key;
dataGridView1.Rows[dataGridView1.Rows.Count - 1].Cells[2].Value = var.Value;
}
}

Dictionary<int, string> cardlist = new Dictionary<int, string>();
Dictionary<string, int> cardcount = new Dictionary<string, int>();


private void Counting2(byte[] data, string str)
{
if (str.IndexOf("430000000000") > 0)
{

int start = str.IndexOf("430000000000");

while (true)
{

string str1 = str.Substring(start, 16);
if (str1.StartsWith("43"))
{


if (cardcount.ContainsKey(str1))
{
cardcount[str1] = cardcount[str1] + 1;
}
else
{
cardcount.Add(str1, 1);
}
}

start++;
if (start > 1284)
{
break;
}
}


}
}

private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{

}



}
}


...全文
1463 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
kenriy 2011-11-14
  • 打赏
  • 举报
回复
private void DoThread()
{

while (true)
{
allDone.Reset();
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
allDone.WaitOne();

}
}

搞不明这里干啥的
niulei 2011-07-23
  • 打赏
  • 举报
回复
受教了,我也搞这些东西,学起来有点费劲!
xu_2007 2011-07-04
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 sp1234 的回复:]
另外,随便说两个题外话(你的程序的明显问题绝不止2个):

1. 编写软件的目的不是应付差事,而是要首先在心里与想到如何测试,然后才去编写出代码好让心里的测试可以通过。而这些心理的测试,最基本地,是可以以任意次序、重复任意次的!而你的button1如果被连续按下两次,或者与其他用户操作打乱次序被操作,会不会抛出异常?!如果你心里有测试,就应该在写代码时直接让这个测试通过,而不应该有这么明显的b……
[/Quote]

第二点说的得很对,还有一些丢包,粘包等现象都应该想到。其实楼主测试的时候早就应该发现这个问题,当发送数据的时候,比如你发送了:“123456”字符串,但可能第一次只会接收到“123”,余下的“456”会在第二次收到,还有更多的情况,所以楼主最后是把这个问题搞清(自定协议)再去做下面的事吧,要不然当会出现非常多你意想不到的问题!
xu_2007 2011-07-04
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 sp1234 的回复:]
不要搞什么“信号量阻塞”编程。你既然使用异步多线程编程,那么使用阻塞不就成了自欺欺人、拿异步代码做着同步的工作了了嘛。

在你的 AcceptCallBack 方法的 clientSocket.BeginReceive(...)之后,不应该结束,而应该再写上一行
serverSocket.BeginAccept 代码。异步编程就是这么简单,根本不需要什么 while 循环,什么ManualR……
[/Quote]

又是SP1234大侠,我认为SP1234大侠回答的很有特色,也很明确。如果你用的是.NET3.5以上版本的话,请使用异步完成端口模型(socketasynceventargs事件类+内存池+asyncXXXX开头的套接字方法),那性能才叫强劲!
dajie13 2011-07-04
  • 打赏
  • 举报
回复
学习啦
  • 打赏
  • 举报
回复
另外,随便说两个题外话(你的程序的明显问题绝不止2个):

1. 编写软件的目的不是应付差事,而是要首先在心里与想到如何测试,然后才去编写出代码好让心里的测试可以通过。而这些心理的测试,最基本地,是可以以任意次序、重复任意次的!而你的button1如果被连续按下两次,或者与其他用户操作打乱次序被操作,会不会抛出异常?!如果你心里有测试,就应该在写代码时直接让这个测试通过,而不应该有这么明显的bug。

2. 第二个问题可能你不太清楚基础知识。你设置了1300字节的buffer,能够保证客户端一次仅传送少于这么多字节的数据吗?即使可以保证客户端不会传送多于1300字节的数据,实际上当网络非常不好的时候(如果有人在你旁边使用类似“bt终结者”之类的ARP入侵软件之类的干扰网络正常流量的软件时),比如客户端只是发送1200字节,服务器端也可能一次收不全,比如第一次receive只能收到1100个字节,然后下一次异步回调才能收到剩下的100个字节。所以真正的通讯程序是将receice到的字节累积起来,然后等到看到了信令协议中作为消息结束的标记时,才把累积起来的所有字节取出来,这才进行Encoding.xxx.GetString(data)的操作。而你的程序作为一个给第一次编程的人的简单demo也许可以,如果你在网络上见到这种“范例”程序可千万别以为它是正规程序员经过检验的程序。
  • 打赏
  • 举报
回复
而你的 ReceiveMessage 方法中 catch{}中的那句代码显然是画蛇添足的。服务器一个客户端断开了,跟其它客户端能不能 Accept进来没有关系。当一个客户端与服务器通讯(服务器端Receive),服务器端当然可以并行处理对其它客户端连接 Accept 操作,所以是没有先后顺序的。
  • 打赏
  • 举报
回复
不要搞什么“信号量阻塞”编程。你既然使用异步多线程编程,那么使用阻塞不就成了自欺欺人、拿异步代码做着同步的工作了了嘛。

在你的 AcceptCallBack 方法的 clientSocket.BeginReceive(...)之后,不应该结束,而应该再写上一行
serverSocket.BeginAccept 代码。异步编程就是这么简单,根本不需要什么 while 循环,什么ManualResetEvent 阻塞之类的代码。
开发者孙小聪 2011-05-25
  • 打赏
  • 举报
回复
还没结贴吧 ??? 问题解决了么????
yjl49 2010-10-25
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 senyi168 的回复:]
你只要把BeginAccetp()放到while循环里面它就会循环监控进来的连接,你上面代码没有把BeginAccetp放到循环里,所以只能连接一次.
http://http://www.cnblogs.com/JimmyZhang/archive/2008/09/07/1286301.html
这里有一个很好的解讲!
[/Quote]
谢谢,代码太粗糙,希望楼主能理解意思。
senyi168 2010-10-21
  • 打赏
  • 举报
回复
你只要把BeginAccetp()放到while循环里面它就会循环监控进来的连接,你上面代码没有把BeginAccetp放到循环里,所以只能连接一次.
http://http://www.cnblogs.com/JimmyZhang/archive/2008/09/07/1286301.html
这里有一个很好的解讲!
yjl49 2010-10-18
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 dz_uc 的回复:]
引用 7 楼 yjl49 的回复:
个人觉得你是不是应该建一个socket 列表,然后有设备连接进来的时候就new 一个client socket加入到列表中,然后从这个client socket 接收数据(最好是能把这个socket包装下,每一个client socket都能有自己独立的线程来接收数据)。

我也觉得这样很好,但是不知道代码如何写,我认为用同步多线程也是可以的,各位大哥有没……
[/Quote]
完整的例子比较复杂,所有的连接都是需要多层包装的,并与线程绑在一起。写个简单的你参考下:

Socket m_Soket;
//这就是那个列表用来保存接收到的client连接
ArrayList a_acceptedSockt;


public void Server_Listen()
{
//如何建立server连接就不写了,总之使用前把m_Socket初始化好就行了。
Socket clientSockt = m_Soket.Accept();
//这个Add函数自己写,注意线程互斥。
a_acceptedSockt.Add();

///这个函数就是用来将接收到的连接进行包装,用来收发数据
AfterConnect(clientSockt);
}


publice AfterConnect(Sockt clientSockt)
{
//这里就是一个对clinet 连接进行包装的线程类,有start方法需要你自己实现,目的还是接收数据。
//一般来讲你只要持有了那个列表那你就可以得到所有已连接的clientSocket,然后就想干什么干什么
//自由发挥了。
Thread_clientThre clientThre = new Thread_clientThre(clientSockt);
clientThre.start();
}


dz_uc 2010-10-15
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 yjl49 的回复:]
个人觉得你是不是应该建一个socket 列表,然后有设备连接进来的时候就new 一个client socket加入到列表中,然后从这个client socket 接收数据(最好是能把这个socket包装下,每一个client socket都能有自己独立的线程来接收数据)。
[/Quote]
我也觉得这样很好,但是不知道代码如何写,我认为用同步多线程也是可以的,各位大哥有没有参考的例子啊
yjl49 2010-10-14
  • 打赏
  • 举报
回复
个人觉得你是不是应该建一个socket 列表,然后有设备连接进来的时候就new 一个client socket加入到列表中,然后从这个client socket 接收数据(最好是能把这个socket包装下,每一个client socket都能有自己独立的线程来接收数据)。
dz_uc 2010-10-14
  • 打赏
  • 举报
回复
我改进了一下代码,可以接受多个,但是没法同同时接受多个设备,只能一个设备接受很长时间后,另一个设备才连接上
namespace WindowsApplication1
{
public partial class Form1 : Form
{


Socket serverSocket;
Socket clientSocket;
byte[] data = new byte[1300];

public Form1()
{
InitializeComponent();
}

public static ManualResetEvent allDone = new ManualResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
int port;
this.button1.Enabled = false;

try
{
port = Convert.ToInt32(textBox1.Text.Trim());
}
catch
{
MessageBox.Show("您输入的端口号码格式不正确,请重新输入!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
textBox1.Text = "";
return;
}

//IPAddress addr = Dns.GetHostAddresses(Dns.GetHostName())[0];
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);

serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

serverSocket.Bind(ipEndPoint);
serverSocket.Listen(10);

Thread tt = new Thread(DoThread);
tt.Start();


}

private void DoThread()
{

while (true)
{
allDone.Reset();
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
allDone.WaitOne();

}
}

private void AcceptCallBack(IAsyncResult ar)
{
allDone.Set();
// listBox1.Items.Add("检测到一次设备访问接入");

try
{
listBox1.Items.Add("检测到一次设备访问接入");
Socket oldSocket = (Socket)ar.AsyncState;

clientSocket = oldSocket.EndAccept(ar);
listBox1.Items.Add("与访问的设备建立连接成功");

this.button1.Enabled = false;

clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);




}
catch
{
//clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);
listBox1.Items.Add("检测到一次设备访问接入,但是接入失败");
}

}

private void SendMessage(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;

int send = clientSocket.EndSend(ar);

clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);
}
catch
{
clientSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
}
}

private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;

int receiveDataLength = client.EndReceive(ar);
IPEndPoint ip = client.RemoteEndPoint as IPEndPoint;

listBox1.Items.Add(ip.Address);
string str = System.Text.Encoding.ASCII.GetString(data);

if (!string.IsNullOrEmpty(str))
{
ConvertToData(data);
clientSocket.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);
}
}
catch
{
clientSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
}
}




private void ConvertToData(byte[] data)//处理数据,无关系


机器人 2010-10-13
  • 打赏
  • 举报
回复
分布式的Unit测试,建议你看看PNUnit
dz_uc 2010-10-13
  • 打赏
  • 举报
回复
谢谢各位,从哪里加While循环啊?
laj0600310219 2010-10-13
  • 打赏
  • 举报
回复

IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipEndPoint);
serverSocket.Listen(10);

将上面这段代码放在try{ }语句里面试试看。
bobyisland 2010-10-13
  • 打赏
  • 举报
回复
有连接进来时,重新new一个新的socket。让老的socket继续监听。在while循环里面做。MSDN上有例子的~

110,577

社区成员

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

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

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