Lập trình Socket phi kết nối (UDP)

0
3890

Ở các bài viết trước, chúng ta đã làm quen với lập trình Socket đơn giản với các hướng kết nối. Nhưng lập trình Socket phi kết nối khác hay có một số ưu điểm và nhược điểm so với hướng kết nối như thế nào chúng ta sẽ làm rõ ở một số bài viết về lập trình phi kết nối UDP. 

Các Socket phi kết nối cho phép gởi các thông điệp mà không cần phải thiết lập kết nối trước. Một phương thức  đọc sẽ  đọc toàn bộ thông  điệp  được gởi bởi một phương thức gởi, điều này làm tránh được các rắc rối, phức tạp với biên dữ liệu. Thật không may mắn là giao thức phi kết nối UDP không đảm bảo dữ liệu được truyền tới đích. Nhiều yếu tố như mạng bận, mạng bị đứt nữa chừng có thể ngăn cản các gói tin được truyền tới đích.

Nếu một thiết bị chờ dữ liệu từ một thiết bị ở xa, nó phải được gán một địa chỉ và port cục bộ, dùng hàm Bind() để gán. Một khi đã thực hiện xong, thiết bị có thể dùng Socket để gởi dữ liệu ra ngoài hay nhận dữ liệu từ Socket.

Bởi vì thiết bị Client không tạo ra kết nối đến một địa chỉ Server cụ thể do đó phương thức Connect() không cần dùng trong chương trình UDP Client. Mô hình bên dưới mô tả các bước lập trình Socket phi kết nối:

13933041_932140090241862_241170357_n

Khi kết nối không được thành lập thì phương thức Send() và Receive() không được dùng bởi vì trong hai phương thức trên đều không chỉ ra địa chỉ đích của dữ liệu.

Thay vào đó, Socket phi kết nối cung cấp hai phương thức để thực hiện việc này là SendTo() và ReceiveFrom().

Lập trình phía server 

UDP là một giao thức phi kết nối do đó các lập trình viên chỉ phải làm hai việc để tạo ra một ứng dụng Server gởi và nhận dữ liệu:

Tạo ra Socket

Kết nối Socket đến một IPEndPoint cục bộ

        IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
        Socket newsock = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);
        newsock.Bind(ipep);

Để thực hiện truyền thông phi kết nối, chúng ta phải chỉ ra SocketType là Dgram và ProtocolType là Udp.

Sau khi thực hiện xong hai bước trên, Socket có thể được dùng hoặc để chấp nhận các gói tin UDP đến trên IPEndPoint hoặc gởi các gói tin udp đến các thiết bị nhận khác trên mạng.

Phương thức SendTo() dùng để gởi dữ liệu, phương thức này chỉ ra dữ liệu để gởi và IPEndPoint của thiết bị nhận. Có nhiều quá tải hàm của phương thức SendTo() có thể được dùng tùy vào yêu cầu cụ thể.

Phương thức trên gởi một mảng dữ liệu  đến một EndPoint  được chỉ ra bởi Remote. Một quá tải hàm khác phức tạp hơn của phương thức SendTo().

Phương thức ReceiveFrom() có dùng định dạng với phương thức SendTo(), chỉ có  một   điểm khác  biệt  sau   ở  cách  EndPoint   được  khai  báo.

Cũng như thông thường, tham số thứ nhất là một mảng byte được định nghĩa để nhận dữ liệu, tham số thứ hai ra phải truyền tham chiếu của đối tượng EndPoint. Tham chiếu  này tham  chiếu  đến  vị  trí  bộ  nhớ  nơi  biến  được  lưu  trữ.  Phương  thức ReceiveFrom() sẽ đặt thông tin EndPoint từ thiết bị  ở xa vào vùng bộ nhớ của  đối tượng EndPoint tham chiếu đến. Bằng việc sử dụng đối số thứ hai là tham chiếu ta sẽ lấy được địa chỉ IP và port của máy ở xa.

