C#学习笔记【十三】—— 网络

13.1 socket(套接字)编程

13.1.1 Tcp

基于Tcp协议的Socket通讯类似于B/S架构,面向连接,但不同的是服务器端可以向客户端主动推送消息。
 
使用Tcp协议通讯需要具备以下几个条件:
  (1).建立一个套接字(Socket)
  (2).绑定服务器端IP地址及端口号–服务器端
  (3).利用Listen()方法开启监听–服务器端
  (4).利用Accept()方法尝试与客户端建立一个连接–服务器端
  (5).利用Connect()方法与服务器建立连接–客户端
  (6).利用Send()方法向建立连接的主机发送消息
  (7).利用Recive()方法接受来自建立连接的主机的消息(可靠连接)
在这里插入图片描述

namespace c_sharp_socket编程_TCP协议_服务器端
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1创建socket
            Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 2绑定IP和端口号 192.168.1.101
            IPAddress ipAddres = new IPAddress(new byte[] { 192, 168, 1, 101 });
            EndPoint point = new IPEndPoint(ipAddres,7788);//IPEndPoint是对IP+端口做了一层封装的类
            tcpServer.Bind(point);//向操作系统申请一个可用的IP和端口号,用来通信

            // 3开始监听(等待客户端连接)
            tcpServer.Listen(100);//参数是最大连接数
            Console.WriteLine("开始监听");
            Socket clientSocket = tcpServer.Accept();//暂停当前线程,直到有一个客户端链接过来
            Console.WriteLine("一个客户端连接成功");

            // 4使用返回的socket与客户端做通信
            string message = "Hello 欢迎你";
            byte[] data = Encoding.UTF8.GetBytes(message);//对字符串做编码,得到一个字符串的字节数组
            clientSocket.Send(data);
            Console.WriteLine("向客户端发送消息");

            byte[] data2 = new byte[1024];
            int length = clientSocket.Receive(data2);//这里传递一个byte数组,data数组用来接收数据
                                                 //length返回值表示接收了多少字节的数据
            string message2 = Encoding.UTF8.GetString(data2, 0, length);
            Console.WriteLine("接收到一个客户端发送来的消息:",message2);

            Console.ReadKey();
        }
    }
}
namespace c_sharp_socket编程_ TCP协议_客户端
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1创建socket
            Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 2发起建立连接请求
            IPAddress ipAddress = IPAddress.Parse("192.168.1.101");//可以把一个字符串的ip地址转化为IPAddress															的对象
            EndPoint point = new IPEndPoint(ipAddress, 7788);
            tcpClient.Connect(point);//通过ip:端口号定位一个要连接到的服务器

            byte[] data = new byte[1024];
            int length = tcpClient.Receive(data);//这里传递一个byte数组,data数组用来接收数据
                                                //length返回值表示接收了多少字节的数据
            string message = Encoding.UTF8.GetString(data,0,length);
            Console.WriteLine(message);

            // 3向服务器端发送消息
            string message2 = Console.ReadLine();
            tcpClient.Send(Encoding.UTF8.GetBytes(message2));
            Console.ReadKey();
        }
    }
}

13.1.2 Udp

基于Udp协议是无连接模式通讯,占用资源少,响应速度快,延时低。至于可靠性,可通过应用层的控制来满足。(不可靠连接)
  (1).建立一个套接字(Socket)
  (2).绑定服务器端IP地址及端口号–服务器端
  (3).通过SendTo()方法向指定主机发送消息(需提供主机IP地址及端口)
  (4).通过ReciveFrom()方法接收指定主机发送的消息(需提供主机IP地址及端口)
在这里插入图片描述

namespace c_sharp_practice10_UDP协议_服务器端
{
    class Program
    {
        static Socket udpServer;
        static void Main(string[] args)
        {
            // 1创建Socket
            udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            // 2绑定IP:Port
            udpServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));

