.NET 跨平台RPCDotNettyRPC

 

DotNettyRPC

1.简介

DotNettyRPC是一个基于DotNetty的跨平台RPC框架,支持.NET45以及.NET Standard2.0

2.产生背景

传统.NET开发中遇到远程调用服务时,多以WCF为主。而WCF虽然功能强大,但是其配置复杂,不易于上手。而且未来必定是.NET Core的天下,WCF暂不支持.NET Core(只有客户端,无法建立服务端)。市面上的其他.NET的 RPC框架诸如gRPC、surging甚至微服务框架Orleans等,这些框架功能强大,性能也很好,并且比较成熟,但是使用起来不够简单。基于上述比较(无任何吹捧贬低的意思),鄙人不才撸了一个轮子DotNettyRPC,它的定位是一个跨平台(.NET45和.NET Standard)、简单却实用的RPC框架

3.使用方法

3.1引入DotNettyRPC

打开Nuget包管理器,搜索DotNettyRPC即可找到并使用

或输入Nuget命令:Install-Package DotNettyRPC

3.2定义服务接口


    public interface IHello
    {
        string SayHello(string msg);
    }

    public class Hello : IHello
    {
        public string SayHello(string msg)
        {
            return msg;
        }
    }

3.3服务端

using Coldairarrow.DotNettyRPC;
using Common;
using System;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            RPCServer rPCServer = new RPCServer(9999);
            rPCServer.RegisterService<IHello, Hello>();
            rPCServer.Start();

            Console.ReadLine();
        }
    }
}

3.4客户端

using Coldairarrow.DotNettyRPC;
using Common;
using System;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            IHello client = RPCClientFactory.GetClient<IHello>("127.0.0.1", 9999);
            var msg = client.SayHello("Hello");
            Console.WriteLine(msg);
            Console.ReadLine();
        }
    }
}

3.5运行

先运行服务端,再运行客户端,即可在客户端输出Hello

4.结语

本机测试一次RPC请求平均0.4ms左右,性能不高,但是足以应对绝大多数业务场景,重在简单实用。可以优化的地方很多,还望大家多多支持。

GitHub地址:https://github.com/Coldairarrow/DotNettyRPC

QQ群:373144077

Web后台快速开发框架(.NET Core)

 

Web后台快速开发框架(.NET Core)

Coldairarrow

EasyWcf------无需配置,无需引用,动态绑定,轻松使用

 

设计原则:万物皆对象

前言:在上一篇的0配置使用Wcf中,虽然使用已经很方便了,但是对于最求极致简洁得人来说(比如我),客户端需要通过手动引用服务才能够调用服务接口,那么有没有办法能够绕过手动引用这一步,并且直接通过调用地址使用呢?答案肯定是有的,不然我这篇文章就毫无意义了,而我是从来不做无意义之事,人狠话不多,下面介绍如何简单、优雅、高效的使用Wcf

正文:

首先需要引入框架,框架代码以及Demo源码在最后的Git地址中!

服务端:

定义接口:

using System.ServiceModel;

namespace WcfServer
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        string Hello();
    }
}

实现接口:

namespace WcfServer
{
    public class MyService : IMyService
    {
        public string Hello()
        {
            return "Hello World!";
        }
    }
}

这里只是简单输出Hello World,别的操作只需要仿造即可!

服务端启动:

using Coldairarrow.Util.Wcf;
using System;

namespace WcfServer
{
    class Program
    {
        static void Main(string[] args)
        {
            WcfHost<MyService, IMyService> wcfHost = new WcfHost<MyService, IMyService>("http://localhost:14725", "http://localhost:14725/mex");

            wcfHost.HandleHostOpened = new Action<object, EventArgs>((obj, tar) =>
            {
                Console.WriteLine("服务已启动!");
            });

            wcfHost.StartHost();

            while (Console.ReadLine() != "quit")
            {

            }
        }
    }
}

