求解决socket连接异常导致断线

whj518 2009-03-05 10:15:52
socket多客户端连接,接收数据或反序列化时偶尔发生异常导致断线,异常是偶尔发生,不是每次都出现。求各位帮忙解答,由于系统只给我送100分,解决实际问题的还可再加分。

服务器代码如下:
public void NetWorkServer()
{
IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName())[0];
//定义socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//定义端点
IPEndPoint ep = new IPEndPoint(ip, 2008);
//绑定端点
socket.Bind(ep);
//最大监听数
socket.Listen(connectNum);
//开启线程监听
listenThread = new Thread(new ThreadStart(listenSocket));
listenThread.IsBackground = true;
listenThread.Start();

}
/// <summary>
/// 监听
/// </summary>
private void listenSocket()
{
while (true)//不停的监听客户端连接请求
{
try
{
chatSocket = socket.Accept();
//开辟线程处理
Thread newChatThread = new Thread(new ThreadStart(beginChat));
newChatThread.IsBackground = true;
newChatThread.Start();
}
catch (Exception ex)
{
log.Error("监听失败");
}
}
}
/// <summary>
/// 接收数据
/// 处理
/// </summary>
private void beginChat()
{
Socket s = chatSocket;
string ep = s.RemoteEndPoint.ToString();//远程ip+端口
string[] str = ep.Split(':');
lock (socketsMap)
{
if (!socketsMap.ContainsKey(str[0]))
{
socketsMap.Add(str[0], s); //保存用户socket
}
else
{
socketsMap[str[0]] = s;
}
}
BinaryFormatter format = new BinaryFormatter();
MemoryStream mem = new MemoryStream();
while (true)
{
#region 方法一,直接从网络流中读取数据
/*
NetworkStream stream = null;
try
{
stream = new NetworkStream(s);
}
catch (IOException)
{
log.Error("客户端关闭");
return;
}
try
{
NetWordMsg msg = (NetWordMsg)format.Deserialize(stream);//这里偶尔出现“在分析完成之前就遇到流结尾”异常
//处理信息,引发事件给DoMsg(String msg)
ReveiveEvent(s, msg);
}
catch (Exception ex)
{
lock (socketsMap)
{
socketsMap.Remove(str[0]);
try
{
s.Disconnect(false);
}
catch (Exception)
{
}
}
s.Close();
s = null;
log.Error("格式化信息失败",ex);
return;
}
*/

#endregion

#region 方法二:发送方法是带长度的方法
byte[] data = new byte[4];
try
{
if (s != null && s.Connected)
{
int revc=0;
try
{
revc = s.Receive(data, 4, SocketFlags.None);//读出数据长度
}
catch (SocketException ex)
{
log.Error("连接关闭", ex);
s.Disconnect(false);
s = null;
}
int size = BitConverter.ToInt32(data, 0);//得到数据长度
int offset = 0;
if (size > 0)
{
data = new byte[size];//接收数据数组
revc = s.Receive(data, size, SocketFlags.None);//此方法偶尔出现“连接已关闭”的异常
mem.Write(data, offset, revc);
offset += revc;
size -= revc;
//mem.Position = 0;
mem.Seek(0, SeekOrigin.Begin);
NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
//处理信息,引发事件给DoMsg(String msg)
ReveiveEvent(s, msg);
mem = new MemoryStream();
}
}
}
catch (SocketException ex)
{
lock (socketsMap)
{
socketsMap.Remove(str[0]);
try
{
s.Disconnect(false);
}
catch (Exception)
{
}
}
s.Close();
s = null;
log.Error("格式化信息失败", ex);
continue;
}
#endregion
}
}
...全文
1994 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
whj518 2009-03-16
  • 打赏
  • 举报
回复
今天才明白,socket是无法保持长连接的。系统会根据资源关闭连接。心跳机制也是在SOCKET连接断开后重新连接。
用阻塞模式没必要用心跳。只能在socket异常断开后重新连接。谢谢各位的解答。
czjearth 2009-03-14
  • 打赏
  • 举报
回复

请教大伙一个问题:

使用异步通讯时, 

客户端的每一次接收, 会不会能保证到把服务器的每一次发送数据都接收完呢?

如果不能保证,怎么去判断客户端接收完服务器的一次交互的数据呢?

czjearth 2009-03-13
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 whj518 的回复:]
引用 12 楼 czjearth 的回复:
楼主,


我对你的代码作了测试,

好像并没有你的问题啊。我使用三台主机同时开启多个客户端。

楼主能把所有相关代码都发出来吗?




没问题,这个异常不是一下子就出现的。有时候半个小时,有时候一个小时。但是只要出现了一次,后面出现就频繁了。我的客户端断开后回自动的连接,所以一天就有十几次。而且不是固定一台断开。
下面我就贴出收发的代码。
[/Quote]