            // 3接收数据
            new Thread(ReceiveMessage) { IsBackground = true }.Start();

            //udpServer.Close();
            Console.ReadKey();
        }
        static void ReceiveMessage()
        {
            while (true)
            {
                EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = new byte[1024];
                int length = udpServer.ReceiveFrom(data, ref remoteEndPoint);//这个方法会把数据的来源放到第二个参数上
                string message = Encoding.UTF8.GetString(data);
                Console.WriteLine("从" + (remoteEndPoint as IPEndPoint).Address.ToString() + ":" +
                    (remoteEndPoint as IPEndPoint).Port + "收到了消息");
            }
        }
    }
}
namespace c_sharp_practice10_UDP协议_客户端
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1创建Socket
            Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            // 2发送数据
            while (true)
            {
                EndPoint serverPoint = new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788);
                string message = Console.ReadLine();
                byte[] data = new byte[1024];
                udpClient.SendTo(data, serverPoint);
            }

            udpClient.Close();
            Console.ReadKey();
        }
    }
}

TCP协议和UDP协议连接过程的区别:
(1)基于连接与无连接;
(2)对系统资源的要求(TCP较多,UDP少);
(3)UDP程序结构较简单;
(4)流模式与数据报模式 ;
(5)TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。

13.1.3 Unity聊天室-TCP协议

namespace c_sharp_聊天室_服务器端
{
    //用来与客户端做通信
    class Client
    {
        private Socket clientSocket;
        private Thread t;
        private byte[] data = new byte[1024];//数据容器
        public Client(Socket s)
        {
            clientSocket = s;
            //启动一个线程处理客户端数据的接收
            t = new Thread(ReceiveMessage);
            t.Start();
        }

        private void ReceiveMessage()
        {
            //一直接收客户端的数据
            while (true)
            {
                //安全校验,判断socket连接是否断开
                if (clientSocket.Poll(5,SelectMode.SelectRead))
                {
                    clientSocket.Close();
                    break;
                }
                int length = clientSocket.Receive(data);
                string message = Encoding.UTF8.GetString(data, 0, length);
                //服务器接收到数据后,把数据分发到各个客户端
                Program.BroadcastMessage(message);
                Console.WriteLine("服务器接收到:" + message);
            }
        } 

        public void SendMessage(string message)
        {
            clientSocket.Send(Encoding.UTF8.GetBytes(message));
        }

        public bool Connected
        {
            get{ return clientSocket.Connected; }
        }
    }
}
namespace c_sharp_聊天室_服务器端
{
    class Program
    {
        static List<Client> clientList = new List<Client>();//创建一个客户端对象的数组,管理每一个连接到的客														户端
        //服务器端在客户端广播消息
        public static void BroadcastMessage(string message)
        {
            var notConnectedList = new List<Client>();
            foreach (var client in clientList)
            {
                if (client.Connected)
                {
                    client.SendMessage(message);
                }
                else
                {
                    notConnectedList.Add(client);
                }
            }
            foreach(var notClient in notConnectedList)
            {
                clientList.Remove(notClient);
            }
        }
        static void Main(string[] args)
        {
            //创建socket并绑定IP:端口
            Socket tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            tcpServer.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));

            //监听
            tcpServer.Listen(100);
            Console.WriteLine("Server Running...");
            while (true)
            {
                Socket clientSocket = tcpServer.Accept();
                Console.WriteLine("A client is Connected (U_U)");
                Client client = new Client(clientSocket);//把与每个客户端通信的逻辑(收发消息)放到Client类													里处理
                clientList.Add(client);
            }
        }
    }
}
Unity客户端
public class ChatManger : MonoBehaviour
{
    public string ipAddress = "192.168.1.101";
    public int port = 7788;

    public UIInput textInput;
    public UILabel chatLabel;

    private Socket clientSocket;
    private Thread t;
    private byte[] data = new byte[1024];//数据容器
    private string message;//消息容器

