在现代网络应用中,客户端与服务器之间的通信是一个核心功能。TCP(传输控制协议)因其可靠性和顺序性,成为实现这种通信的首选协议之一。本文将介绍一种简单易用的TCP协议下的客户端服务器通信实现,它不仅易于集成到项目中,而且性能出色,能够满足多线程环境下的高并发需求。
在开发过程中,经常需要处理客户端与服务器之间的数据传输问题。传统的命名管道通信虽然在某些情况下速度较快,但它要求在连接到计算机时进行身份验证,这在非域计算机上非常受限,并且需要用户在目标计算机上输入有效的系统用户名和密码才能工作。此外,命名管道的实现代码通常非常庞大。因此,开发了一种替代方案,以满足以下需求:
运行解决方案,将得到一个命令行应用程序。按下'S'键进入服务器模式,'C'键进入客户端模式,其他任何键则进入自动模式,开始向服务器发送请求。应用程序硬编码为在127.0.0.1 IP地址上发送和接收,这是本地系统;可以更改此设置以在真实网络上的不同计算机上进行测试。
要使用代码,首先必须创建数据包,如下所示:
[Serializable()]
public class Packet
{
public Packet()
{
data = new byte[3 * 1024 * 1024]; // 3 mega byte
}
public byte[] data { get; set; }
public string Message { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public Guid SessionGuid { get; set; }
public new string ToString()
{
return Message;
}
}
如所见,数据结构是常规类,而不是代理;还必须在类上放置Serializable属性,以便.NET序列化器可以处理它。
NetworkServer ns = new NetworkServer(99, new ProcessPayload(serverprocess));
// 将监听端口99
ns.Start();
private static object serverprocess(object data)
{
Packet dp = data as Packet;
if (dp != null)
return HandlePacket(dp);
Console.WriteLine("message not recognized");
return new ReturnPacket();
}
private static object HandlePacket(Packet dp)
{
ReturnPacket ret = new ReturnPacket();
if (dp.SessionGuid == Guid.Empty)
{
// 可能需要与LDAP服务器进行身份验证
}
else
{
// 检查sessionguid是否有效 -> 如果不是则返回失败
}
ret.OK = true;
ret.Message = "your msg : " + dp.Message + "\r\nreturn from server " + DateTime.Now;
return ret;
}
如所见,它非常简单直观。
NetworkClient nc = new NetworkClient("127.0.0.1", 99);
// 发送到本地主机端口99
nc.Connect();
Packet p = new Packet();
ReturnPacket ret = nc.Send(p) as ReturnPacket;
以下是在AMD K625 1.5ghz,4GB RAM,Windows 7 Home,win评分3.9的笔记本电脑上进行的性能测试结果(CPU使用率超过88%):
同时客户端数量 | 数据包大小 | 块大小 | 10秒内的请求数 |
---|---|---|---|
5 | ~12kb | 32kb | 12681 |
5 | ~12kb | 16kb | 12291 |
5 | ~12kb | 4kb | 11089 |
5 | ~3mb | 4kb | 141 |
5 | ~3mb | 16kb | 207 |
5 | ~3mb | 32kb | 220 |
如所见,对于小数据包和大数据包,最佳性能是32KB的CHUNK_SIZE。可以提高它,例如64KB,但会得到递减的回报,并且可能(不确定)会在一些交换机和路由器上遇到问题,因为它们可能会阻止大的数据包大小。