c#_TcpListener&TcpClient

Posted x1angzeeD.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c#_TcpListener&TcpClient相关的知识,希望对你有一定的参考价值。

本篇博客示例适用于上位机与机器人之间的通讯

①  首先先介绍一下TcpClient和TcpListener:

    1、TcpClient 类

    为 TCP 网络服务提供客户端连接。
TcpClient 类提供了一些简单的方法,用于在同步阻塞模式下通过网络来连接、发送和接收流数据。

为使 TcpClient 连接并交换数据,使用 TCP ProtocolType 创建的 TcpListener 或 Socket 必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接到该侦听器:

创建一个 TcpClient,并调用三个可用的 Connect 方法之一。
使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。
 
要发送和接收数据,请使用 GetStream 方法来获取一个 NetworkStream。调用 NetworkStream 的 Write 和 Read 方法与远程主机之间发送和接收数据。使用 Close 方法释放与 TcpClient 关联的所有资源。

     2、TcpListener 类

    从 TCP 网络客户端侦听连接。
TcpListener 类提供一些简单方法,用于在阻塞同步模式下侦听和接受传入连接请求。可使用 TcpClient 或 Socket 来连接 TcpListener。可使用 IPEndPoint、本地 IP 地址及端口号或者仅使用端口号,来创建 TcpListener。可以将本地 IP 地址指定为 Any,将本地端口号指定为 0(如果希望基础服务提供程序为您分配这些值)。如果选择这样做,可使用 LocalEndpoint 来标识已指定的信息。

Start 方法用来开始侦听传入的连接请求。Start 将对传入连接进行排队,直至您调用 Stop 方法或它已经完成 MaxConnections 排队为止。可使用 AcceptSocket 或 AcceptTcpClient 从传入连接请求队列提取连接。这两种方法将阻塞。如果要避免阻塞,可首先使用 Pending 方法来确定队列中是否有可用的连接请求。

调用 Stop 方法来关闭 TcpListener。

注意    Stop 方法不会关闭任何已接受的连接。需要用户负责分别关闭这些连接。 

②  完整代码示例:

代码结构简介:1、面向接口编程​    2、单例模式​​​​​​    3、异步

public interface IAsyncTcpSever

    bool IsRunning  get; 
    List<Object> Clients  get;   //连入的客户端列表
    IPAddress Address  get; 
    int Port  get; 
    void StartSever();
    void StartSever(int backlog);
    void StopSever();
    void Send(TCPClientState state, string msg);  //发消息选择指定的客户端在客户端列表中选择



public class x1angzzzAsyncTcpSever : IAsyncTcpSever


#region  全局变量
private TcpListener _listener;  // 服务器使用的异步TcpListener
private List<Object> _clients;  // 客户端列表
private Encoding _encoding;     // 编码格式
#endregion

#region  属性
public bool IsRunning  get; private set;   // 服务器是否正在运行
public List<Object> Clients  get  return _clients;    // 客户端
public IPAddress Address  get; private set;   // IP地址
public int Port  get; private set;            // 端口号
#endregion

#region 构造函数
private static x1angzzzAsyncTcpSever _instance;
private static object Singleton_Lock = new object();
public static x1angzzzAsyncTcpSever GetInstance(int listenPort, Encoding encoding)

    if (_instance == null)
    
        lock (Singleton_Lock)
        
            if (_instance == null)
                _instance = new x1angzzzAsyncTcpSever(listenPort, encoding);
        
    
    return _instance;

public static x1angzzzAsyncTcpSever GetInstance(IPAddress localIPAddress, int listenPort, Encoding encoding)

    if (_instance == null)
    
        lock (Singleton_Lock)
        
            if (_instance == null)
                _instance = new x1angzzzAsyncTcpSever(localIPAddress, listenPort, encoding);
        
    
    return _instance;

private x1angzzzAsyncTcpSever(int listenPort, Encoding encoding)     // 任意IP均可
    : this(IPAddress.Any, listenPort, encoding)  
private x1angzzzAsyncTcpSever(IPAddress localIPAddress, int listenPort, Encoding encoding)  // 常规IP+Port+编码格式

    Address = localIPAddress;
    Port = listenPort;
    this._encoding = encoding;

    _clients = new List<Object>();
    _listener = new TcpListener(Address, Port);
    _listener.AllowNatTraversal(true);

#endregion

