Xây Dựng Hệ Thống Mạng Cơ Bản Trong Unity

Nguyên Tắc Mạng Cơ Sở

Các thành phần cốt lõi bao gồm địa chỉ IP, cổng kết nối và địa chỉ MAC. Địa chỉ IP xác định thiết bị trên mạng, cổng kết nối định tuyến lưu lượng đến ứng dụng cụ thể. Thư viện System.Net cung cấp lớp IPAddressIPEndPoint để quản lý thông tin kết nối:

// Khởi tạo điểm kết nối
var serverAddress = IPAddress.Parse("10.1.5.20");
var connectionPoint = new IPEndPoint(serverAddress, 7777);

Phân Giải Tên Miền

Lớp Dns thực hiện chuyển đổi tên miền thành địa chỉ IP. Kết quả trả về qua IPHostEntry chứa danh sách địa chỉ và thông tin máy chủ:

async Task ResolveDomainAsync()
{
    var hostInfo = await Dns.GetHostEntryAsync("api.example.com");
    
    foreach (var ip in hostInfo.AddressList)
    {
        Debug.Log($"Địa chỉ IP: {ip}");
    }
    
    Debug.Log($"Tên máy chủ: {hostInfo.HostName}");
}

Giao Tiếp Socket TCP

Máy chủ:

var listener = new Socket(AddressFamily.InterNetwork, 
                         SocketType.Stream, 
                         ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, 7777));
listener.Listen(5);

// Chấp nhận kết nối không đồng bộ
var clientSocket = await listener.AcceptAsync();

// Gửi phản hồi
var response = Encoding.UTF8.GetBytes("Kết nối thành công!");
await clientSocket.SendAsync(response);

// Nhận dữ liệu
var buffer = new byte[1024];
var received = await clientSocket.ReceiveAsync(buffer);
Debug.Log(Encoding.UTF8.GetString(buffer, 0, received));

Máy khách:

var client = new Socket(AddressFamily.InterNetwork, 
                       SocketType.Stream, 
                       ProtocolType.Tcp);
try 
{
    await client.ConnectAsync("10.1.5.20", 7777);
    
    // Gửi yêu cầu
    var request = Encoding.UTF8.GetBytes("Yêu cầu dữ liệu");
    await client.SendAsync(request);
    
    // Xử lý phản hồi
    var result = new byte[2048];
    var count = await client.ReceiveAsync(result);
    Debug.Log(Encoding.UTF8.GetString(result, 0, count));
}
catch (SocketException ex) when (ex.ErrorCode == 10061)
{
    Debug.LogError("Máy chủ chưa sẵn sàng");
}

Xử Lý Gói Tin

Vấn đề dính gói (packet sticking) và phân mảnh (fragmentation) được giải quyết bằng cơ chế đệm vòng lặp:

private byte[] _buffer = new byte[8192];
private int _bufferPosition;

private void ProcessIncomingData(byte[] data, int length)
{
    Array.Copy(data, 0, _buffer, _bufferPosition, length);
    _bufferPosition += length;

    int index = 0;
    while (_bufferPosition - index >= 4)
    {
        int packetLength = BitConverter.ToInt32(_buffer, index);
        index += 4;

        if (_bufferPosition - index >= packetLength)
        {
            byte[] packet = new byte[packetLength];
            Array.Copy(_buffer, index, packet, 0, packetLength);
            HandlePacket(packet);
            index += packetLength;
        }
        else
        {
            // Giữ lại dữ liệu chưa xử lý
            Array.Copy(_buffer, index - 4, _buffer, 0, _bufferPosition - index + 4);
            _bufferPosition = _bufferPosition - index + 4;
            break;
        }
    }
}

Giao Thức UDP Cơ Bản

Giao tiếp không kết nối với độ trễ thấp:

// Máy chủ
var udpServer = new Socket(AddressFamily.InterNetwork, 
                         SocketType.Dgram, 
                         ProtocolType.Udp);
udpServer.Bind(new IPEndPoint(IPAddress.Any, 9999));

var remoteEp = new IPEndPoint(IPAddress.Any, 0);
var receivedData = udpServer.ReceiveFrom(new byte[512], ref remoteEp);
Debug.Log($"Nhận từ {remoteEp}: {Encoding.UTF8.GetString(receivedData)}");

// Máy khách
var udpClient = new Socket(AddressFamily.InterNetwork, 
                         SocketType.Dgram, 
                         ProtocolType.Udp);
udpClient.SendTo(Encoding.UTF8.GetBytes("Ping"), 
                new IPEndPoint(IPAddress.Parse("10.1.5.20"), 9999));

Yêu Cầu HTTP Với UnityWebRequest

Tải tài nguyên từ máy chủ:

IEnumerator DownloadResource()
{
    using var request = UnityWebRequest.Get("https://example.com/data.json");
    yield return request.SendWebRequest();

    if (request.result == UnityWebRequest.Result.Success)
    {
        var jsonData = request.downloadHandler.text;
        // Xử lý dữ liệu JSON
    }
}

Sử Dụng Protobuf

Mô hình dữ liệu hiệu quả cho truyền tải mạng:

// File player.proto
syntax = "proto3";
package GameData;

message PlayerState {
  int32 id = 1;
  float x = 2;
  float y = 3;
  repeated string items = 4;
}

Sau khi biên dịch, sử dụng trong C#:

var state = new PlayerState { id = 101, x = 5.2f, y = 3.7f };
byte[] data = state.ToByteArray();

// Gửi qua mạng
socket.Send(data);

// Nhận và giải mã
var receivedState = PlayerState.Parser.ParseFrom(receivedBytes);

Thẻ: unity-networking sockets tcp-udp protobuf message-framing

Đăng vào ngày 22 tháng 6 lúc 23:00