呵呵, 这样的话就比较麻烦了。

whj518 2009-03-12
  • 打赏
  • 举报
回复
服务器发送数据的方法
/// <summary>
/// 发送信息
/// </summary>
/// <param name="ip"></param>
/// <param name="msg"></param>
private void sendMsg(string ip, NetWordMsg msg)
{
#region 原方法
/*
BinaryFormatter format = new BinaryFormatter();
if (ip != null)
{
Socket s = socketsMap[ip] as Socket;
if (s != null)
{
NetworkStream stream = new NetworkStream(s);
try
{
format.Serialize(stream, msg);
stream.Flush();
}
catch (Exception)
{
log.Error("序列化信息失败");
lock (socketsMap)
{
socketsMap.Remove(ip);
}
//RemoFromMachinList(ip);
try
{
s.Disconnect(false);
s.Close();
}
catch (Exception ex)
{
throw ex;
}
}
finally
{
stream.Close();
}
}
}
else//向所有客户发送信息
{
if (socketsMap.Count == 0)
{
return;
}
lock (socketsMap)
{
IDictionaryEnumerator enumerator = socketsMap.GetEnumerator();
Socket temps;
while (enumerator.MoveNext())
{
temps = enumerator.Value as Socket;
if (temps != null)
{
NetworkStream stream = new NetworkStream(temps);
try
{
format.Serialize(stream, msg);
stream.Flush();
}
catch (Exception)
{
log.Error("初始化失败");
continue;
}
finally
{
stream.Close();
}
}
Thread.Sleep(10);//线程暂停10毫秒
}
}
}
*/

#endregion
#region 发送带长度的方法
BinaryFormatter format = new BinaryFormatter();
if (ip != null)
{
Socket s = socketsMap[ip] as Socket;
MemoryStream mem = new MemoryStream();
if (s != null && s.Connected)
{
NetworkStream ns = null;
format.Serialize(mem, msg);
byte[] data = mem.GetBuffer();//把内存流转成字节数组
int memsize = (int)mem.Length;//数据长度
byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
try
{
ns = new NetworkStream(s);
ns.Write(size, 0, 4);
ns.Write(data, 0, memsize);
ns.Flush();
}
catch (Exception ex)
{
lock (socketsMap)
{
socketsMap.Remove(ip);
}
log.Error("连接关闭,无法发送数据!", ex);
s.Disconnect(false);
s.Close();
//s = null;
}
finally
{
mem.Close();
}

}
}
else//向所有客户发送信息
{
if (socketsMap.Count == 0)
{
return;
}
lock (socketsMap)
{
IDictionaryEnumerator enumerator = socketsMap.GetEnumerator();
Socket temps;
while (enumerator.MoveNext())
{
temps = enumerator.Value as Socket;
MemoryStream mem = new MemoryStream();
if (temps != null)
{
NetworkStream ns = null;
format.Serialize(mem, msg);
byte[] data = mem.GetBuffer();//把内存流转成字节数组
int memsize = (int)mem.Length;//数据长度
byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
try
{
ns = new NetworkStream(temps);
ns.Write(size, 0, 4);
ns.Write(data, 0, memsize);
ns.Flush();
}
catch (IOException ex)
{
lock (socketsMap)
{
socketsMap.Remove(temps.RemoteEndPoint.ToString().Split(':')[0]);
}
log.Error("连接关闭,无法发送数据!", ex);
temps.Disconnect(false);
temps.Close();
//temps = null;
}
finally
{
mem.Close();
}
}
Thread.Sleep(50);//线程暂停10毫秒
}
}
}
#endregion

}

socketsMap 是HashTable,用于保存客户Socket,key是ip,value是socket
whj518 2009-03-12
  • 打赏
  • 举报
回复
服务器接收数据方法三(代替方法一、方法二)
这是按照8楼的朋友改进的
#region 方法三

if (s != null && s.Connected)
{
byte[] data = new byte[1024 * 10];//每次接收1024字节
int revc = 0;//实际接收数据大小
try
{
revc = s.Receive(data, 1024 * 10, SocketFlags.None);//读出数据长度
}
catch (Exception ex)
{
//s.Disconnect(false);
s.Shutdown(SocketShutdown.Both);
//s = null;
socketsMap.Remove(str[0]);
log.Error(str[0] + "连接断开", ex);
break;
}
for (int i = 0; i < revc; i++)
{
receBytes.Add(data[i]);
}
revc = 0;
try
{
if (receBytes.Count > 4)//接收完毕
{
byte[] bt = new byte[4];
receBytes.CopyTo(0, bt, 0, 4);
int size = BitConverter.ToInt32(bt, 0);
if (receBytes.Count >= size + 4)//数据接收完整
{
receBytes.RemoveRange(0, 4);
byte[] mess = new byte[size];
receBytes.CopyTo(0, mess, 0, size);
mem.Write(mess, 0, size);
mem.Seek(0, SeekOrigin.Begin);
NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
//处理信息,引发事件给DoMsg(String msg)
ReveiveEvent(s, msg);
mem = new MemoryStream();
receBytes.RemoveRange(0, size);
}
}
}
catch (Exception ex)
{
log.Error("格式化信息失败", ex);
//s.Disconnect(false);
//s = null;
}
}
else {
socketsMap.Remove(str[0]);
log.Error(str[0]+"连接断开");
break;
}