#region 开启/停止/发送/接受消息的事件处理函数
public void StartSever()

    if (!IsRunning)
    
        IsRunning = true;
        _listener.Start();
        _listener.BeginAcceptTcpClient(
          new AsyncCallback(HandleTcpClientAccepted), _listener);
    
    else
        MessageBox.Show("服务端正在运行!", "Infromation", MessageBoxButtons.OK,
        MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);

public void StartSever(int backlog)  // 服务器所允许的挂起连接序列的最大长度

    if (!IsRunning)
    
        IsRunning = true;
        _listener.Start(backlog);
        _listener.BeginAcceptTcpClient(
          new AsyncCallback(HandleTcpClientAccepted), _listener);
    
    else
        MessageBox.Show("服务端正在运行!", "Infromation", MessageBoxButtons.OK,
        MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);

public void StopSever()

        if (IsRunning)
        
        var dr = MessageBox.Show("是否主动关闭客户端?", "Warning",           MessageBoxButtons.OKCancel,
         MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
        if (dr == DialogResult.Cancel) return;
        IsRunning = false;
        _listener.Stop();
        lock (_clients)
        
            CloseAllClient();  // 关闭所有客户端连接
            _listener.Stop();
        

GC.SuppressFinalize(this);在对象创建的时候CLR会对带有终结器的对象进行标记(这使创建成本变高)


public void Send(TCPClientState state, string msg)

    byte[] data = System.Text.Encoding.Default.GetBytes(msg);
    Send(state.TcpClient, data);

public void ReceivedRecallMethod(string msg)

    //接收消息之后的事件处理函数

#endregion

#region 内部函数
private void HandleDataReceived(IAsyncResult ar)  // 数据接受回调函数

    if (IsRunning)
    
        TCPClientState state = (TCPClientState)ar.AsyncState;
        NetworkStream stream = state.NetworkStream;

        int recv = 0;
        try  recv = stream.EndRead(ar); 
        catch  recv = 0; 

        if (recv == 0)
        
            lock (_clients)  // 连接已经被关闭
            
                _clients.Remove(state);
                return;
            
        
        byte[] buff = new byte[recv];  // received byte and trigger event notification
        Buffer.BlockCopy(state.Buffer, 0, buff, 0, recv);
        string str = string.Empty;
        if (recv != 0)
        
            str = _encoding.GetString(buff, 0, recv);
            Action<string> action = ReceivedRecallMethod;
            action.Invoke(str);
        

        // continue listening for tcp datagram packets   
        stream.BeginRead(state.Buffer, 0, state.Buffer.Length, HandleDataReceived, state);

    

private void CloseAllClient()

    try
    
        foreach (TCPClientState client in _clients) //这里好像报了一个无关紧要的错,所以我加了个try,catch
        
            if (client != null)
            
                client.Close();
                _clients.Remove(client);
            
        
    
    catch  
    _clients.Clear();

private void Send(TcpClient client, byte[] data)

    if (!IsRunning)
    
        var dr = MessageBox.Show("服务端尚未启用!是否启用?", "Warning", MessageBoxButtons.OKCancel,
        MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
        if (dr == DialogResult.OK)
        
            //这里调用StartSever方法,怎样写看你实际应用
        
        return;
    
    if (client == null)
    
        MessageBox.Show("未检测到客户端!", "Warning", MessageBoxButtons.OK,
       MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
        return;
    
    if (data == null)
    
        MessageBox.Show("发送内容不可为空!", "Error", MessageBoxButtons.OK,
       MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
        return;
    
    client.GetStream().BeginWrite(data, 0, data.Length,
        (IAsyncResult ar) =>  ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar); , client);

#endregion 




public class TCPClientState

    public TcpClient TcpClient  get; private set; 

    public byte[] Buffer  get; private set; 

    public NetworkStream NetworkStream
    
        get  return TcpClient.GetStream(); 
    

    public TCPClientState(TcpClient tcpClient, byte[] buffer)
    
        if (tcpClient == null)
            throw new ArgumentNullException("tcpClient");
        if (buffer == null)
            throw new ArgumentNullException("buffer");

        this.TcpClient = tcpClient;
        this.Buffer = buffer;
    

    public void Close()
    
        //关闭数据的接受和发送
        TcpClient.Close();
        Buffer = null;
    

 

以上是关于c#_TcpListener&TcpClient的主要内容,如果未能解决你的问题,请参考以下文章

TCPListener和TCPClient之间的通信代码

c# tcplistener问题

golang - interface的作用

C# 在客户端和服务器上使用 TcpListener

在关闭其关联的套接字时取消侦听任务

TcpListener 类