Maison  >  Article  >  développement back-end  >  Tutoriel sur l'utilisation du framework Socket en C#

Tutoriel sur l'utilisation du framework Socket en C#

黄舟
黄舟original
2017-09-18 11:19:151901parcourir

En raison du problème de transmission Socket dans un projet récent, j'ai décidé de l'étudier et de résumer et partager ce que j'ai appris. L'article suivant vous présente principalement les informations pertinentes sur l'utilisation du framework Socket simple et pratique en C# .NET. , l'article le présente en détail à travers un exemple de code, les amis dans le besoin peuvent s'y référer.

Préface

En ce qui concerne Socket, tout le monde doit y avoir été plus ou moins impliqué Depuis le cours initial sur les réseaux informatiques, TCP. protocole, et Socket est une encapsulation supplémentaire du protocole, facilitant la communication entre nos développeurs entre les logiciels.

Je viens d'accepter un projet de serrure pour espace de stationnement partagé cette semaine. Il doit utiliser Socket pour communiquer avec le matériel, pour parler franchement, cela signifie envoyer des instructions à la serrure pour contrôler son ouverture ou sa fermeture, puis. ouvrir l'interface de fonctionnement de l'application. C'est pratique pour les tests et l'utilisation par l'utilisateur. Le cœur de ceci est l'utilisation de Socket.Après avoir développé cette fonction, je l'ai trouvé très peu pratique à utiliser, j'ai donc passé 2 jours à résumer ses fonctions principales et à les encapsuler dans un framework. Enfin, j'ai utilisé ce framework pour reconstruire le projet original. et le mettre en ligne. Cela améliore grandement l'évolutivité, la robustesse et la tolérance aux pannes du logiciel.

Principes auxquels je crois fermement : Tout est objet

D'accord, fini les bêtises, entrons le texte

Texte :

1. Tout d’abord, parlons brièvement de l’utilisation simple de Socket en C#.

Étape 1 : Le serveur écoute un certain port

Étape 2 : Le client initie une demande de connexion Socket à l'adresse et au port du serveur

Étape 3 : Le serveur crée une connexion Socket après avoir reçu la demande de connexion et maintient la file d'attente de connexion.

Étape 4 : Le client et le serveur ont établi une communication duplex (c'est-à-dire une communication bidirectionnelle). Le client et le serveur peuvent s'envoyer des informations facilement et commodément.

Quant au code d'implémentation simple, je l'ai encapsulé dans le projet. Si vous avez besoin d'apprendre une implémentation simple, vous pouvez consulter mon code source ou Baidu. Il existe de nombreux tutoriels

<.>2. Core, utilisation du framework

En fait, c'est peut-être un peu exagéré de dire que c'est un framework, car chacun a sa propre compréhension du framework, mais quel est le essence des bibliothèques de classes et des frameworks ? Quelle est la différence ? Tout n'est que du code~haha, c'est trop loin

Tout d'abord, mettons tout le code en l'air :

Fichier source du serveur :

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
 }
}
Ce qui précède est le code Framework, présenter comment utiliser

Tout d'abord, comment utiliser le serveur :


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;
   }
  }
 }
}
Comment utiliser le client :


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

Capture d'écran du dernier test exécuté :

Résumé :

La chose la plus pratique est qu'il résume comment créer une connexion. Les utilisateurs doivent uniquement se concentrer sur les données envoyées après la connexion, sur la manière de traiter les données après leur réception et sur de nombreux autres événements. , qui repose principalement sur l'utilisation de délégués anonymes et l'utilisation d'expressions Lambda.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn