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 类