socket与TcpListener/TcpClient/UdpClient 的区别及联系

网络编程

应用程序可以通过 TCPClient、TCPListener 和 UDPClient 类使用传输控制协议
(TCP) 和用户数据文报协议 (UDP) 服务。这些协议类建立在
System.Net.Sockets.Socket
类的基础之上,负责数据传送的细节。(也就是说TCPClient、TCPListener 和
UDPClient 类是用来简化Socket)

       
使用C#进行网络编程时,通常都需要用到System.Net命名空间、System.Net.Sockets命名空间和System.Net.Mail命名空间;

TcpClient 和 TcpListener 使用 NetworkStream 类表示网络。使用 GetStream
方法返回网络流,然后调用该流的 Read 和 Write 方法。NetworkStream
不拥有协议类的基础套接字,因此关闭它并不影响套接字。

1.
System.Net命名空间:
为当前网络上使用的多种协议提供了简单的编程接口。

UdpClient 类使用字节数组保存 UDP 数据文报。使用 Send
方法向网络发送数据,使用 Receive 方法接收传入的数据文报。

  1. DNS类:DNS是一个静态类,它从Internet域名系统(DNS)检索关于特定主机的信息,在IPHostEntry类的实例中返回来自DNS查询的主机信息。(GetHostName()找到本地系统的主机名,然后再用GetHostByName()找到主机的IP地址)
  2. IPAddress类:包含计算机在IP网络上的地址,主要用来提供网际协议(IP)地址。
  3. IPEndPoint类包含应用程序连接到主机上的服务所需要的主机和本地或远程端口信息。主要用来将网络端点表示为IP地址和端口号。
  4. WebClient类:提供向URL标识的任何本地、Intranet或Internet资源发送数据以及从这些资源接收数据的公共方法。
  5. WebRequest类:.Net
    Framework的请求/响应模型的抽象基类,用于访问Internet数据。使用该请求/响应模型的应用程序可以用协议不可知的方式从Internet请求数据。

1.TcpClient

2. System.Net.Sockets命名空间

TcpClient
类提供了一些简单的方法,用于在同步阻止模式下通过网络来连接、发送和接收流数据。为使
TcpClient 连接并交换数据,使用 TCP ProtocolType 创建的 TcpListener 或
Socket
必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接到该侦听器:

       
主要提供制作Sockets网络应用程序的相关类,其中Socket类,TcpClient类、TcpListener类和UdpClient类较为常用。在.NET框架下开发时,直接使用System.Net.Sockets名称空间中的Socket类编程较为复杂,而应用层的类TcpClient、TcpListener
和 UdpClient为 Socket
通信提供了更简单、对用户更友好的接口。它们和Socket类之间的这种层次关系如图

(1)创建一个 TcpClient,并调用三个可用的 Connect 方法之一。

图片 1

(2)使用远程主机的主机名和端口号创建
TcpClient。此构造函数将自动尝试一个连接。

(1)Socket类:主要用于管理连接(将应用程序与端口连接起来,端口是一台计算机的数据都通过此连接网络,
Socket是基于流套接字协议(TCP协议)/基于数据报套接字协议(UDP协议)最为通用的API。),实现Berkeley通信端套接字接口,同时它还定义了绑定、连接网络端点及传输数据所需的各种方法。

给继承者的说明要发送和接收数据,请使用 GetStream 方法来获取一个
NetworkStream。调用 NetworkStream 的 Write 和 Read
方法与远程主机之间发送和接收数据。使用 Close 方法释放与 TcpClient
关联的所有资源。

    1. void Bind(IPEndPoint
      localEP):绑定地址,使Socket与一个本地终节点相关联。
    2. Void Connect(IPEndPoint ip):建立连接
    3. Void Listen(int backlog):将Socket置于侦听状态,
      backlog最多可连接数。
    4. Int Receive(byte[] buffer):接收数据
    5. Int Receive (byte[] buffer,ref IPEndPoint
      ip):从指定地址接收数据
    6. Int Send(byte[] buffer):发送数据
    7. Int Send To(byte[] buffer,ref IPEndPoint
      remoteIP):向指定地址发送数据
    8. Void Shutdown(SocketShutdown
      how):关闭套接字,how指定不在允许执行的操作

下面的例子给出怎么利用TcpClient连接到服务器:

