Overview
FastSocket是一个轻量级易扩展的c#异步socket通信库,项目开始于2011年,经过近3年不断调整与改进,目前在功能和性能上均有不错的表现。
项目地址:https://github.com/devhong/FastSocket.Net
FastSocket内置了命令行、二进制、thrift协议,基于此开发了Zookeeper, Redis, Thrift等c#异步客户端,接下来将会一一公开。
Requirements
.Net 4.0 or Mono 2.6
Example Usage
简单的命令行服务
新建控制台项目,添加FastSocket.SocketBase,FastSocket.Server引用
自定义服务实现MyService
using System; using Sodao.FastSocket.Server; using Sodao.FastSocket.Server.Command; using Sodao.FastSocket.SocketBase; /// <summary>
/// 实现自定义服务 /// </summary>
public class MyService : CommandSocketService<StringCommandInfo> { /// <summary>
/// 当连接时会调用此方法 /// </summary>
/// <param name="connection"></param>
public override void OnConnected(IConnection connection) { base.OnConnected(connection); Console.WriteLine(connection.RemoteEndPoint.ToString() + " connected"); connection.BeginSend(PacketBuilder.ToCommandLine("welcome")); } /// <summary>
/// 当连接断开时会调用此方法 /// </summary>
/// <param name="connection"></param>
/// <param name="ex"></param>
public override void OnDisconnected(IConnection connection, Exception ex) { base.OnDisconnected(connection, ex); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(connection.RemoteEndPoint.ToString() + " disconnected"); Console.ForegroundColor = ConsoleColor.Gray; } /// <summary>
/// 当发生错误时会调用此方法 /// </summary>
/// <param name="connection"></param>
/// <param name="ex"></param>
public override void OnException(IConnection connection, Exception ex) { base.OnException(connection, ex); Console.WriteLine("error: " + ex.ToString()); } /// <summary>
/// 处理未知命令 /// </summary>
/// <param name="connection"></param>
/// <param name="commandInfo"></param>
protected override void HandleUnKnowCommand(IConnection connection, StringCommandInfo commandInfo) { commandInfo.Reply(connection, "unknow command:" + commandInfo.CmdName); } }
Exit命令
/// <summary> /// 退出命令 /// </summary> public sealed class ExitCommand : ICommand<StringCommandInfo> { /// <summary> /// 返回命令名称 /// </summary> public string Name { get { return "exit"; } } /// <summary> /// 执行命令 /// </summary> /// <param name="connection"></param> /// <param name="commandInfo"></param> public void ExecuteCommand(IConnection connection, StringCommandInfo commandInfo) { connection.BeginDisconnect();//断开连接 } }
App.config配置
<?xml version="1.0"?> <configuration> <configSections> <section name="socketServer" type="Sodao.FastSocket.Server.Config.SocketServerConfig, FastSocket.Server"/> </configSections> <socketServer> <servers> <server name="cmdline" port="8400" socketBufferSize="8192" messageBufferSize="8192" maxMessageSize="102400" maxConnections="20000" serviceType="CommandLine.MyService, CommandLine" protocol="commandLine"/> </servers> </socketServer> </configuration>
初始化及启动服务
static void Main(string[] args) { SocketServerManager.Init(); SocketServerManager.Start(); Console.ReadLine(); }
启动服务,然后在cmd中运行telnet 127.0.0.1 8400, 运行截图如下:
其中welcome中当连接建立时服务端发送到终端的。
connection.BeginSend(PacketBuilder.ToCommandLine("welcome"));
unknow command:Hello是因为没有对应的"Hello"命令实现由HandleUnKnowCommand输出的
/// <summary> /// 处理未知命令 /// </summary> /// <param name="connection"></param> /// <param name="commandInfo"></param> protected override void HandleUnKnowCommand(IConnection connection, StringCommandInfo commandInfo) { commandInfo.Reply(connection, "unknow command:" + commandInfo.CmdName); }
当在终端中键入exit时,触发了ExitCommand.ExecuteCommand方法,服务端主动断开连接,终端退出。
CLR支持两种类型:引用类型和值类型,虽然FCL中大多的类型都是引用类型,但是我们程序员用得最多的还是值类型。引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址---也就是指向对象数据的内存地址,引用类型有以下特点:
托管堆的概念,简单的说,所有的.NET语言在分配引用类型对象时都要使用托管堆。像值类型这样的轻量级对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆。托管堆的更深入理解请看这里:http://blog.sina.com.cn/s/blog_538342930100nlmc.html
试想如果所有类型都是引用类型,那么我们的应用程序在使用一个Int32值都要进行一次内存分配,性能会受多么大的影响,幸运的是CLR提供了更为轻量级的值类型,值类型有以下特点:
按照国际惯例,应该上一段代码:
class Aref { public Int32 x;} struct Aval { public Int32 x;} static void Main(string[] args) { Aref r1 = new Aref(); Aval v1; r1.x = 1; v1.x = 1; Aref r2 = r1; Aval v2 = v1; r1.x = 5; v1.x = 6; Console.WriteLine(r1.x);//显示5 Console.WriteLine(r2.x);//显示5 Console.WriteLine(v1.x);//显示6 Console.WriteLine(v2.x);//显示1 }
上面这段代码应该很容易看懂,这也是我们程序员面试的时候经常碰到的一个问题之一,它很好的说明了值类型与引用类型的区别。注意到上面的代码Aval v1 并没有写成 Aval v1 = new Aval(),使用new操作符可以对值类型初始化,在线程栈上分配实例,并将字段初始化为零。
值类型能提供更好的性能,将类型声明为值类型需要满足以下条件:
一个方法的返回值为值类型,那么调用者调用该方法的时候,实例中的字段会复制到调用者分配的内存中,所以值类型的实例如果过大也会对性能有损害,CLR推荐值类型的实例应该小于16字节或者更小,如果大于16字节那么该类型不应作为方法的实参传递或者作为返回值。
值类型的主要优势在于他不作为对象在托管堆上分配,下面列出了值类型与引用类型的一些不同:
值类型与引用类型的相互转换称为装箱和拆箱,装箱需要在托管堆上分配额外的对象,将来必须对其进行垃圾回收,所以不必要的,额外的装箱应尽量避免,否则会影响程序的性能和内存消耗,比如在一个循环中重复对一个值类型的变量进行装箱。
又是深夜了,杭州还是那么热,“尤特” 你快些来...