C# .NET Socket封装

Socket封装,支持多客户端。

封装所要达到的效果,是可以像下面这样使用Socket和服务端通信,调用服务端的方法:

DemoService demoService = new DemoService();
DemoService2 demoService2 = new DemoService2();
string result = demoService.Test("测试DemoService", 1);
demoService.Test2("测试DemoService", 1);
string result2 = demoService2.RunTest("测试DemoService2", 2);

一、数据结构:

CmdType:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataStruct
{
    /// <summary>
    /// cmd类型
    /// </summary>
    public enum CmdType
    {
        /// <summary>
        /// 执行方法
        /// </summary>
        RunFunction = 1,
        /// <summary>
        /// 心跳
        /// </summary>
        Heartbeat = 2
    }
}

SocketData:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataStruct
{
    /// <summary>
    /// Socket数据
    /// </summary>
    [Serializable]
    public class SocketData
    {
        /// <summary>
        /// 命令类型
        /// </summary>
        public CmdType cmdType { get; set; }
        /// <summary>
        /// 类名
        /// </summary>
        public string className { get; set; }
        /// <summary>
        /// 方法名
        /// </summary>
        public string functionName { get; set; }
        /// <summary>
        /// 方法参数
        /// </summary>
        public object[] funParam { get; set; }
    }
}

FunctionUtil(根据SocketData执行服务端的方法):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DataStruct.Utils
{
    /// <summary>
    /// 执行方法
    /// </summary>
    public class FunctionUtil
    {
        /// <summary>
        /// 执行方法
        /// </summary>
        public static object RunFunction(string applicationPath, SocketData socketData)
        {
            Assembly assembly = Assembly.LoadFile(Path.Combine(applicationPath, "DataService.dll"));
            object obj = assembly.CreateInstance("DataService." + socketData.className);
            Type type = obj.GetType();
            return type.GetMethod(socketData.functionName).Invoke(obj, socketData.funParam);
        }
    }
}

二、服务端Socket通信:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Common.Utils;
using CommonDll;
using DataService;
using DataStruct;
using DataStruct.Utils;
using Newtonsoft.Json;

namespace XXPLServer
{
    public partial class Form1 : Form
    {
        #region 变量
        private Socket m_ServerSocket;
        private List<Socket> m_ClientList = new List<Socket>();
        #endregion

        #region Form1构造函数
        public Form1()
        {
            InitializeComponent();
        }
        #endregion

        #region Form1_Load
        private void Form1_Load(object sender, EventArgs e)
        {

        }
        #endregion