服务端与上次的使用没多大区别

注意:服务端启动必须要以管理员身份运行!

客户端:

using Coldairarrow.Util.Wcf;
using System;
using WcfServer;

namespace WcfClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var client = WcfClientFactory.CreateClientByUrl<IMyService>("http://localhost:14725/MyService");
            var data = client.Hello();
            Console.WriteLine(data);

            Console.ReadKey();
        }
    }
}

客户端的使用不需要再从地址引用服务了,直接通过调用WcfClientFactory.CreateClientByUrl方法就可以返回操作接口,其中需要传入泛型接口类,也就是服务端中的IMyService。

服务端运行后,客户端直接运行即可!

运行截图如下:

服务端截图:

客户端截图:

可以看到,使用起来十分地简单方便,可以极大的提高开发效率!

老规矩,全部源码及Demo在GitHub:https://github.com/Coldairarrow/EasyWcf

大家用得爽了别忘了点星星哦~~~

分割线------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

经过了差不多半年,毕业设计也终于完成了,我的毕设是后台快速开发框架,不出意外这个框架会永远伴随着我,我也会一直完善它,希望它能够在我的职业生涯中发放光彩!

毕设虽然完成了,但是探索技术的步伐是永远不会停止的!大家一起加油~~~

END

C# .NET 0配置使用Wcf(半成品)

 

设计原则:万物皆对象

背景:微软提供了一套强大的通信框架Wcf,了解请看百度百科:ttps://baike.baidu.com/item/Wcf/7374854?fr=aladdin

虽然这套通信框架很强大,但是配置起来也不简单,因此导致很多人望而却步(包括我),我这人向来不喜欢麻烦,喜欢简单,最好就是给我一个对象,告诉我怎么传参就使用是最爽的,我相信应该有很多人跟我一样的想法,因此,这篇文章应运而生,没错,就是零配置使用Wcf,下面我会详细道来。

正文:

1、核心类库,注意,需要引用程序集

System.ServiceModel

WcfHost.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading.Tasks;

