现打算用C#实现这样的需求,程序可以建立多个连接,采用异步接收与发送数据包。
客户端代码封装参照网上的经典写法:
代码如下:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
public class TcpClient
{
#region 字段 /// <summary>
/// 客户端与服务器之间的会话类
/// </summary>
private Session _session; /// <summary>
/// 客户端是否已经连接服务器
/// </summary>
private bool _isConnected = false; /// <summary>
/// 接收数据缓冲区大小64K
/// </summary>
public const int DefaultBufferSize = 3 * 1024; /// <summary>
/// 通讯格式编码解码器
/// </summary>
private Coder _coder; /// <summary>
/// 接收数据缓冲区
/// </summary>
private byte[] _recvDataBuffer = new byte[DefaultBufferSize]; #endregion #region 事件定义 //需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅 /// <summary>
/// 已经连接服务器事件
/// </summary>
public event NetEvent ConnectedServer; /// <summary>
/// 发送数据事件
/// </summary>
public event NetEvent SendDatagram; /// <summary>
/// 接收到数据报文事件
/// </summary>
public event NetEvent ReceivedDatagram; /// <summary>
/// 连接断开事件
/// </summary>
public event NetEvent DisConnectedServer;
#endregion #region 属性 /// <summary>
/// 返回客户端与服务器之间的会话对象
/// </summary>
public Session ClientSession
{
get
{
return _session;
}
} /// <summary>
/// 返回客户端与服务器之间的连接状态
/// </summary>
public bool IsConnected
{
get
{
return _isConnected;
}
} /// <summary>
/// 编码解码器
/// </summary>
public Coder ServerCoder
{
get
{
return _coder;
}
} #endregion #region 公有方法 /// <summary>
/// 默认构造函数,使用默认的编码格式
/// </summary>
public TcpClient()
{
_coder = new Coder(Coder.EncodingMothord.Default);
} /// <summary>
/// 构造函数,使用一个特定的编码器来初始化
/// </summary>
/// <param name="_coder">报文编码器</param>
public TcpClient(Coder coder)
{
_coder = coder;
} /// <summary>
/// 连接服务器
/// </summary>
/// <param name="ip">服务器IP地址</param>
/// <param name="port">服务器端口</param>
public virtual void Connect(string ip, int port)
{
if (IsConnected)
{
//重新连接
Debug.Assert(_session != null); Close();
} Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock); } /// <summary>
/// 发送数据包
/// </summary>
/// <param name="datagram">数据包</param>
public virtual void Send(byte[] datagram)
{
if (datagram.Length == 0)
{
return;
} if (!_isConnected)
{
throw (new ApplicationException("没有连接服务器,不能发送数据"));
} _session.ClientSocket.BeginSend(datagram, 0, datagram.Length, SocketFlags.None,
new AsyncCallback(SendDataEnd), _session.ClientSocket);
} /// <summary>
/// 关闭连接
/// </summary>
public virtual void Close()
{
if (!_isConnected)
{
return;
} _session.Close(); _session = null; _isConnected = false;
} #endregion #region 受保护方法 /// <summary>
/// 数据发送完成处理函数
/// </summary>
/// <param name="iar"></param>
protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
Debug.Assert(sent != 0); if (SendDatagram != null)
{
SendDatagram(this, new NetEventArgs(_session));
}
} /// <summary>
/// 建立Tcp连接后处理过程
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void Connected(IAsyncResult iar)
{
Socket socket = (Socket)iar.AsyncState; socket.EndConnect(iar); //创建新的会话
_session = new Session(socket); _isConnected = true; //触发连接建立事件
if (ConnectedServer != null)
{
ConnectedServer(this, new NetEventArgs(_session));
} //建立连接后应该立即接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), socket);
} /// <summary>
/// 数据接收处理函数
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void RecvData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState; try
{
int recv = remote.EndReceive(iar); //正常的退出
if (recv == 0)
{
_session.TypeOfExit = Session.ExitType.NormalExit; if (DisConnectedServer != null)
{
DisConnectedServer(this, new NetEventArgs(_session));
} return;
} ICloneable copySession = (ICloneable)_session; Session clientSession = (Session)copySession.Clone(); clientSession.Datagram = _recvDataBuffer; ReceivedDatagram(this, new NetEventArgs(clientSession)); //继续接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), _session.ClientSocket);
}
catch (SocketException ex)
{
//客户端退出
if (10054 == ex.ErrorCode)
{
//服务器强制的关闭连接,强制退出
_session.TypeOfExit = Session.ExitType.ExceptionExit; if (DisConnectedServer != null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}
}
else
{
throw (ex);
}
}
catch (ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if (ex != null)
{
ex = null;
//DoNothing;
}
} } #endregion
}建立多个连接时,代码如下:TcpClient tcp;
for (int i = 0; i < 3; i++)
{
tcp = new TcpClient();
tcp.Connect("127.0.0.1", 8080);
tcp.Send(Encoding.Unicode.GetBytes("测试数据"));
}PS:在断点调试时不报错,不调试则会在这一句throw (new ApplicationException("没有连接服务器,不能发送数据"));报错。
请各位高手指点一二,谢谢!
客户端代码封装参照网上的经典写法:
代码如下:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
public class TcpClient
{
#region 字段 /// <summary>
/// 客户端与服务器之间的会话类
/// </summary>
private Session _session; /// <summary>
/// 客户端是否已经连接服务器
/// </summary>
private bool _isConnected = false; /// <summary>
/// 接收数据缓冲区大小64K
/// </summary>
public const int DefaultBufferSize = 3 * 1024; /// <summary>
/// 通讯格式编码解码器
/// </summary>
private Coder _coder; /// <summary>
/// 接收数据缓冲区
/// </summary>
private byte[] _recvDataBuffer = new byte[DefaultBufferSize]; #endregion #region 事件定义 //需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅 /// <summary>
/// 已经连接服务器事件
/// </summary>
public event NetEvent ConnectedServer; /// <summary>
/// 发送数据事件
/// </summary>
public event NetEvent SendDatagram; /// <summary>
/// 接收到数据报文事件
/// </summary>
public event NetEvent ReceivedDatagram; /// <summary>
/// 连接断开事件
/// </summary>
public event NetEvent DisConnectedServer;
#endregion #region 属性 /// <summary>
/// 返回客户端与服务器之间的会话对象
/// </summary>
public Session ClientSession
{
get
{
return _session;
}
} /// <summary>
/// 返回客户端与服务器之间的连接状态
/// </summary>
public bool IsConnected
{
get
{
return _isConnected;
}
} /// <summary>
/// 编码解码器
/// </summary>
public Coder ServerCoder
{
get
{
return _coder;
}
} #endregion #region 公有方法 /// <summary>
/// 默认构造函数,使用默认的编码格式
/// </summary>
public TcpClient()
{
_coder = new Coder(Coder.EncodingMothord.Default);
} /// <summary>
/// 构造函数,使用一个特定的编码器来初始化
/// </summary>
/// <param name="_coder">报文编码器</param>
public TcpClient(Coder coder)
{
_coder = coder;
} /// <summary>
/// 连接服务器
/// </summary>
/// <param name="ip">服务器IP地址</param>
/// <param name="port">服务器端口</param>
public virtual void Connect(string ip, int port)
{
if (IsConnected)
{
//重新连接
Debug.Assert(_session != null); Close();
} Socket newsock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), port);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock); } /// <summary>
/// 发送数据包
/// </summary>
/// <param name="datagram">数据包</param>
public virtual void Send(byte[] datagram)
{
if (datagram.Length == 0)
{
return;
} if (!_isConnected)
{
throw (new ApplicationException("没有连接服务器,不能发送数据"));
} _session.ClientSocket.BeginSend(datagram, 0, datagram.Length, SocketFlags.None,
new AsyncCallback(SendDataEnd), _session.ClientSocket);
} /// <summary>
/// 关闭连接
/// </summary>
public virtual void Close()
{
if (!_isConnected)
{
return;
} _session.Close(); _session = null; _isConnected = false;
} #endregion #region 受保护方法 /// <summary>
/// 数据发送完成处理函数
/// </summary>
/// <param name="iar"></param>
protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
Debug.Assert(sent != 0); if (SendDatagram != null)
{
SendDatagram(this, new NetEventArgs(_session));
}
} /// <summary>
/// 建立Tcp连接后处理过程
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void Connected(IAsyncResult iar)
{
Socket socket = (Socket)iar.AsyncState; socket.EndConnect(iar); //创建新的会话
_session = new Session(socket); _isConnected = true; //触发连接建立事件
if (ConnectedServer != null)
{
ConnectedServer(this, new NetEventArgs(_session));
} //建立连接后应该立即接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), socket);
} /// <summary>
/// 数据接收处理函数
/// </summary>
/// <param name="iar">异步Socket</param>
protected virtual void RecvData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState; try
{
int recv = remote.EndReceive(iar); //正常的退出
if (recv == 0)
{
_session.TypeOfExit = Session.ExitType.NormalExit; if (DisConnectedServer != null)
{
DisConnectedServer(this, new NetEventArgs(_session));
} return;
} ICloneable copySession = (ICloneable)_session; Session clientSession = (Session)copySession.Clone(); clientSession.Datagram = _recvDataBuffer; ReceivedDatagram(this, new NetEventArgs(clientSession)); //继续接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), _session.ClientSocket);
}
catch (SocketException ex)
{
//客户端退出
if (10054 == ex.ErrorCode)
{
//服务器强制的关闭连接,强制退出
_session.TypeOfExit = Session.ExitType.ExceptionExit; if (DisConnectedServer != null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}
}
else
{
throw (ex);
}
}
catch (ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if (ex != null)
{
ex = null;
//DoNothing;
}
} } #endregion
}建立多个连接时,代码如下:TcpClient tcp;
for (int i = 0; i < 3; i++)
{
tcp = new TcpClient();
tcp.Connect("127.0.0.1", 8080);
tcp.Send(Encoding.Unicode.GetBytes("测试数据"));
}PS:在断点调试时不报错,不调试则会在这一句throw (new ApplicationException("没有连接服务器,不能发送数据"));报错。
请各位高手指点一二,谢谢!
for (int i = 0; i < 3; i++)
{
tcp = new TcpClient();
tcp.Connect("127.0.0.1", 8080);
tcp.Send(Encoding.Unicode.GetBytes("测试数据"));
}改成TcpClient tcp=new TcpClient();
tcp.Connect("127.0.0.1", 8080);
tcp.Send(Encoding.Unicode.GetBytes("测试数据"));是可以正常工作的。
tcp.Connect("127.0.0.1", 8080);连接过后,你得知道是否连接上再发吧。连接方法里面使用的是异步连接,需要等到有结果后再进行操作。
你可以在循环外建立一个TcpClient数组,然后再循环内,建立连接.TcpClient tcp = new TcpClient[3];
for (int i = 0; i < 3; i++)
{
tcp[i] = new TcpClient();
tcp[i].Connect("127.0.0.1", 8080);
}
ConnectedServer这个事件中Send