        #region 启动服务
        private void btnStartServer_Click(object sender, EventArgs e)
        {
            try
            {
                btnStartServer.Enabled = false;
                btnStopServer.Enabled = true;
                int port = Convert.ToInt32(ConfigurationManager.AppSettings["ServerPort"]);
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);
                m_ServerSocket = new Socket(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                m_ServerSocket.Bind(ipEndPoint);
                m_ServerSocket.Listen(10);
                new Thread(new ThreadStart(delegate()
                {
                    while (true)
                    {
                        Socket m_Client;
                        try
                        {
                            m_Client = m_ServerSocket.Accept();
                            m_Client.SendTimeout = 20000;
                            m_Client.ReceiveTimeout = 20000;
                            m_ClientList.Add(m_Client);
                            LogUtil.Log("监听到新的客户端,当前客户端数:" + m_ClientList.Count);
                        }
                        catch { break; }

                        DateTime lastHeartbeat = DateTime.Now;
                        new Thread(new ThreadStart(delegate()
                        {
                            try
                            {
                                while (true)
                                {
                                    byte[] inBuffer = new byte[m_Client.ReceiveBufferSize];
                                    int receiveCount;
                                    try
                                    {
                                        receiveCount = m_Client.Receive(inBuffer, m_Client.ReceiveBufferSize, SocketFlags.None);//如果接收的消息为空 阻塞 当前循环
                                    }
                                    catch { break; }
                                    if (receiveCount != 0)
                                    {
                                        SocketData data = (SocketData)SerializeUtil.Deserialize(inBuffer);
                                        if (data.cmdType != CmdType.Heartbeat)
                                        {
                                            object obj = FunctionUtil.RunFunction(Application.StartupPath, data);
                                            m_Client.Send(SerializeUtil.Serialize(obj));
                                            LogUtil.Log("接收客户端数据,并向客户端返回数据");
                                        }
                                        else
                                        {
                                            lastHeartbeat = DateTime.Now;
                                        }
                                    }
                                    else
                                    {
                                        m_ClientList.Remove(m_Client);
                                        LogUtil.Log("客户端正常关闭,当前客户端数:" + m_ClientList.Count);
                                        if (m_Client.Connected) m_Client.Disconnect(false);
                                        m_Client.Close();
                                        m_Client.Dispose();
                                        break;
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                LogUtil.LogError(ex.Message + "\r\n" + ex.StackTrace);
                            }
                        })).Start();

                        //检测客户端
                        new Thread(new ThreadStart(delegate()
                        {
                            try
                            {
                                while (true)
                                {
                                    DateTime now = DateTime.Now;
                                    if (now.Subtract(lastHeartbeat).TotalSeconds > 20)
                                    {
                                        m_ClientList.Remove(m_Client);
                                        LogUtil.Log("客户端已失去连接,当前客户端数:" + m_ClientList.Count);
                                        if (m_Client.Connected) m_Client.Disconnect(false);
                                        m_Client.Close();
                                        m_Client.Dispose();
                                        break;
                                    }
                                    Thread.Sleep(500);
                                }
                            }
                            catch (Exception ex)
                            {
                                LogUtil.LogError("检测客户端出错:" + ex.Message + "\r\n" + ex.StackTrace);
                            }
                        })).Start();
                    }
                })).Start();
                LogUtil.Log("服务已启动");
            }
            catch (Exception ex)
            {
                LogUtil.LogError("启动服务出错:" + ex.Message + "\r\n" + ex.StackTrace);
            }
        }
        #endregion

        #region 停止服务
        private void btnStopServer_Click(object sender, EventArgs e)
        {
            btnStopServer.Enabled = false;
            btnStartServer.Enabled = true;
            foreach (Socket socket in m_ClientList)
            {
                if (socket.Connected) socket.Disconnect(false);
                socket.Close();
                socket.Dispose();
            }
            if (m_ServerSocket != null)
            {
                if (m_ServerSocket.Connected) m_ServerSocket.Disconnect(false);
                m_ServerSocket.Close();
                m_ServerSocket.Dispose();
            }
            LogUtil.Log("服务已停止");
        }
        #endregion

        #region Form1_FormClosing
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach (Socket socket in m_ClientList)
            {
                if (socket.Connected) socket.Disconnect(false);
                socket.Close();
                socket.Dispose();
            }
            if (m_ServerSocket != null)
            {
                if (m_ServerSocket.Connected) m_ServerSocket.Disconnect(false);
                m_ServerSocket.Close();
                m_ServerSocket.Dispose();
            }
            LogUtil.Log("服务已停止");
        }
        #endregion

    }
}

三、服务端的服务接口类:

DemoService:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DataService
{
    /// <summary>
    /// socket服务
    /// </summary>
    public class DemoService
    {
        public string Test(string str, int n)
        {
            return str + ":" + n;
        }

        public void Test2(string str, int n)
        {
            string s = str + n;
        }
    }
}

DemoService2:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataService
{
    public class DemoService2
    {
        public string RunTest(string str, int n)
        {
            return str + ":" + n;
        }
    }
}

三、客户端Socket通信代码:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using DataStruct;
using CommonDll;

namespace Common.Utils
{
    /// <summary>
    /// 服务
    /// </summary>
    public class ServiceUtil
    {
        public static Socket clientSocket;
        public static object _lock = new object();

        /// <summary>
        /// 连接服务器
        /// </summary>
        public static void ConnectServer()
        {
            if (clientSocket == null || !clientSocket.Connected)
            {
                if (clientSocket != null)
                {
                    clientSocket.Close();
                    clientSocket.Dispose();
                }
                string ip = ConfigurationManager.AppSettings["ServerIP"];
                IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), 3001);
                clientSocket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                clientSocket.SendTimeout = 20000;
                clientSocket.ReceiveTimeout = 20000;
                clientSocket.Connect(ipep);
            }
        }