namespace Coldairarrow.Util.Wcf
{
    /// <summary>
    /// Wcf服务代码控制类(必须开启管理员权限)
    /// </summary>
    /// <typeparam name="Service">服务处理</typeparam>
    /// <typeparam name="IService">服务接口</typeparam>
    public class WcfHost<Service,IService>
    {
        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="baseUrl">http基地址(服务器真实地址),默认为:http://127.0.0.1:14725/ </param>
        /// <param name="httpGetUrl">http获取服务引用的地址(服务器真实地址),默认为:http://127.0.0.1:14725/mex </param>
        public WcfHost(string baseUrl= "http://127.0.0.1:14725/", string httpGetUrl= "http://127.0.0.1:14725/mex")
        {
            _serviceHost = new ServiceHost(typeof(Service), new Uri(baseUrl));
            //ServiceEndPoint 终结点 包含Address地址 Binding绑定 Contracts契约 简称ABC
            _serviceHost.AddServiceEndpoint(typeof(IService), new WSHttpBinding(), typeof(Service).Name);
            //添加服务终结点
            if (_serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
            {
                //判断是否在配置文件中定义了元数据终结点
                ServiceMetadataBehavior metaData = new ServiceMetadataBehavior();
                metaData.HttpGetEnabled = true;
                metaData.HttpGetUrl = new Uri(httpGetUrl);
                _serviceHost.Description.Behaviors.Add(metaData);//添加元数据终结点
            }
        }

        #endregion

        #region 私有成员

        private ServiceHost _serviceHost;

        #endregion

        #region 外部接口

        /// <summary>
        /// 开始Wcf服务
        /// </summary>
        public void StartHost()
        {
            Task task = new Task(() =>
            {
                try
                {
                    if (HandleHostOpened != null)
                        _serviceHost.Opened += new EventHandler(HandleHostOpened);

                    if (_serviceHost.State != CommunicationState.Opened)
                    {
                        _serviceHost.Open();
                    }
                }
                catch (Exception ex)
                {
                    HandleException?.Invoke(ex);
                }
            });
            task.Start();
        }

        #endregion

        #region 事件处理

        /// <summary>
        /// 当Wcf服务开启后执行
        /// </summary>
        public Action<object, EventArgs> HandleHostOpened { get; set; }

        /// <summary>
        /// 异常处理
        /// </summary>
        public Action<Exception> HandleException { get; set; }

        #endregion
    }
}

2、服务端使用:

服务接口定义:

IService.cs

using System.ServiceModel;

namespace _01.WcfServer
{
    /// <summary>
    /// 对外提供的接口规范,必须要ServiceContract特性
    /// </summary>
    [ServiceContract]
    public interface IService
    {
        /// <summary>
        /// 对外提供的接口方法,必须OperationContract特性,方法不能重载
        /// </summary>
        /// <returns></returns>
        [OperationContract]
        string Hello();
    }
}

服务接口实现:

Service.cs

namespace _01.WcfServer
{
    /// <summary>
    /// 接口具体实现类
    /// </summary>
    public class Service : IService
    {
        /// <summary>
        /// 方法具体实现
        /// </summary>
        /// <returns></returns>
        public string Hello()
        {
            return "Hello World";
        }
    }
}

服务端运行:注意,必须以管理员权限运行

Program.cs

using Coldairarrow.Util.Wcf;
using System;

namespace _01.WcfServer
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建Wcf服务对象,泛型参数Service为实现类,IService为服务接口
            //第一个参数baseUrl为服务基地址(必须为真实地址)
            //第二个参数httpGetUrl为服务引用地址(必须为真实地址),也就是客户端添加服务引用时用的地址
            WcfHost<Service, IService> wcfHost = new WcfHost<Service, IService>("http://localhost:14725", "http://localhost:14725/mex");

            //当Wcf服务开启后执行的事件
            wcfHost.HandleHostOpened = new Action<object, EventArgs>((obj, tar) =>
              {
                  Console.WriteLine("服务已启动!");
              });

            //开始Wcf服务
            wcfHost.StartHost();

            while(Console.ReadLine()!="quit")
            {

            }
        }
    }
}

客户端使用:

Program.cs

using System;

namespace _02.WcfClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //ServiceReference1为引用时自定义的命名空间
            //ServiceClient为具体实现类,Service为类名,Client为后缀
            //可以在很多地方使用,比如控制台,Winform,ASP.NET网站等,把它当做一个类库就很好理解了66666
            ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();

            //调用Service提供的Hello方法,Wcf服务端必须运行
            var data = client.Hello();
            Console.WriteLine(data);

            Console.ReadKey();
        }
    }
}

详细使用步骤:

1、运行Wcf服务端,必须以管理员权限

2、打开浏览器,测试Wcf是否成功开启

3、客户端引用服务

右键引用,引用服务,输入服务地址(即Wcf初始化时第二个参数)

3、客户端代码调用

2、客户端成功运行

总结:

全程实现真正的0配置搭建了Wcf服务,满不满意,意不意外,惊不惊喜,爽不爽~~

最后,惯例,全部代码代码在GitHub,欢迎大家点赞~

https://github.com/Coldairarrow/Wcf

C# .NET Socket 简单实用框架

 

背景:

首先向各位前辈,大哥哥小姐姐问一声好~

这是我第一次写博客,目前为一个即将步入大四的学生,上学期在一家公司实习了半年,后期发现没有动力,而且由于薪水问题(废话嘛),于是跳槽到这家新的公司。

说到Socket,想必大家都或多或少有所涉及,从最初的计算机网络课程,讲述了tcp协议,而Socket就是对协议的进一步封装,使我们开发人员能够更加容易轻松的进行软件之间的通信。