#endregion
whj518 2009-03-12
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 czjearth 的回复:]
楼主,


我对你的代码作了测试,

好像并没有你的问题啊。我使用三台主机同时开启多个客户端。

楼主能把所有相关代码都发出来吗?


[/Quote]

没问题,这个异常不是一下子就出现的。有时候半个小时,有时候一个小时。但是只要出现了一次,后面出现就频繁了。我的客户端断开后回自动的连接,所以一天就有十几次。而且不是固定一台断开。
下面我就贴出收发的代码。
czjearth 2009-03-12
  • 打赏
  • 举报
回复
楼主,


我对你的代码作了测试,

好像并没有你的问题啊。我使用三台主机同时开启多个客户端。

楼主能把所有相关代码都发出来吗?

whj518 2009-03-12
  • 打赏
  • 举报
回复
cup高的问题已经解决,但是socket异常还是一样,我的服务器SOCKET设置了超时为2分钟,服务器最多隔1分钟就会想所有客户端发送数据。但是还是在Receive()这个地方出现“远程主机强迫关闭现有连接”.主要是不知道系统为什么会自动关闭连接,而且捕获不到其它异常。等待各位帮忙解决,谢谢!
whj518 2009-03-06
  • 打赏
  • 举报
回复
还有一个问题是,我断开客户端后出现
在 System.Net.Sockets.SocketException 中第一次偶然出现的“System.dll”类型的异常
“ServerClient.exe”(托管): 已加载“C:\WINDOWS\assembly\GAC_MSIL\System.resources\2.0.0.0_zh-CHS_b77a5c561934e089\System.resources.dll”
在 System.IO.IOException 中第一次偶然出现的“System.dll”类型的异常
在 System.InvalidOperationException 中第一次偶然出现的“mscorlib.dll”类型的异常

但是我没办法捕获到异常,CPU占用率非常高。不知道这么去处理这个问题,希望各位帮忙解答。
whj518 2009-03-05
  • 打赏
  • 举报
回复
客户端原码:
/// <summary>
/// 连接服务器
/// </summary>
private void ConnectToServer()
{

//定义socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//定义端点
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(sip), 2008);
//连接
try
{
socket.Connect(ep);
}
catch (Exception)
{
//Init_DisConnection();
isConnected = false;
}
if (socket.Connected)
{
isConnected = true;
//发送消息,通知服务器
NetWordMsg msg = new NetWordMsg();
msg.Type = "connect";
sendMsg(msg);
//开线程负责接收服务器传来的消息
Thread recThread = new Thread(new ThreadStart(ReceiveMsg));
recThread.IsBackground = true;
recThread.Start();
}
}
/// <summary>
/// 发送信息
/// </summary>
/// <param name="msg"></param>
private void sendMsg(NetWordMsg msg)
{
#region 原方法
/*
if (socket.Connected)
{
NetworkStream stream = null;
try
{
stream = new NetworkStream(socket);
BinaryFormatter format = new BinaryFormatter();

format.Serialize(stream, msg);
stream.Flush();
}
catch {
Init_DisConnection();
}
finally
{
if (stream != null)
{
stream.Close();
}
}
}
else
{
isConnected = false;
}
*/

#endregion
NetworkStream ns = null;
if (socket.Connected)
{
MemoryStream mem = new MemoryStream();
BinaryFormatter format = new BinaryFormatter();
format.Serialize(mem, msg);
byte[] data = mem.GetBuffer();//把内存流转成字节数组
int memsize = (int)mem.Length;//数据长度
byte[] size = BitConverter.GetBytes(memsize);//把长度转成字节数组
try
{
ns = new NetworkStream(socket);
ns.Write(size, 0, 4);
ns.Write(data, 0, memsize);
ns.Flush();
}
catch (Exception)
{
Init_DisConnection();
}
finally
{
mem.Close();
}
}
else
{
isConnected = false;
}

}
/// <summary>
/// 接收信息
/// </summary>
private void ReceiveMsg()
{
#region 原方法
/*
NetworkStream stream = null;
try
{
while (isConnected)
{
stream = new NetworkStream(socket);
BinaryFormatter format = new BinaryFormatter();
NetWordMsg msg = (NetWordMsg)format.Deserialize(stream);
ReveiveEvent(msg); //引发事件给DoMsg(string msg)
}
}
catch (Exception)
{
Init_DisConnection();
}
*/

#endregion
#region 带发送长度方法
BinaryFormatter format = new BinaryFormatter();
MemoryStream mem = new MemoryStream();
NetworkStream ns =null;
while (true)
{
byte[] data = new byte[4];
try
{
int revc=0;
try
{
revc = socket.Receive(data, 4, SocketFlags.None);
ns = new NetworkStream(socket);
}
catch (SocketException ex)
{
log.Error("连接断开", ex);
Init_DisConnection();
break;
}
finally
{
if (ns != null)
{
ns.Dispose();
ns = null;
}
}
int size = BitConverter.ToInt32(data, 0);
int offset = 0;
if (size > 0)
{
data = new byte[size];
revc = socket.Receive(data, size, SocketFlags.None);
mem.Write(data, offset, revc);
offset += revc;
size -= revc;

//mem.Position = 0;
mem.Seek(0, SeekOrigin.Begin);
NetWordMsg msg = (NetWordMsg)format.Deserialize(mem);
ReveiveEvent(msg); //引发事件给DoMsg(string msg)
mem = new MemoryStream();
}
}
catch (IOException ex)
{
log.Error("接收信息失败", ex);
}
}

#endregion
}