        /// <summary>
        /// 请求
        /// </summary>
        public static object Request(SocketData data)
        {
            lock (_lock)
            {
                try
                {
                    ConnectServer();

                    clientSocket.Send(SerializeUtil.Serialize(data));

                    byte[] inBuffer = new byte[clientSocket.ReceiveBufferSize];
                    int count = clientSocket.Receive(inBuffer, clientSocket.ReceiveBufferSize, SocketFlags.None);//如果接收的消息为空 阻塞 当前循环
                    if (count > 0)
                    {
                        return SerializeUtil.Deserialize(inBuffer);
                    }
                    else
                    {
                        if (clientSocket.Connected) clientSocket.Disconnect(false);
                        clientSocket.Close();
                        clientSocket.Dispose();
                        return Request(data);
                    }
                }
                catch (Exception ex)
                {
                    if (clientSocket.Connected) clientSocket.Disconnect(false);
                    LogUtil.LogError(ex.Message);
                    throw ex;
                }
            }
        }

        /// <summary>
        /// 请求
        /// </summary>
        public static object Request(string className, string methodName, object[] param)
        {
            SocketData data = new SocketData();
            data.className = className;
            data.functionName = methodName;
            data.funParam = param;
            return ServiceUtil.Request(data);
        }
    }
}

六、客户端接口类代码:

DemoService:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataStruct;
using Common.Utils;
using System.Reflection;

namespace ClientService
{
    public class DemoService
    {
        public string Test(string str, int n)
        {
            object result = ServiceUtil.Request(this.GetType().Name,
                 MethodBase.GetCurrentMethod().Name,
                 new object[] { str, n });
            return result.ToString();
        }

        public void Test2(string str, int n)
        {
            object result = ServiceUtil.Request(this.GetType().Name,
                 MethodBase.GetCurrentMethod().Name,
                 new object[] { str, n });
        }
    }
}

DemoService2:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Common.Utils;
using DataStruct;

namespace ClientService
{
    public class DemoService2
    {
        public string RunTest(string str, int n)
        {
            object result = ServiceUtil.Request(this.GetType().Name,
                MethodBase.GetCurrentMethod().Name,
                new object[] { str, n });
            return result.ToString();
        }
    }
}

六:客户端测试代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Common.Utils;
using CommonDll;
using DataStruct;
using ClientService;

namespace XXPLClient
{
    public partial class Form1 : Form
    {
        #region 变量
        private System.Timers.Timer m_timer;
        #endregion

        #region Form1构造函数
        public Form1()
        {
            InitializeComponent();
        }
        #endregion

        #region Form1_Load
        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                //连接服务器
                ServiceUtil.ConnectServer();
            }
            catch (Exception ex)
            {
                MessageBox.Show("连接服务器失败:" + ex.Message);
            }

            //心跳包
            m_timer = new System.Timers.Timer();
            m_timer.Interval = 5000;
            m_timer.Elapsed += new System.Timers.ElapsedEventHandler((obj, eea) =>
            {
                try
                {
                    lock (ServiceUtil._lock)
                    {
                        SocketData data = new SocketData();
                        data.cmdType = CmdType.Heartbeat;
                        ServiceUtil.ConnectServer();
                        ServiceUtil.clientSocket.Send(SerializeUtil.Serialize(data));
                    }
                }
                catch (Exception ex)
                {
                    LogUtil.LogError("向服务器发送心跳包出错:" + ex.Message);
                }
            });
            m_timer.Start();
        }
        #endregion

        private void btnTest_Click(object sender, EventArgs e)
        {
            try
            {
                DemoService demoService = new DemoService();
                DemoService2 demoService2 = new DemoService2();
                MessageBox.Show(demoService.Test("测试DemoService", 1) + "\r\n" + demoService2.RunTest("测试DemoService2", 2));
            }
            catch (Exception ex)
            {
                LogUtil.LogError(ex.Message);
                MessageBox.Show(ex.Message);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            m_timer.Stop();
            if (ServiceUtil.clientSocket != null)
            {
                if (ServiceUtil.clientSocket.Connected) ServiceUtil.clientSocket.Disconnect(false);
                ServiceUtil.clientSocket.Close();
                ServiceUtil.clientSocket.Dispose();
            }
        }

    }
}