这个星期刚好接受一个共享车位锁的项目,需要使用Socket与硬件进行通信控制,说白了也就是给锁发送指令,控制其打开或者关闭,再就是对App开放操作接口,使其方便测试以及用户的使用。这其中核心就是Socket的使用,再开发出这个功能之后,我发现使用起来很不方便,于是耗时2天抽象其核心功能并封装成框架,最后使用这个框架将原来的项目重构并上线,极大的提高了软件的可拓展性,健壮性,容错率。

个人坚信的原则:万物皆对象

好了,不废话了,下面进入正文

正文:

1、首先简单讲下C#中Socket的简单使用。

第一步:服务端监听某个端口

第二步:客户端向服务端地址和端口发起Socket连接请求

第三步:服务端收到连接请求后创建Socket连接,并维护这个连接队列。

第四步:客户端和服务端已经建立双工通信(即双向通信),客户端和服务端可以轻松方便的给彼此发送信息。

至于简单使用的具体实现代码全部被我封装到项目中了,如果需要学习简单的实现,可以看我的源码,也可以自行百度,有很多的教程

2、核心,框架的使用

其实,说其为框架,可能有点牵强,因为每个人对框架都有自己的理解,但是类库和框架又有什么本质区别呢?全部都是代码~哈哈,扯远了

首先,空说无凭,先放上所有的代码:

服务端源文件:

