ホームページ  >  記事  >  バックエンド開発  >  C# での Socket フレームワークの使用に関するチュートリアル

C# での Socket フレームワークの使用に関するチュートリアル

黄舟
黄舟オリジナル
2017-09-18 11:19:151959ブラウズ

最近のプロジェクトでソケット送信の問題が発生したため、それを研究し、学んだことを要約して共有することにしました。次の記事では、主に C# .NET でのソケットのシンプルで実用的なフレームワークの使用に関する関連情報を紹介します。この記事ではサンプルコードによる紹介が非常に詳しく書かれているので、困っている友人は参考にしてください。

はじめに

Socket に関して言えば、最初のコンピューター ネットワークのコースから、誰もが多かれ少なかれ関わったことがあるはずですが、Socket はそのプロトコルをさらにカプセル化したものです。開発者 ソフトウェア間の通信がますます容易になります。

今週、共有駐車スペースのロックのプロジェクトを受注したところです。ハードウェアと通信するために Socket を使用する必要があります。これは、ロックの開閉を制御する命令を送信し、操作インターフェイスを開くことを意味します。テストやユーザーの使用に便利なように、アプリに追加します。この機能の核となるのは Socket の使用です。この機能を開発した後、非常に使いにくいことがわかったので、2 日かけてそのコア機能を抽象化し、フレームワークにカプセル化しました。それをオンラインにすると、ソフトウェアの拡張性、堅牢性、耐障害性が大幅に向上します。

私が強く信じている原則: すべてはオブジェクトです

さて、これ以上ナンセンスではなく、本文に入りましょう

本文:

まず、Socket の簡単な使い方について簡単に説明します。 C#で。

ステップ 1: サーバーは特定のポートをリッスンします

ステップ 2: クライアントはサーバーのアドレスとポートへのソケット接続リクエストを開始します

ステップ 3: サーバーは接続リクエストを受信した後にソケット接続を作成し、それを維持しますこの接続キュー。

ステップ 4: クライアントとサーバーは二重通信 (つまり、双方向通信) を確立しており、クライアントとサーバーは相互に簡単かつ便利に情報を送信できます。

簡単な使用のための具体的な実装コードについては、プロジェクトにカプセル化しました。簡単な実装を学ぶ必要がある場合は、私のソースコードまたはBaiduを参照してください。コアの使用。とフレームワーク

実際、フレームワークについては人それぞれの理解があるため、これをフレームワークと呼ぶのは少し突飛かもしれませんが、クラス ライブラリとフレームワークの本質的な違いは何でしょうか?すべてコードです~笑、遠すぎます

まず、すべてのコードを最初に置きます:

サーバーソースファイル:

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);
    }
   }
  }
 }
}


最後にテストを実行します。スクリーンショット:

概要:

最も便利なのは、接続の作成方法と、接続後に送信されるデータの処理方法のみに集中すればよいことです。多くのイベントの処理は、主に匿名デリゲートの使用とラムダ式の使用に依存します。

以上がC# での Socket フレームワークの使用に関するチュートリアルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。