我用.net 3.5, VS 2008 开发, TCP Socket通讯,递归调用接受信息。在实际项目中发现当 Socket一端关闭(调用Socket.Close())后,另外一端不是报出异常(我期望是如此),而是不断递归调用接受0字节。我整理了下代码,下面Demo能重现此问题。期望有人能给出解释。
服务器端代码 class TestServer
{
static void Main()
{
try
{
TCPServer tcpServer = (TCPServer)TCPServer.GetInstance();
tcpServer.ServerIP = "127.0.0.1";
tcpServer.ServerPort = 3721;
tcpServer.CreatListener();
Console.WriteLine(String.Format("Server is ready with {0}:{1}", tcpServer.ServerIP, tcpServer.ServerPort));
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
Console.WriteLine("Press Enter to Exit"); Console.ReadLine();
}
} public abstract class PacketTransferBase
{
public PacketTransferBase()
{
}
private int count = 0;
/// <summary>
/// 接收信息
/// </summary>
/// <param name="ar"></param>
protected void ReceiveMessage(IAsyncResult ar)
{
try
{
StateObject so = (StateObject)ar.AsyncState;
Socket socket = so.workSocket; //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
var length = socket.EndReceive(ar);
byte[] actualData = new byte[length];
Array.Copy(so.buffer, 0, actualData, 0, length);
Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2])); bool continueReceive = true;
Console.WriteLine(Encoding.UTF8.GetString(actualData));
if (count == 0)
{
socket.Send(Encoding.UTF8.GetBytes("this is a back message from server!"));
count++;
}
socket.Close(); if (continueReceive && socket != null && socket.Connected)
{
socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} /// <summary>
/// TCP服务器端
/// </summary>
public class TCPServer : PacketTransferBase
{
public string ServerIP { get; set; }
public int ServerPort { get; set; }
private static TCPServer _TCPServer = new TCPServer();
Socket socketServer = null;
private TCPServer()
{
} public static PacketTransferBase GetInstance()
{
return _TCPServer;
} /// <summary>
/// 服务器端建立侦听
/// </summary>
public void CreatListener()
{
//创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //将该socket绑定到主机上面的某个端口
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
socketServer.Bind(new IPEndPoint(IPAddress.Parse(ServerIP), ServerPort)); //启动监听,并且设置一个最大的队列长度
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
socketServer.Listen(100); //开始接受客户端连接请求
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
} /// <summary>
/// 接收客户端
/// </summary>
/// <param name="ar"></param>
public void ClientAccepted(IAsyncResult ar)
{
try
{
var socketServer = ar.AsyncState as Socket; //这就是客户端的Socket实例,我们后续可以将其保存起来
var socketClient = socketServer.EndAccept(ar); //接收客户端的消息(这个和在客户端实现的方式是一样的)
StateObject so = new StateObject();
so.workSocket = socketClient;
socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so); //准备接受下一个客户端请求
socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} public class StateObject
{
public Socket workSocket = null;
public EndPoint tempRemoteEP = null;
public const int BUFFER_SIZE = 1024;
public byte[] buffer = new byte[BUFFER_SIZE];
}
客户端代码 class TestClient
{
static void Main()
{
try
{
TCPClient tcpClient = (TCPClient)TCPClient.GetInstance();
tcpClient.ServerIP = "127.0.0.1";
tcpClient.ServerPort = 3721; Socket socket = tcpClient.ConnectToServer();
socket.Send(Encoding.UTF8.GetBytes("this is a test message from client!"));
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
Console.WriteLine("Press Enter to Exit"); Console.ReadLine();
}
} public abstract class PacketTransferBase
{
public PacketTransferBase()
{
} private int count = 0;
/// <summary>
/// 接收信息
/// </summary>
/// <param name="ar"></param>
protected void ReceiveMessage(IAsyncResult ar)
{
try
{
StateObject so = (StateObject)ar.AsyncState;
Socket socket = so.workSocket; //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
var length = socket.EndReceive(ar);
byte[] actualData = new byte[length];
Array.Copy(so.buffer, 0, actualData, 0, length); Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2])); bool continueReceive = true;
Console.WriteLine(Encoding.UTF8.GetString(actualData));
//把以下代码去掉注释,把服务器端socket.Close()注释,则服务器端产生一样效果。
//if (count == 0)
//{
// socket.Send(Encoding.UTF8.GetBytes("this is a second message from server!"));
// count++;
//}
//socket.Close(); if (continueReceive && socket != null && socket.Connected)
{
socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} public class StateObject
{
public Socket workSocket = null;
public EndPoint tempRemoteEP = null;
public const int BUFFER_SIZE = 1024;
public byte[] buffer = new byte[BUFFER_SIZE];
}
/// <summary>
/// TCP 客户端
/// </summary>
public class TCPClient : PacketTransferBase
{
public string ServerIP { get; set; }
public int ServerPort { get; set; } private static TCPClient _TCPClient = new TCPClient(); private TCPClient()
{
} public static PacketTransferBase GetInstance()
{
return _TCPClient;
} public Socket ConnectToServer()
{
//创建一个Socket
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //连接到指定服务器的指定端口
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
socketClient.Connect(ServerIP, ServerPort); StateObject so = new StateObject();
so.workSocket = socketClient;
socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
return socketClient;
}
}
服务端端和客户端关闭Socket部分互换后(注释服务器端,开启客户端关闭),服务器端也能长生此同样现象。socket
服务器端代码 class TestServer
{
static void Main()
{
try
{
TCPServer tcpServer = (TCPServer)TCPServer.GetInstance();
tcpServer.ServerIP = "127.0.0.1";
tcpServer.ServerPort = 3721;
tcpServer.CreatListener();
Console.WriteLine(String.Format("Server is ready with {0}:{1}", tcpServer.ServerIP, tcpServer.ServerPort));
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
Console.WriteLine("Press Enter to Exit"); Console.ReadLine();
}
} public abstract class PacketTransferBase
{
public PacketTransferBase()
{
}
private int count = 0;
/// <summary>
/// 接收信息
/// </summary>
/// <param name="ar"></param>
protected void ReceiveMessage(IAsyncResult ar)
{
try
{
StateObject so = (StateObject)ar.AsyncState;
Socket socket = so.workSocket; //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
var length = socket.EndReceive(ar);
byte[] actualData = new byte[length];
Array.Copy(so.buffer, 0, actualData, 0, length);
Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2])); bool continueReceive = true;
Console.WriteLine(Encoding.UTF8.GetString(actualData));
if (count == 0)
{
socket.Send(Encoding.UTF8.GetBytes("this is a back message from server!"));
count++;
}
socket.Close(); if (continueReceive && socket != null && socket.Connected)
{
socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} /// <summary>
/// TCP服务器端
/// </summary>
public class TCPServer : PacketTransferBase
{
public string ServerIP { get; set; }
public int ServerPort { get; set; }
private static TCPServer _TCPServer = new TCPServer();
Socket socketServer = null;
private TCPServer()
{
} public static PacketTransferBase GetInstance()
{
return _TCPServer;
} /// <summary>
/// 服务器端建立侦听
/// </summary>
public void CreatListener()
{
//创建一个新的Socket,这里我们使用最常用的基于TCP的Stream Socket(流式套接字)
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //将该socket绑定到主机上面的某个端口
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.bind.aspx
socketServer.Bind(new IPEndPoint(IPAddress.Parse(ServerIP), ServerPort)); //启动监听,并且设置一个最大的队列长度
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.listen(v=VS.100).aspx
socketServer.Listen(100); //开始接受客户端连接请求
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.beginaccept.aspx
socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
} /// <summary>
/// 接收客户端
/// </summary>
/// <param name="ar"></param>
public void ClientAccepted(IAsyncResult ar)
{
try
{
var socketServer = ar.AsyncState as Socket; //这就是客户端的Socket实例,我们后续可以将其保存起来
var socketClient = socketServer.EndAccept(ar); //接收客户端的消息(这个和在客户端实现的方式是一样的)
StateObject so = new StateObject();
so.workSocket = socketClient;
socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so); //准备接受下一个客户端请求
socketServer.BeginAccept(new AsyncCallback(ClientAccepted), socketServer);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} public class StateObject
{
public Socket workSocket = null;
public EndPoint tempRemoteEP = null;
public const int BUFFER_SIZE = 1024;
public byte[] buffer = new byte[BUFFER_SIZE];
}
客户端代码 class TestClient
{
static void Main()
{
try
{
TCPClient tcpClient = (TCPClient)TCPClient.GetInstance();
tcpClient.ServerIP = "127.0.0.1";
tcpClient.ServerPort = 3721; Socket socket = tcpClient.ConnectToServer();
socket.Send(Encoding.UTF8.GetBytes("this is a test message from client!"));
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
Console.WriteLine("Press Enter to Exit"); Console.ReadLine();
}
} public abstract class PacketTransferBase
{
public PacketTransferBase()
{
} private int count = 0;
/// <summary>
/// 接收信息
/// </summary>
/// <param name="ar"></param>
protected void ReceiveMessage(IAsyncResult ar)
{
try
{
StateObject so = (StateObject)ar.AsyncState;
Socket socket = so.workSocket; //方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.endreceive.aspx
var length = socket.EndReceive(ar);
byte[] actualData = new byte[length];
Array.Copy(so.buffer, 0, actualData, 0, length); Console.WriteLine(string.Format("TCPServer.ReceiveMessage: buffer.{0} at Socket.{1}.{2} with buffer[0].{3},buffer[1].{4},buffer[2].{5}", length, socket.Handle, socket.Connected, so.buffer[0], so.buffer[1], so.buffer[2])); bool continueReceive = true;
Console.WriteLine(Encoding.UTF8.GetString(actualData));
//把以下代码去掉注释,把服务器端socket.Close()注释,则服务器端产生一样效果。
//if (count == 0)
//{
// socket.Send(Encoding.UTF8.GetBytes("this is a second message from server!"));
// count++;
//}
//socket.Close(); if (continueReceive && socket != null && socket.Connected)
{
socket.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
} public class StateObject
{
public Socket workSocket = null;
public EndPoint tempRemoteEP = null;
public const int BUFFER_SIZE = 1024;
public byte[] buffer = new byte[BUFFER_SIZE];
}
/// <summary>
/// TCP 客户端
/// </summary>
public class TCPClient : PacketTransferBase
{
public string ServerIP { get; set; }
public int ServerPort { get; set; } private static TCPClient _TCPClient = new TCPClient(); private TCPClient()
{
} public static PacketTransferBase GetInstance()
{
return _TCPClient;
} public Socket ConnectToServer()
{
//创建一个Socket
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //连接到指定服务器的指定端口
//方法参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.connect.aspx
socketClient.Connect(ServerIP, ServerPort); StateObject so = new StateObject();
so.workSocket = socketClient;
socketClient.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(ReceiveMessage), so);
return socketClient;
}
}
服务端端和客户端关闭Socket部分互换后(注释服务器端,开启客户端关闭),服务器端也能长生此同样现象。socket
还有更多的问题。
codeproject有一个开源东东很多也碰到过原子锁会出现负的情况。使用心跳包吧。