Chương trình UDP Server đơn gin

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpSrvr
{
    public static void Main()
    {
        int recv;
        byte[] data = new byte[1024];
        IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 5000);
        Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        newsock.Bind(ipep);
        Console.WriteLine("Dang cho Client ket noi den...");
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
        EndPoint Remote = (EndPoint)(sender);
        recv = newsock.ReceiveFrom(data, ref Remote);
        Console.WriteLine("Thong diep duoc nhan tu {0}:", Remote.ToString());
        Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
        string welcome = "Hello Client";
        data = Encoding.ASCII.GetBytes(welcome);
        newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
        while (true)
        {
            data = new byte[1024];
            recv = newsock.ReceiveFrom(data, ref Remote);
            Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
            newsock.SendTo(data, recv, SocketFlags.None, Remote);
        }
    }
}

Để chương trình UDP chấp nhận các thông điệp UDP đến, nó phải được gắn với một port trên hệ thống. Việc này được thực hiện bằng cách tạo ra một đối tượng IPEndPoint sử dụng một địa chỉ IP cục bộ thích hợp, trong trường hợp này ta chỉ ra IPAddresss.Any để có thể dùng bất kỳ địa chỉ IP nào trên máy cục bộ để lắng nghe.

Sau khi gắn Socket vào một IPEndPoint, Server sẽ chờ Client kết nối đến, khi Client kết nối  đến, Client sẽ gởi thông  điệp  đến Server. Server sau khi nhận  được thông điệp từ Client nó sẽ gởi câu chào ngược lại cho Client:

        recv = newsock.ReceiveFrom(data, ref Remote);
        Console.WriteLine("Thong diep duoc nhan tu {0}:", Remote.ToString());
        Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
        string welcome = "Hello Client";

Khi gởi câu chào cho Client xong, Server sẽ bắt đầu nhận và gởi thông điệp.

Lập trình phía Client 

Bởi vì Client không cần chờ trên một port UDP định sắn nên nó cũng chẳng cần dùng phương thức Bind(), thay vì vậy nó sẽ lấy một port ngẫu nhien trên hệ thống khi dữ liệu được gởi và nó giữa port này để nhận dữ liệu trả về. Chương trình UDP Client cũng tương tự chương trình UDP Server:

Chương trình UDP Client đơn gin

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SimpleUdpClient
{
    public static void Main()
    {
        byte[] data = new byte[1024];
        string input, stringData;
        IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5000);
        Socket server = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);
        string welcome = "Hello server";
        data = Encoding.ASCII.GetBytes(welcome);
        server.SendTo(data, data.Length, SocketFlags.None, ipep);
        IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
        EndPoint Remote = (EndPoint)sender;
        data = new byte[1024];
        int recv = server.ReceiveFrom(data, ref Remote);
        Console.WriteLine("Thong diep duoc nhan tu {0}:", Remote.ToString());
        Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
        while (true)
        {
            input = Console.ReadLine();
            if (input == "exit")
                break;
            server.SendTo(Encoding.ASCII.GetBytes(input), Remote);
            data = new byte[1024];
            recv = server.ReceiveFrom(data, ref Remote);
            stringData = Encoding.ASCII.GetString(data, 0, recv); Console.WriteLine(stringData);
        }
        Console.WriteLine("Dang dong client");
        server.Close();
    }
}

 Chương trình Client gởi thông điệp đến Server và chờ câu chào trả về từ Server. Bởi vì Client không cần chấp nhận các thông điệp UDP trên một port định trước nên Client không dùng phương thức Bind(). Nó sẽ nhận các thông điệp UDP trên cùng port mà nó đã gởi.

Chương trình SimpleUdpClient đọc dữ liệu nhập vào từ bàn phím rồi gởi đến và chờ dữ liệu từ Server gởi trả về. Khi Server gởi trả dữ liệu về, Client sẽ lấy thông điệp đó ra và hiển thị lên màn hình. Nếu người dùng nhận vào “exit” thì vòng lặp sẽ thoát và kết nối bị đóng lại.

Không giống như chương trình TCP Server, chương trình UDP Server sẽ không biết khi nào Client ngắt kết nối do đó khi Client ngắt kết nối thì nó phải gởi thông điệp ngắt kết nối cho Server biết.

Trên đây là bài viết đơn giản đầu tiên về lập trình socket phi kết nối. Tuy nhiên còn khá nhiều vấn đề cần phải xử lí và một số lỗi trong quá trình truyền dữ liệu, chúng ta sẽ giải quyết các vấn đề đó trong các bài viết sau. Cảm ơn các bạn đã theo dõi, chúc các bạn thành công!

This site uses Akismet to reduce spam. Learn how your comment data is processed.