发送和接收的数据都是用NetWordMsg类进行封装,发送和接收进行序列化和反序列化,由于代码太长,发送数据部分就不贴出来了。异常总是出现在接收数据那部分。主要的异常是“连接已关闭”。请各位帮忙解决。
whj518 2009-03-05
  • 打赏
  • 举报
回复
万分感谢homejiji的耐心解答,我原来一次读出所有数据后进行反序列化会出现
“在分析完成之前已到流的结尾”异常,这有可能是数据包粘包引起的。现在先按你说的试试先。
homejiji 2009-03-05
  • 打赏
  • 举报
回复
if (s != null && s.Connected)
{
int revc=0;
try
{
revc = s.Receive(data, 4, SocketFlags.None);//读出数据长度
}
catch (SocketException ex)
{
log.Error("连接关闭", ex);
s.Disconnect(false);
s = null;
}
int size = BitConverter.ToInt32(data, 0);//得到数据长度
int offset = 0;
if (size > 0)
{
data = new byte[size];//接收数据数组
revc = s.Receive(data, size, SocketFlags.None);//此方法偶尔出现“连接已关闭”的异常
//你前面的如果读数据长度的时候出错的话,你这个地方当然是连接已经关闭了,因为你前面在catch中作了关闭处理。。。出错的原因可能是因为数据包粘包/
/你应该一次将所有数据接收,然后判断接受数据的长度是否大于4,如果大于4,取出前4位,得到数据长度a,然后判断余下的部分长度是否达到a,如果大于a,取出长度a的数据进行处理,再将多余的数据进行保存用于下一次接收,如果数据长度小于4保存用于下一次接收,将多余的数据与下一次接收的数据和并,在重复一遍上面的操作。。。

就下楼上前几位说的
通过heartbeat等机制来维护连接
心跳包的一个例子
byte[] inOptionValues = new byte[4 * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, 4);//一回送信間隔
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, 8);//二回送信間隔
socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
注意必须是在基础socket上设置
dabaicai11 2009-03-05
  • 打赏
  • 举报
回复
为什么要重新连接呢,不能防止超时吗
whj518 2009-03-05
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 oyljerry 的回复:]
是不是发生超时,断开连接了,通过heartbeat等机制来维护连接,当连接断开了,重新连接
[/Quote]
我也怀疑是超时导致断线,但是我对socket只是初步了解。实在没办法解决。希望各位能帮我修改这部分代码。先谢过了。
zhujiechang 2009-03-05
  • 打赏
  • 举报
回复
socket出现异常很正常,重新连接或者重发就可以了。
楼主只是网络编程这块的经验少了些
oyljerry 2009-03-05
  • 打赏
  • 举报
回复
是不是发生超时,断开连接了,通过heartbeat等机制来维护连接,当连接断开了,重新连接
wenbin 2009-03-05
  • 打赏
  • 举报
回复
连接关闭异常出现后,客户端断开重新连接。
服务器关闭这个连接的所有资源。
采用心跳包机制
whj518 2009-03-05
  • 打赏
  • 举报
回复
急切希望得到各位的指点
我自己顶上去

110,536

社区成员

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

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

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