注:使用accept函数建立新的连接时,并不是使用原来的Socket进行通信,而是返回一个新的Socket套接字进行连接通信。原来的Socket继续进入监听状态,等待他人的连接要求。

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
 
namespace tcpclient
{
    class Program
    {
        private static int portNum = 11000;
        private static string hostName = Dns.GetHostName().ToString();
        public static void Main(String[] args)
        {
            try
            {
                Console.WriteLine(“主机名字:”+ Dns.GetHostName());
                Console.WriteLine(“主机IP地址:”+ Dns.GetHostAddresses(Dns.GetHostName())[0]);
                TcpClient client = new TcpClient(hostName, portNum);
                NetworkStream ns = client.GetStream();
                byte[] bytes = new byte[1024];
                int bytesRead = ns.Read(bytes, 0, bytes.Length);
                //将字节流解码为字符串
                Console.WriteLine(Encoding.ASCII.GetString(bytes, 0, bytesRead));
                client.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
     
        }
    }
}

(2)TcpClient类:基于Socket类构建,用于在同步阻止模式下通过网络来连接,发送,接收数据。

2.TcpListener

       
这是它能够以更高的抽象程度提供TCP服务的基础。体现在网络数据的发送和接受方面,是TcpClient使用NetworkStream网络流处理技术,使得它读写数据更加方便直观。TcpClient类专为客户端设计,它为
TCP 网络服务提供客户端连接。

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

(3)NetworkStream网络流:可以被视为一个数据通道,架设在数据来源端(客户Client)和接收端(服务Server)之间,通过TcpClient.GetStream方法,返回用于发送和接收数据的网络流NetworkStream。

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

       
注:要创建NetworkStream必须提供连接的Socket.默认情况下关闭NetworkStream并不会关闭所提供的Socket.如果要关闭Socket权限,则必须将ownsSocket构造函数参数的值指定为true.而后的数据读取及写入均针对这个通道来进行。不支持对网络流的随机访问。

调用 Stop 方法来关闭 TcpListener。

示例如下:通过以下方法得到NetworkStream网络流之后,就可以使用标准流读写方法Write和Read来发送和接受数据了。

下面的例子给出怎么利用TcpListener监听客户端的请求:

1 TcpClient tcpClient = new TcpClient();             //创建TcpClient对象实例
2 tcpClient.Connect("www.baidu.com",4088);         //尝试与远程主机相连
3 NetworkStream stream=tcpClient.GetStream();      //获取网络传输流

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace tcpclient
{
    class Program
    {
        private const int portNum = 11000;
        static void Main(string[] args)
        {
            bool done = false;
            //TcpListener listener = new TcpListener(portNum); //根据VS2005 MSDN 此方法已经过时,不再使用
            // IPEndPoint类将网络标识为IP地址和端口号
            TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, portNum));
            listener.Start();
            while (!done)
            {
                Console.Write(“Waiting for connection…”);
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine(“Connection accepted.”);
                NetworkStream ns = client.GetStream();
                byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());
                try
                {
                    ns.Write(byteTime, 0, byteTime.Length);
                    ns.Close();
                    client.Close();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
            listener.Stop();
         }
    }
}

  1. BeginRead():从NetworkStream开始一步读取
  2. BeginWrite():开始向流异步写入
  3. Close():关闭NetworkStream
  4. EndRead():处理异步读取结束
  5. EndWrite():处理异步写入结束
  6. Flush():刷新流中的数据
  7. Read():从流中读取
  8. ReadByte():从流中读取一个字节,并将流内的位置向前推进一个字节。到达末尾,返回-1。
  9. Write():将数据写入NetworkStream
  10. WriteByte():将一个字节写入流内的当前位置,并将流内的位置向前推进一个字节。

3.UdpClient

Server:获取基础网络Socket

UdpClient 类提供了一些简单的方法,用于在阻止同步模式下发送和接收无连接
UDP 数据报。因为 UDP
是无连接传输协议,所以不需要在发送和接收数据前建立远程主机连接。但您可以选择使用下面两种方法之一来建立默认远程主机:

  1. AcceptSocket/AcceptTcpClient:接受挂起的连接请求
  2. BeginAcceptSocket/BeginAcceptTcpClient:开始一个异步操作来接受一个传入的连接尝试
  3. Start:开始侦听传入的连接请求
  4. Stop:关闭侦听

·         使用远程主机名和端口号作为参数创建 UdpClient
类的实例。

Client客户端连接侦听器的两种方法:

