boxmoe_header_banner_img

Hello! 欢迎来到我的博客!

加载中

文章导读

Unity基础网络开发(1)——Socket连接


avatar
xiaoifei 2024年4月11日 485

知识引入

弱联网和强联网游戏

  • 弱联网游戏:不会频繁的进行数据通信,通常服务端处理完客户端请求就断开连接,例如一些休闲游戏
  • 强联网游戏:频繁进行数据通信,游戏核心逻辑在服务端处理,与客户端不停同步信息,例如MOBA,MMO等

TCP、UDP 和 Socket

TCP:TCP 是一种传输层协议,提供面向连接的、可靠的数据传输服务

TCP特点:

1. 面向连接——两者之间必须建立可靠的连接

2. 一对一——只能是1对1的建立连接

3. 可靠性高——消息传送失败会重新发送,不允许丢包

4. 有序的——是按照顺序进行消息发送的

UDP:UDP 是一种传输层协议,提供无连接的、不可靠的数据传输服务

UDP特点:

1. 无连接 —— 两者之间无需建立连接

2. n对n —— TCP只能1对1连接进行消息传递,而UDP由于无连接所以可以n对n

3. 可靠性低 —— 消息可能在传送过程中丢失,丢失后不会重发

4. 传输效率高 —— 由于它的可靠性低并且也无需建立连接,所以传输效率上更高一些

Socket(套接字):一种编程接口,使用TCP或UDP协议进行网络通信

知识点

IPAdress类

//1.通过Byte数组
byte[] ipAddress = new byte[] { 118, 102, 111, 11 };
IPAddress ip1 = new IPAddress(ipAddress);
//2.通过Long初始化
IPAddress ip2 = new IPAddress(0x7666F0B);
//3.通过字符串转换(推荐)
IPAddress ip3 = IPAddress.Parse("118.102.111.11");

IPEndPoint类

IPEndPoint类将网络端点表示为IP地址和端口号,表现为IP地址端口号的组合

//初始化方式
IPEndPoint ipPoint = new IPEndPoint(0x79666F0B, 8080);
IPEndPoint ipPoint2 = new IPEndPoint(IPAddress.Parse("118.102.111.11"), 8080);

Socket类

语法:Socket socketTcp = new Socket(参数1, 参数2, 参数3);

每一个参数都由枚举类型构成,下面列出每个枚举变量常用的参数

参数1:

InterNetwork IPv4寻址
InterNetwork6 IPv6寻址

参数2:
Dgram 支持数据报,最大长度固定的无连接、不可靠的消息(主要用于UDP通信)
Stream 支持可靠、双向、基于连接的字节流(主要用于TCP通信)

参数3:

TCP TCP传输控制协议
UDP UDP用户数据报协议

而这里列出常用的搭配方式

2、3参数的常用搭配:
SocketType.Dgram + ProtocolType.Udp = UDP协议通信(常用,主要学习)
SocketType.Stream + ProtocolType.Tcp = TCP协议通信(常用,主要学习)
SocketType.Raw + ProtocolType.Icmp = Internet控制报文协议(了解)
SocketType.Raw + ProtocolType.Raw = 简单的IP包通信(了解)

如何在服务端和客户端间建立通信

  1. 客户端:

    1. 创建套接字Socket

    2. 用Connect方法与服务器相连接

    3. 用Send和Receive相关方法收发数据

    4. 用Shutdown方法释放连接

    5. 关闭套接字
  2. 服务端:

    1. 创建套接字Socket

    2. 用Bind方法将套接字与本地地址绑定

    3. 用Listen方法监听

    4. 用Accept方法等待客户端连接

    5. 建立连接,Accept返回新套接字

    6. 用Send和Receive相关方法收发数据

    7. 用Shutdown方法释放连接

    8. 关闭套接字

 

实战

服务端

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace LearnTcpServer;

class Program
{
    static void Main(string[] args)
    {
        // 1. 创建套接字Socket
        Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        // 2. 用Bind方法将套接字与本地地址绑定
        try
        {
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
            socketTcp.Bind(ipEndPoint);
        }
        catch (Exception e)
        {
            Console.WriteLine("绑定失败"+e.Message);
            throw;
        }
        // 3. 用Listen方法监听
        socketTcp.Listen(1);
        Console.WriteLine("服务端绑定监听结束,等待客户端连入");
        // 4. 用Accept方法等待客户端连接
        // 5. 建立连接,Accept返回新套接字
        Socket socketClient = socketTcp.Accept();
        // 6. 用Send和Receive相关方法收发数据
        //发送
        socketClient.Send(Encoding.UTF8.GetBytes("Welcome to Connect to Server!"));
        //接收
        byte[] result = new byte[1024];//1kb
        int receiveNum = socketClient.Receive(result);
        Console.WriteLine("Received Message From {0}:{1}",socketClient.RemoteEndPoint.ToString(),Encoding.UTF8.GetString(result,0,receiveNum));
        // 7. 用Shutdown方法释放连接
        socketClient.Shutdown(SocketShutdown.Both);
        // 8. 关闭套接字
        socketClient.Close();
        
        Console.WriteLine("服务端已关闭,按任意键退出");
        Console.ReadKey();
    }
}

 

客户端

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace LearnTcpClient
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 1. 创建套接字Socket
            Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            // 2. 用Connect方法与服务器相连接
            try
            {
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
                socketTcp.Connect(ipEndPoint);
            }
            catch (SocketException e)
            {
                if (e.ErrorCode == 10061)
                {
                    Console.WriteLine("服务器拒绝连接");
                }
                else
                {
                    Console.WriteLine("连接失败" + e.ErrorCode);
                }
                return;
            }
            // 3. 用Send和Receive相关方法收发数据
            byte[] result = new byte[1024];
            int receiveNum = socketTcp.Receive(result);
            Console.WriteLine($"Received Message From {socketTcp.RemoteEndPoint.ToString()}:{Encoding.UTF8.GetString(result, 0, receiveNum)}");

            socketTcp.Send(Encoding.UTF8.GetBytes("Hello i'm client!"));
            // 4. 用Shutdown方法释放连接
            socketTcp.Shutdown(SocketShutdown.Both);
            // 5. 关闭套接字
            socketTcp.Close();
            
            Console.WriteLine("客户端已关闭,按任意键退出");
	        Console.ReadKey();
        }
    }
}

首先运行服务端然后再运行客户端,服务端先发送一个消息Welcome to Connect to Server!,然后客户端接收消息打印,紧接着发送Hello i’m client!随后关闭,服务端在接收到客户端消息后打印,随后关闭,整个流程是一次性的。

问题

在主线程中会经历以下卡顿阶段:

1. 服务端Accept卡顿,这是由于服务端在一直等待客户端连接,直到客户端连上才能继续执行

2.Receive接收消息卡顿,一般由于网络情况所导致

当然还有收发消息不可控,服务端只能连接一个客户端的局限性

要解决以上问题,需要引入多线程的知识,在下一节将用多线程解决这几个问题

 



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码