SocketServer.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Coldairarrow.Util.Sockets
{
    /// <summary>
    /// Socket服务端
    /// </summary>
    public class SocketServer
    {
        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ip">监听的IP地址</param>
        /// <param name="port">监听的端口</param>
        public SocketServer(string ip, int port)
        {
            _ip = ip;
            _port = port;
        }

        /// <summary>
        /// 构造函数,监听IP地址默认为本机0.0.0.0
        /// </summary>
        /// <param name="port">监听的端口</param>
        public SocketServer(int port)
        {
            _ip = "0.0.0.0";
            _port = port;
        }

        #endregion

        #region 内部成员

        private Socket _socket = null;
        private string _ip = "";
        private int _port = 0;
        private bool _isListen = true;
        private void StartListen()
        {
            try
            {
                _socket.BeginAccept(asyncResult =>
                {
                    try
                    {
                        Socket newSocket = _socket.EndAccept(asyncResult);

                        //马上进行下一轮监听,增加吞吐量
                        if (_isListen)
                            StartListen();

                        SocketConnection newClient = new SocketConnection(newSocket, this)
                        {
                            HandleRecMsg = HandleRecMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleRecMsg),
                            HandleClientClose = HandleClientClose == null ? null : new Action<SocketConnection, SocketServer>(HandleClientClose),
                            HandleSendMsg = HandleSendMsg == null ? null : new Action<byte[], SocketConnection, SocketServer>(HandleSendMsg),
                            HandleException = HandleException == null ? null : new Action<Exception>(HandleException)
                        };

                        newClient.StartRecMsg();
                        ClientList.AddLast(newClient);

                        HandleNewClientConnected?.Invoke(this, newClient);
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        #endregion

        #region 外部接口

        /// <summary>
        /// 开始服务,监听客户端
        /// </summary>
        public void StartServer()
        {
            try
            {
                //实例化套接字(ip4寻址协议,流式传输,TCP协议)
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //创建ip对象
                IPAddress address = IPAddress.Parse(_ip);
                //创建网络节点对象包含ip和port
                IPEndPoint endpoint = new IPEndPoint(address, _port);
                //将 监听套接字绑定到 对应的IP和端口
                _socket.Bind(endpoint);
                //设置监听队列长度为Int32最大值(同时能够处理连接请求数量)
                _socket.Listen(int.MaxValue);
                //开始监听客户端
                StartListen();
                HandleServerStarted?.Invoke(this);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        /// <summary>
        /// 所有连接的客户端列表
        /// </summary>
        public LinkedList<SocketConnection> ClientList { get; set; } = new LinkedList<SocketConnection>();

        /// <summary>
        /// 关闭指定客户端连接
        /// </summary>
        /// <param name="theClient">指定的客户端连接</param>
        public void CloseClient(SocketConnection theClient)
        {
            theClient.Close();
        }

        #endregion

        #region 公共事件

        /// <summary>
        /// 异常处理程序
        /// </summary>
        public Action<Exception> HandleException { get; set; }

        #endregion

        #region 服务端事件

        /// <summary>
        /// 服务启动后执行
        /// </summary>
        public Action<SocketServer> HandleServerStarted { get; set; }

        /// <summary>
        /// 当新客户端连接后执行
        /// </summary>
        public Action<SocketServer, SocketConnection> HandleNewClientConnected { get; set; }

        /// <summary>
        /// 服务端关闭客户端后执行
        /// </summary>
        public Action<SocketServer, SocketConnection> HandleCloseClient { get; set; }

        #endregion

        #region 客户端连接事件

        /// <summary>
        /// 客户端连接接受新的消息后调用
        /// </summary>
        public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }

        /// <summary>
        /// 客户端连接发送消息后回调
        /// </summary>
        public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }

        /// <summary>
        /// 客户端连接关闭后回调
        /// </summary>
        public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }

        #endregion
    }
}
using System;
using System.Net.Sockets;
using System.Text;

namespace Coldairarrow.Util.Sockets
{
    /// <summary>
    /// Socket连接,双向通信
    /// </summary>
    public class SocketConnection
    {
        #region 构造函数

        public SocketConnection(Socket socket,SocketServer server)
        {
            _socket = socket;
            _server = server;
        }

        #endregion

        #region 私有成员

        private readonly Socket _socket;
        private bool _isRec=true;
        private SocketServer _server = null;
        private bool IsSocketConnected()
        {
            bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
            bool part2 = (_socket.Available == 0);
            if (part1 && part2)
                return false;
            else
                return true;
        }

        #endregion

        #region 外部接口

        /// <summary>
        /// 开始接受客户端消息
        /// </summary>
        public void StartRecMsg()
        {
            try
            {
                byte[] container = new byte[1024 * 1024 * 2];
                _socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
                {
                    try
                    {
                        int length = _socket.EndReceive(asyncResult);

                        //马上进行下一轮接受,增加吞吐量
                        if (length > 0 && _isRec && IsSocketConnected())
                            StartRecMsg();

                        if (length > 0)
                        {
                            byte[] recBytes = new byte[length];
                            Array.Copy(container, 0, recBytes, 0, length);

                            //处理消息
                            HandleRecMsg?.Invoke(recBytes, this, _server);
                        }
                        else
                            Close();
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                        Close();
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
                Close();
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据字节</param>
        public void Send(byte[] bytes)
        {
            try
            {
                _socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
                {
                    try
                    {
                        int length = _socket.EndSend(asyncResult);
                        HandleSendMsg?.Invoke(bytes, this, _server);
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        /// <summary>
        /// 发送字符串(默认使用UTF-8编码)
        /// </summary>
        /// <param name="msgStr">字符串</param>
        public void Send(string msgStr)
        {
            Send(Encoding.UTF8.GetBytes(msgStr));
        }

        /// <summary>
        /// 发送字符串(使用自定义编码)
        /// </summary>
        /// <param name="msgStr">字符串消息</param>
        /// <param name="encoding">使用的编码</param>
        public void Send(string msgStr,Encoding encoding)
        {
            Send(encoding.GetBytes(msgStr));
        }

        /// <summary>
        /// 传入自定义属性
        /// </summary>
        public object Property { get; set; }

        /// <summary>
        /// 关闭当前连接
        /// </summary>
        public void Close()
        {
            try
            {
                _isRec = false;
                _socket.Disconnect(false);
                _server.ClientList.Remove(this);
                HandleClientClose?.Invoke(this, _server);
                _socket.Close();
                _socket.Dispose();
                GC.Collect();
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        #endregion

        #region 事件处理

        /// <summary>
        /// 客户端连接接受新的消息后调用
        /// </summary>
        public Action<byte[], SocketConnection, SocketServer> HandleRecMsg { get; set; }

        /// <summary>
        /// 客户端连接发送消息后回调
        /// </summary>
        public Action<byte[], SocketConnection, SocketServer> HandleSendMsg { get; set; }

        /// <summary>
        /// 客户端连接关闭后回调
        /// </summary>
        public Action<SocketConnection, SocketServer> HandleClientClose { get; set; }

        /// <summary>
        /// 异常处理程序
        /// </summary>
        public Action<Exception> HandleException { get; set; }

        #endregion
    }
}
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Coldairarrow.Util.Sockets
{
    /// <summary>
    /// Socket客户端
    /// </summary>
    public class SocketClient
    {
        #region 构造函数

        /// <summary>
        /// 构造函数,连接服务器IP地址默认为本机127.0.0.1
        /// </summary>
        /// <param name="port">监听的端口</param>
        public SocketClient(int port)
        {
            _ip = "127.0.0.1";
            _port = port;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ip">监听的IP地址</param>
        /// <param name="port">监听的端口</param>
        public SocketClient(string ip, int port)
        {
            _ip = ip;
            _port = port;
        }

        #endregion

        #region 内部成员

        private Socket _socket = null;
        private string _ip = "";
        private int _port = 0;
        private bool _isRec=true;
        private bool IsSocketConnected()
        {
            bool part1 = _socket.Poll(1000, SelectMode.SelectRead);
            bool part2 = (_socket.Available == 0);
            if (part1 && part2)
                return false;
            else
                return true;
        }

        /// <summary>
        /// 开始接受客户端消息
        /// </summary>
        public void StartRecMsg()
        {
            try
            {
                byte[] container = new byte[1024 * 1024 * 2];
                _socket.BeginReceive(container, 0, container.Length, SocketFlags.None, asyncResult =>
                {
                    try
                    {
                        int length = _socket.EndReceive(asyncResult);

                        //马上进行下一轮接受,增加吞吐量
                        if (length > 0 && _isRec && IsSocketConnected())
                            StartRecMsg();

                        if (length > 0)
                        {
                            byte[] recBytes = new byte[length];
                            Array.Copy(container, 0, recBytes, 0, length);

                            //处理消息
                            HandleRecMsg?.Invoke(recBytes, this);
                        }
                        else
                            Close();
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                        Close();
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
                Close();
            }
        }

        #endregion

        #region 外部接口

        /// <summary>
        /// 开始服务,连接服务端
        /// </summary>
        public void StartClient()
        {
            try
            {
                //实例化 套接字 (ip4寻址协议,流式传输,TCP协议)
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //创建 ip对象
                IPAddress address = IPAddress.Parse(_ip);
                //创建网络节点对象 包含 ip和port
                IPEndPoint endpoint = new IPEndPoint(address, _port);
                //将 监听套接字  绑定到 对应的IP和端口
                _socket.BeginConnect(endpoint, asyncResult =>
                {
                    try
                    {
                        _socket.EndConnect(asyncResult);
                        //开始接受服务器消息
                        StartRecMsg();

                        HandleClientStarted?.Invoke(this);
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据字节</param>
        public void Send(byte[] bytes)
        {
            try
            {
                _socket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, asyncResult =>
                {
                    try
                    {
                        int length = _socket.EndSend(asyncResult);
                        HandleSendMsg?.Invoke(bytes, this);
                    }
                    catch (Exception ex)
                    {
                        HandleException?.Invoke(ex);
                    }
                }, null);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        /// <summary>
        /// 发送字符串(默认使用UTF-8编码)
        /// </summary>
        /// <param name="msgStr">字符串</param>
        public void Send(string msgStr)
        {
            Send(Encoding.UTF8.GetBytes(msgStr));
        }

        /// <summary>
        /// 发送字符串(使用自定义编码)
        /// </summary>
        /// <param name="msgStr">字符串消息</param>
        /// <param name="encoding">使用的编码</param>
        public void Send(string msgStr, Encoding encoding)
        {
            Send(encoding.GetBytes(msgStr));
        }

        /// <summary>
        /// 传入自定义属性
        /// </summary>
        public object Property { get; set; }

        /// <summary>
        /// 关闭与服务器的连接
        /// </summary>
        public void Close()
        {
            try
            {
                _isRec = false;
                _socket.Disconnect(false);
                HandleClientClose?.Invoke(this);
            }
            catch (Exception ex)
            {
                HandleException?.Invoke(ex);
            }
        }

        #endregion

        #region 事件处理

        /// <summary>
        /// 客户端连接建立后回调
        /// </summary>
        public Action<SocketClient> HandleClientStarted { get; set; }

        /// <summary>
        /// 处理接受消息的委托
        /// </summary>
        public Action<byte[], SocketClient> HandleRecMsg { get; set; }

        /// <summary>
        /// 客户端连接发送消息后回调
        /// </summary>
        public Action<byte[], SocketClient> HandleSendMsg { get; set; }

        /// <summary>
        /// 客户端连接关闭后回调
        /// </summary>
        public Action<SocketClient> HandleClientClose { get; set; }

        /// <summary>
        /// 异常处理程序
        /// </summary>
        public Action<Exception> HandleException { get; set; }

        #endregion
    }
}

上面放上的是框架代码,接下来介绍下如何使用

首先,服务端使用方式:

using Coldairarrow.Util.Sockets;
using System;
using System.Text;

namespace Console_Server
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建服务器对象,默认监听本机0.0.0.0,端口12345
            SocketServer server = new SocketServer(12345);

            //处理从客户端收到的消息
            server.HandleRecMsg = new Action<byte[], SocketConnection, SocketServer>((bytes, client, theServer) =>
            {
                string msg = Encoding.UTF8.GetString(bytes);
                Console.WriteLine($"收到消息:{msg}");
            });

            //处理服务器启动后事件
            server.HandleServerStarted = new Action<SocketServer>(theServer =>
            {
                Console.WriteLine("服务已启动************");
            });

            //处理新的客户端连接后的事件
            server.HandleNewClientConnected = new Action<SocketServer, SocketConnection>((theServer, theCon) =>
            {
                Console.WriteLine($@"一个新的客户端接入,当前连接数:{theServer.ClientList.Count}");
            });

            //处理客户端连接关闭后的事件
            server.HandleClientClose = new Action<SocketConnection, SocketServer>((theCon, theServer) =>
            {
                Console.WriteLine($@"一个客户端关闭,当前连接数为:{theServer.ClientList.Count}");
            });

            //处理异常
            server.HandleException = new Action<Exception>(ex =>
            {
                Console.WriteLine(ex.Message);
            });

            //服务器启动
            server.StartServer();

            while (true)
            {
                Console.WriteLine("输入:quit,关闭服务器");
                string op = Console.ReadLine();
                if (op == "quit")
                    break;
            }
        }
    }
}

客户端使用方式:

using Coldairarrow.Util.Sockets;
using System;
using System.Text;

namespace Console_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建客户端对象,默认连接本机127.0.0.1,端口为12345
            SocketClient client = new SocketClient(12345);

            //绑定当收到服务器发送的消息后的处理事件
            client.HandleRecMsg = new Action<byte[], SocketClient>((bytes, theClient) =>
            {
                string msg = Encoding.UTF8.GetString(bytes);
                Console.WriteLine($"收到消息:{msg}");
            });

            //绑定向服务器发送消息后的处理事件
            client.HandleSendMsg = new Action<byte[], SocketClient>((bytes, theClient) =>
            {
                string msg = Encoding.UTF8.GetString(bytes);
                Console.WriteLine($"向服务器发送消息:{msg}");
            });

            //开始运行客户端
            client.StartClient();

            while (true)
            {
                Console.WriteLine("输入:quit关闭客户端,输入其它消息发送到服务器");
                string str = Console.ReadLine();
                if (str == "quit")
                {
                    client.Close();
                    break;
                }
                else
                {
                    client.Send(str);
                }
            }
        }
    }
}

最后运行测试截图:

总结:

其最方便之处在于,将如何创建连接封装掉,使用人员只需关注连接后发送什么数据,接收到数据后应该如何处理,等等其它的很多事件的处理,这其中主要依托于匿名委托的使用,Lambda表达式的使用。

框架里面主要使用了异步通讯,以及如何控制连接,详细我就不多说了,大家应该一看就懂,我只希望能给大家带来便利,最后大家有任何问题、意见、想法,都可以给我留言。

最后,附上所有源码项目地址,若觉得有一定价值,还请点赞~

GitHub地址:https://github.com/Coldairarrow/Sockets

C# .NET 0命令行安装Windows服务程序

 

设计原则:万物皆对象

背景:在我的项目中,即需要与硬件通过Socket连接通讯,又需要给App提供Wcf服务操作接口,虽然都完成了,但是却是一个控制台(虽然我很喜欢控制台,因为它简单易用),把它放到服务器运行,总有一个黑乎乎的窗口,总感觉不雅(原谅我的强迫症)。于是各种百度谷歌如何创建运行WIndows服务程序,就像SqlServer数据那样在后台默默运行奉献就可以了。

但是,很多都是那么的麻烦,需要批处理什么的,而我这个人向来喜欢简洁,于是便设计了这么个Windows服务辅助类,没错,就是0命令。

正文:

1、类库源码我就不放了,最后都放到GitHub上

2、使用方法:

using Coldairarrow.Util.WindowsService;
using System;

namespace WindowsServiceTest
{
    class Program
    {
        public static void Main(string[] argc)
        {
            //创建服务容器,第一个参数为指定服务名,第二个参数为主函数入口的参数argc
            WindowsServiceContainer serviceContainer = new WindowsServiceContainer("A_Test_Service", argc);

            //服务启动时执行的事件,即可以看做控制台的主函数Main即可
            serviceContainer.HandleOnStart = new Action<string[]>(args =>
            {
                //可以在这里添加你需要服务干的事情,比如创建Socket通讯,Wcf服务,balabala.........
                //让它在后台默默地工作把~~~~~~~~~~
            });

            //处理日志的事件
            serviceContainer.HandleLog = new Action<string>(log =>
            {
                Console.WriteLine(log);
            });

            //处理异常的事件
            serviceContainer.HandleException = new Action<Exception>(ex =>
            {
                Console.WriteLine(ex.Message);
            });

            //开始运行服务
            serviceContainer.Start();
        }
    }
}

直接运行控制台即可:

选择1进行安装服务:

没错,你可以看见服务已经成功运行了!!!,是不是很假单?

选择2进行服务卸载:

总结:

通过对服务操作一系列的封装,使将控制台程序变成服务程序非常的简单,不需要任何的批处理命令,只需要简单的几行代码即可,感觉很爽的请点赞!

老规矩,GitHub地址:

https://github.com/Coldairarrow/WindowsServiceDemo

.NET 跨平台RPC框架DotNettyRPC Web后台快速开发框架(.NET Core) EasyWcf------无需配置,无需引用,动态绑定,轻松使用 C# .NET 0配置使用Wcf(半成品) C# .NET Socket 简单实用框架 C# .NET 0命令行安装Windows服务程序的相关教程结束。