时间: 2024-11-08 23:33:14

C# .NET Socket封装的相关文章

ACE - 代码层次及Socket封装

原文出自http://www.cnblogs.com/binchen-china,禁止转载. ACE源码约10万行,是c++中非常大的一个网络编程代码库,包含了网络编程的边边角角.在实际使用时,并不是所有代码都能用到你的项目中来,相反你只需要其中的一小部分就已经可以完成实际所需. 最近研究其源码最大的感受就是代码量大,资料少,逻辑跳跃大.网上搜了下ACE方面的书籍和资料,也是皮毛上打滚,概念满天飞,侧重讲解如何使用其框架,复杂的底层代码和实现都避而不谈,不如直接看源码来的直接.ACE代码目录结构

Linux C socket 封装

/************************************************************************** * Linux C socket 封装 * 声明: * 同样的代码当然没必要敲很多遍了,一遍就够了,封起来,什么时候要用, * 什么时候就来这里拿. * * 2015-7-4 晴 深圳 南山平山村 曾剑锋 ***********************************************************************

Socket封装之聊天程序(二)

今天,学习一下socket的封装. 类图 ??首先,我们把需要封装的各个类初步的设计如下: ??接下来,我们建立类与类之间的关系:??其中,CStream类可有可无,这个类是用来封装各种读写流的. socket封装 stream类 stream.h: class CStream { public: CStream(int fd = -1); ~CStream(); void SetFd(int fd); int GetFd(); int Read(char *buf, int count); /

web前端socket封装库--giraffe

摘要: 最近在做前端的socket消息推送,使用了socket.io.js的最新版本.使用过的都知道socket.io.js是基于消息类型来通信的,如果消息类型多了就很难维护.所以本人就对socket.io.js进行了应用层的封装.命名为giraffe.js,giraffe的含义是长颈鹿,意为能够望的远. 源码:https://github.com/baixuexiyang/Giraffe      欢迎fork和star 使用: giraffe.js同时支持AMD和CMD以及node.js环境

Socket封装之聊天程序(三)

  今天,完成一下epoll的封装. 类图   首先,还是画下类图,初步设计一下.  具体函数,我们下面详解. epoll封装 EpollBase类 CEpollBase.h: class CEpollBase { public: CEpollBase(int max_events); virtual ~CEpollBase(); bool Create(int size); bool AddEvent(int fd,int events); bool ModEvent(int fd,int e

Gevent的socket协程安全性分析

一般讨论socket的并发安全性,都是指线程的安全性...而且绝大多数的情况下socket都不是线程安全的.. 当然一些框架可能会对socket进行一层封装,让其成为线程安全的...例如java的netty框架就是如此,将socket封装成channel,然后让channel封闭到一个线程中,那么这个channel的所有的读写都在它所在的线程中串行的进行,那么自然也就是线程安全的了..... 其实很早看Gevent的源码的时候,就已经看过这部分的东西了,当时就已经知道gevent的socket不

Python 使用Socket实现FTP功能

FtpServer #!/usr/bin/env python import SocketServer class MyServer(SocketServer.BaseRequestHandler):   def setup(self):      pass   def handle(self):      path='/tmp'      while True:          print self.request,self.client_address,self.server       

Socket的简单使用

注意:这里是封装出来一个工具类 创建类方法来实现客户端和服务端的socket的创建 和 分别的一些必须实现的方法 socket 通信需要一个客户端和一个服务端 客户端和服务端都得有一个socket (类似于用于通信的手机)int socket的方法 服务器端要做的操作有: 1.创建socket 2.绑定一个众所周知的地址(bind)3.处于监听状态 (listen) 4.随时处于接受请求,作出响应,accept 5.接受信息 recv 客户端要做的操作的有:1.创建socket 2.实现链接(c

Java实现简单的socket通信

今天学习了一下java如何实现socket通信,感觉难点反而是在io上,因为java对socket封装已经很完善了. 今天代码花了整个晚上调试,主要原因是io的flush问题和命令行下如何运行具有package的类,不过最后问题基本都解决了,把代码贴出来供大家参考 server public class TcpServer { public static void main(String[] args) throws Exception { ServerSocket server = new S