·         创建 UdpClient 类的实例,然后调用 Connect 方法。

(1)创建一个TcpClient,并调用3个可用Connect方法之一。

可以使用在 UdpClient 中提供的任何一种发送方法将数据发送到远程设备。使用
Receive 方法可以从远程主机接收数据。

(2)使用远程主机的主机名和端口号创建TcpClient,此构造函数将自动尝试一个连接。

UdpClient 方法还允许发送和接收多路广播数据报。使用 JoinMulticastGroup
方法可以将 UdpClient 预订给多路广播组。使用 DropMulticastGroup
方法可以从多路广播组中取消对 UdpClient 的预订。

  1. Client:获取或设置基础Socket
  2. ReceiveBufferSize:获取或设置接收缓冲区的大小
  3. SendBufferSize:获取或设置发送缓冲区的大小
  4. BeginConnect方法:开始一个对远程主机连接的异步请求
  5. Close:释放TcpClient 实例,不关闭基础连接
  6. Connect:使用指定的主机名和端口号将客户端连接到TCP主机
  7. GetStream:返回用于发送和接收数据的NetworkStream

下面的例子演示同一主机不同端口之间的UDP通信:

创建连接TCPClient连接方法:

监听端:

(1)创建一个TcpClient,并调用三个可用的Connect()方法之一。

using System;
using System.Net.Sockets;
using System.Text;
using System.Net;
using System.Threading;
namespace Udpclient2
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                UdpClient udpClient = new UdpClient(12000);
                string returnData = ”client_end”;
                do
                {
                    Console.WriteLine(“服务器端接收数据:………………………..”);
                    IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
                    // 此处通过引用传值,获得客户端的IP地址及端口号
                    Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                    //此处获得客户端的数据
                    returnData = Encoding.UTF8.GetString(receiveBytes);
                    //Encoding.ASCII.GetString(receiveBytes); 此处若用ASCII,不能正确处理中文
                    Console.WriteLine(“This is the message server received: ” + returnData.ToString());
 
                    Thread.Sleep(3000);
                 
                    Console.WriteLine(“向客户端发送数据:………………………..”);
                    udpClient.Connect(Dns.GetHostName().ToString(), 11000);
                    // Sends a message to the host to which you have connected.
                    string sendStr = ”我来自服务器端:” + DateTime.Now.ToString();
                    Byte[] sendBytes = Encoding.UTF8.GetBytes(sendStr);
                    //Byte[] sendBytes = Encoding.ASCII.GetBytes(sendStr); 此处若用ASCII,不能正确处理中文
                    udpClient.Send(sendBytes, sendBytes.Length);
                    Console.WriteLine(“This is the message server send: ” + sendStr);
 
                 } while (returnData != ”client_end”);
                
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

  1. void
    Connect(IPEndPoint):使用指定的远程网络节点将客户端连接到远程TCP主机。

    1 IPAddress ipAddress = Dns.Resolve(Dns.GetHostName()).AddressList(0);//获取IP地址
    2 IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, 60000);//设置端口
    3 TcpClient tcpClientA = new TcpClient(ipLocalEndPoint);//创建客户端
    

客户端:

 

using System;
using System.Net.Sockets;
using System.Text;
using System.Net;
namespace Udpclient
{
    class Program
    {
        static void Main(string[] args)
        {
           try
            { 
               UdpClient udpClient = new UdpClient(11000);
               
               //向服务器发送数据
               udpClient.Connect(Dns.GetHostName().ToString(), 12000);
               // Sends a message to the host to which you have connected.
               string sendStr = ”我来自客户端:” + DateTime.Now.ToString();
               Byte[] sendBytes = Encoding.UTF8.GetBytes(sendStr);
               //Byte[] sendBytes = Encoding.ASCII.GetBytes(sendStr); 此处若用ASCII,不能正确处理中文
               udpClient.Send(sendBytes, sendBytes.Length);
               Console.WriteLine(“This is the message client send: ” + sendStr);
                
               
               //等待服务器的答复,收到后显示答复,并结束对话
               IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
               // 此处通过引用传值,获得客户端的IP地址及端口号
               Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
               //此处获得服务器端的数据
               string returnData = Encoding.UTF8.GetString(receiveBytes);
               //Encoding.ASCII.GetString(receiveBytes); 此处若用ASCII,不能正确处理中文
               Console.WriteLine(“This is the message come from server: ” + returnData.ToString());
               udpClient.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注