异步TCP调用BeginReceive和EndAccept有时候会产生异常
首先说一下我的代码的大概结构1.程序是CS架构的。客户端负责把有硬件采集到的数据发到服务器,当服务器接收到某个客户端发来的数据(结构体来的),把接收到的数据推进一个队列,有专门的线程循环检测队列中是否有数据,有数据就取数据更新图形界面,并且再分发到其他客户端,客户端接收到来自服务器发来的数据会更新本客户端的图形界面,从而能看到别的客户端的实时情况。目前有16个客户端同时接入服务器。
2.服务器和客户端都是用.Net2.0和C#开发,通信部分都采用异步Socket,代码的结构大致相同,下面会给出代码。
3.服务器端代码主要的函数有:
(1)initListener(),初始化listen的Socket,并启动异步接收客户端连接(这里没有采用while循环来Accept,而是在BeginAccept的回调函数中再次调用BeginAccept)。
(2)OnConnectReuqest(),作为BeginAccept的回调函数,当异步接收到一个客户端连接时调用该函数,在该函数中再次调用lietener.BeginAccept(),继续接受下一个连接
(3)OnReceiveData()函数接收来自客户端的操作并且执行一系列的操作
4.一些自定义类型的解释
TrackStateStr是我自定义的结构体类型
SocketChatClient类封装了一个Socket和接收缓冲区,当接受了一个客户端的连接时候,把与客户端通信的Socket封装到该类中,该类提供了SetupReceiveCallback方法调用BeginReceive异步接收数据(回调函数就是OnReceiveData)
出现的问题:
1.程序运行久了(大概一天不到),会在OnConnectRequest()函数的SockChatClient client = new SockChatClient(listener.EndAccept(ar), this)这一句出现异常,提示只能对一个异步操作调用一次EndAccept,不知道是由什么引起的。
2.运行了一段时间(也是大概一天不到)在SockChatClient类的SetupReceiveCallback()会出现异常,应该是调用BeginReceive失败了,提示要 访问的资源已经释放,应该是在调用的时候Socket已经被释放了。不知道是不是由于客户端的关闭引起的。但是我在OnReceiveData函数里面有捕获客户端关闭的异常。
3.出现了一个异常提示:集合已修改;可能无法执行枚举操作。应该是在SetTrackStateCall()这个函数中出现的异常,不知道是因为什么。SetTrackStateCall()是循环检测接收队列里面是否有数据并进行相应处理的函数。
主要就是以上3个问题,请各位对Socket方面有丰富经验的大大门帮我看看问题是在哪,关键代码我都贴在下面。
服务端异步TCP的主要代码:
Socket listener;
int listenPort;
public bool isRun;
Type trackStateType = (new TrackStateStr()).GetType();
public ArrayList m_clientSockList = new ArrayList();
Dictionary<string, int> m_IPIndex;
object curAllTrackStateLocker = new object();
public Queue<TrackStateStr> recvTrackStateQueue = new Queue<TrackStateStr>(100);
public ManualResetEvent manualResetEvent = new ManualResetEvent(true);
AutoResetEvent writeHandler = new AutoResetEvent(true);
Thread setTrackTrackThread;
public void initListener()
{
this.listenPort = (int)Port.ServerListenPort;
IPEndPoint iep = new IPEndPoint(IPAddress.Parse(MyIP), this.listenPort);
this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.listener.Bind(iep);
this.listener.Listen(16);
this.listener.BeginAccept(new AsyncCallback(OnConnectRequest), this.listener);
this.isRun = true;
}
private void OnConnectRequest(IAsyncResult ar)
{
if (!isRun)
return;
Socket listener = (Socket)ar.AsyncState;
SockChatClient client = new SockChatClient(listener.EndAccept(ar), this);
if (this.IsHandleCreated)
{
int stationIndex = 0;
foreach (KeyValuePair<int, string> kvp in IPArray)
{
if (kvp.Value == ((IPEndPoint)client.Sock.RemoteEndPoint).Address.ToString())
{
stationIndex = kvp.Key;
break;
}
}
if (stationIndex != 0)
{
Invoke(this.m_AddToMsgTips, new string[] { this.stationName[stationIndex] + " 成功连接到服务器" });
client.StationName = this.stationName[stationIndex];
client.StationId = stationIndex;
Invoke(this.m_SetZColor, new object[] { _ZArray[stationIndex], _connectedColor });
m_clientSockList.Add(client);
client.SetupReceiveCallBack();
}
else
{
Invoke(this.m_AddToMsgTips, new string[] { "unknown client join!" });
client.Sock.Close();
//client.StationName = "unknow client";
//client.StationId = 0;
}
}
listener.BeginAccept(new AsyncCallback(OnConnectRequest), listener);
}
object queueLocker = new object();
public void OnReceiveData(IAsyncResult ar)
{
if (!isRun)
return;
SockChatClient client = (SockChatClient)ar.AsyncState;
byte[] recvData = client.GetReceiveData(ar);
try
{
if (recvData.Length < 1)
{
//收到的字节数为0,远程机器可能已经断开连接
if (this.IsHandleCreated)
{
this.Invoke(this.m_AddToMsgTips, new string[] { "与 " + client.StationName + " 的连接断开" });
if (client.StationId != 0)
{
if (this.stationCtrlState[client.StationId] == 2)
this._twinklingTimerArr[client.StationId].Stop();
this.Invoke(this.m_SetZColor, new object[] { _ZArray[client.StationId], _disconnectedColor });
}
}
this.stationCtrlState[client.StationId] = 0;
client.Sock.Close();
this.m_clientSockList.Remove(client);
return;
}
TrackStateStr newTrackState = (TrackStateStr)this.BytesToStruct(recvData, this.trackStateType);
Invoke(m_AddToMsgTips, new string[] { "from " + client.StationName });
//writeHandler.WaitOne();
lock (queueLocker)
{
recvTrackStateQueue.Enqueue(newTrackState);
}
//writeHandler.Set();
}
catch (Exception er)
{
this.Invoke(this.m_AddToMsgTips, new string[] { er.Message });
}
client.SetupReceiveCallBack();
}
int j = 0;
private void SetTrackStateCall()
{
while (true)
{
manualResetEvent.WaitOne();
//manualResetEvent.Reset();
lock (queueLocker)
{
if (recvTrackStateQueue.Count > 0)
{
try
{
TrackStateStr newTrackState = recvTrackStateQueue.Dequeue();
//更新当前状态
Invoke(m_UpdateCurTrackState, new object[] { newTrackState });
//回发到各个车站
SendBackToCli();
//设置控件状态
Invoke(m_SetTrackState, new object[] { newTrackState });
for (int i = 0; i < 16; i++)
{
this._curAllTrackState.Z[i].modified = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, DateTime.Now + ":From SetTrackStateCall");
}
}
}
//manualResetEvent.Set();
Thread.Sleep(10);
}
}
/// <summary>
/// 回发全线状态到所有客户端
/// </summary>
public void SendBackToCli()
{
byte[] sendData = StructToBytes(this._curAllTrackState);
foreach (SockChatClient cli in this.m_clientSockList)
{
try
{
cli.Sock.Send(sendData);
}
catch
{
cli.Sock.Close();
m_clientSockList.Remove(cli);
//return;
}
}
}
#endregion
SockChatClient的代码
class SockChatClient--------------------编程问答-------------------- 1.程序运行久了(大概一天不到),会在OnConnectRequest()函数的SockChatClient client = new SockChatClient(listener.EndAccept(ar), this)这一句出现异常,提示只能对一个异步操作调用一次EndAccept,不知道是由什么引起的。
{
private Socket m_sock;
public Socket Sock
{
get { return m_sock; }
}
private string m_stationName;
public string StationName
{
get { return m_stationName; }
set { m_stationName = value; }
}
private int m_stationId;
public int StationId
{
get { return m_stationId; }
set { m_stationId = value; }
}
private byte[] m_buff;
private int m_bufSize;
ServerForm frmMain;
AsyncCallback receiveData;
public SockChatClient(Socket sock, ServerForm frmMain)
{
this.m_sock = sock;
this.m_bufSize = Marshal.SizeOf((new TrackStateStr()).GetType());
this.frmMain = frmMain;
receiveData = new AsyncCallback(this.frmMain.OnReceiveData);
}
public void SetupReceiveCallBack()
{
try
{
m_buff = null;
m_buff = new byte[m_bufSize];
//AsyncCallback receiveData = new AsyncCallback(frmMain.OnReceiveData);
m_sock.BeginReceive(m_buff, 0, m_buff.Length, SocketFlags.None, receiveData, this);
}
catch (Exception ex)
{
MessageBox.Show("Setup receive callback fail! " + ex.Message, DateTime.Now + ":From Sock " + this.m_stationId + "," + m_stationName);
}
}
public byte[] GetReceiveData(IAsyncResult ar)
{
int nRecvBytes = 0;
try
{
nRecvBytes = m_sock.EndReceive(ar);
}
catch
{
}
byte[] bytesReturn = new byte[nRecvBytes];
Array.Copy(m_buff, bytesReturn, nRecvBytes);
return bytesReturn;
}
}
----------------------------------------------------
那说明你在listener调用2次EndAccept,你把检查检查你的代码。
2.运行了一段时间(也是大概一天不到)在SockChatClient类的SetupReceiveCallback()会出现异常,应该是调用BeginReceive失败了,提示要访问的资源已经释放,应该是在调用的时候Socket已经被释放了。不知道是不是由于客户端的关闭引起的。但是我在OnReceiveData函数里面有捕获客户端关闭的异常。
--------------------------------------------------------------
那说明Socket已经关闭,那么你在调用之前,应该先判断一下是否为NULL,是否处于开打状态或者其他你需要的状态。
3.出现了一个异常提示:集合已修改;可能无法执行枚举操作。应该是在SetTrackStateCall()这个函数中出现的异常,不知道是因为什么。SetTrackStateCall()是循环检测接收队列里面是否有数据并进行相应处理的函数。
----------------------------------------------
那说明,你在某些foreach的地方,集合是被修改了。那么你应该改成for的形式。
以上是个人觉得可能出错的地方。你可以参考的找找问题。 --------------------编程问答-------------------- 首先先谢谢xray2005的回答
(1)代码我检查过了,按我的代码的执行流程,是不会对同一个BeginAccept执行两次EndAccept的
我的代码的执行流程是这样的
a. 程序开始先执行initListener(),对监听Socket进行初始化,并调用BeginAccept()
b. OnConnectRequest函数作为BeginAccept()的回调函数被调用,当有客户端接入,调用OnConnectRequest,在OnConnectRequest函数里会先调用一次EndAccept结束前一次的异步Accept,然后再次调用监听Socket的BeginAccept(监听的Socket是通过OnConnectRequest的IAsyncResult ar参数进行传递的)
c. 当又有客户端接入,会重复上述b的执行流程。
所以我觉得按这样的执行流程应该不会对同一个BeginAccept执行两次EndAccept操作的,不知道是什么引起调用两次了,求解答!
(2)如何判断一个Socket是否已经被释放了?我在释放的时候是调用Socket.Close()的,没有赋值为NULL,所以判断是否为NULL应该不行
(3)这个我似乎找到问题所在了
我是第一次用Socket来写程序,所以有很多地方都不懂,希望各位帮帮忙,项目比较急。。。。 --------------------编程问答-------------------- 没人吗?自己顶起啊
第三个问题应该解决了,还有前两个问题。
什么情况会引起Socket的连接断开呢,昨天测试有一个客户端与服务端的连接断开了,但是客户端程序是没有关掉的,就是突然间与服务端的连接断开了 --------------------编程问答-------------------- 没有其他人帮忙吗??
自己顶起 --------------------编程问答-------------------- 菜鳥,走過
补充:.NET技术 , C#