    // Use this for initialization
    void Start()
    {
        ConnectToServer();
    }

    // Update is called once per frame
    void Update()
    {
        if (message != null && message != "")
        {
            chatLabel.text += "\n" + message;
            message = "";
        }
    }

    void ConnectToServer()
    {
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        //连接服务器端
        clientSocket.Connect(new IPEndPoint(IPAddress.Parse(ipAddress), port));

        //开启一个线程用来接收消息
        t = new Thread(ReceiveMessage);
        t.Start();
    }

    //用来循环接收消息的线程
    void ReceiveMessage()
    {
        while (true)
        {
            if (clientSocket.Connected == false)
            {
                break;
            }
            int length = clientSocket.Receive(data);
            message = Encoding.UTF8.GetString(data, 0, length);
            //chatLabel.text += "\n" + message;//Unity不允许在单独的线程中处理组件
        }
    }

    void SendMessage(string message)
    {
        clientSocket.Send(Encoding.UTF8.GetBytes(message));
    }

    public void OnSendButtonClick()
    {
        string value = textInput.value;
        SendMessage(value);
        textInput.value = "";
    }

    private void OnDestroy()
    {
        clientSocket.Shutdown(SocketShutdown.Both);
        clientSocket.Close();//关闭连接
    }
}

13.1.4 TcpClient&TcpListener

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

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

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

TcpListener:

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

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

调用 Stop 方法来关闭 TcpListener。

TcpClient:

TcpClient类提供了一些简单的方法,用于在同步阻止模式下通过网络来连接、发送和接收流数据。为使 TcpClient 连接并交换数据,使用 TCP ProtocolType 创建的 TcpListener 或 Socket 必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接到该侦听器:
(1)创建一个 TcpClient,并调用三个可用的 Connect 方法之一。
(2)使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。

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

namespace c_sharp_practice10_tcplistener
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1TcpListener对Socket进行了一层封装,会自己创建Socket对象
            TcpListener listener = new TcpListener(IPAddress.Parse("192.168.1.101"), 7788);

            // 2开始监听
            listener.Start();

            // 3等待客户端连接
            TcpClient client = listener.AcceptTcpClient();

            // 4与客户端通信
            NetworkStream stream = client.GetStream();//得到一个网络流,从这个网络流里可以取得客户端发来的数													据
            byte[] data = new byte[1024];
            while (true)
            {
                int length = stream.Read(data, 0, 1024);
                string message = Encoding.UTF8.GetString(data, 0, length);
                Console.WriteLine("服务器端接收到:" + message);
            }

            stream.Close();
            client.Close();
            listener.Stop();
            Console.ReadKey();
        }
    }
}
namespace c_sharp_practice10_tcpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建TcpClient的时候,就会与server建立连接
            TcpClient client = new TcpClient("192.168.1.101", 7788);
            NetworkStream stream = client.GetStream();//通过网络流进行数据的交换
            while (true)
            {
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                stream.Write(data, 0, data.Length);
            }

            stream.Close();
            client.Close();
            Console.ReadKey();
        }
    }
}

13.1.5 UdpClient

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

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

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

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

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

namespace c_sharp_practice10_udpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建UdpClient,绑定IP:Port
            UdpClient udpClient = new UdpClient(new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));
            //接收数据
            while (true)
            {
                IPEndPoint point = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = udpClient.Receive(ref point);//通过point确定数据来自哪个IP:Port
                string message = Encoding.UTF8.GetString(data);
                Console.WriteLine("接收到数据" + message);
            }

            udpClient.Close();
            Console.ReadKey();
        }
    }
}
namespace c_sharp_practice10_udpclient
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建UdpClient对象
            UdpClient client = new UdpClient();
            while (true)
            {
                string message = Console.ReadLine();
                byte[] data = Encoding.UTF8.GetBytes(message);
                client.Send(data, data.Length, new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));
            }

            client.Close();
            Console.ReadKey();
        }
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值