Lớp tiện ích trong lập trình Networking

Lớp Uri và UriBuilder

Lớp Uri để biểu diễn một Uri (nó là chuỗi ký tự xác định, nhận dạng tài nguyên), tài nguyên nhận dạng cho đối tượng trên mạng Uri đó là Url.

Cấu trúc Uri

http://site.yourdomain.com/path/to/page/?a=1&b=price#section

Từ một chuỗi URL ví dụ: https://xuanthulab.net/testpate.html#testfragment?read=1, có thể khởi tạo Uri và đọc được các thông tin về Uri

public static void ShowUriInfo(string url) {
    Uri uri = new Uri(url);
    Console.WriteLine(url);
    Console.WriteLine($"Scheme   : {uri.Scheme}");
    Console.WriteLine($"Host     : {uri.Host}");
    Console.WriteLine($"Port     : {uri.Port}");
    Console.WriteLine($"Fragment : {uri.Fragment}");
    Console.WriteLine($"Query    : {uri.Query}");
    Console.WriteLine($"Path     : {uri.LocalPath}");
    foreach (var seg in uri.Segments)
        Console.WriteLine($"           {seg}");
    /*
    https://xuanthulab.net/abc/testpate.html?read=1#testfragment
    Scheme   : https
    Host     : xuanthulab.net
    Port     : 443
    Fragment : #testfragment
    Query    : ?read=1
    Path     : /abc/testpate.html
            /
            abc/
            testpate.html
    */
}

Ngược lại, bạn có thể từ các thành phần của Uri như Path, Host, Port ... dùng lớp UriBuilder để có được Uri

public static void BuildUriExample() {
    UriBuilder uriBuilder = new UriBuilder();
    uriBuilder.Host = "xuanthulab.net";
    uriBuilder.Port = 80;
    uriBuilder.Path = "path/to/site";
    uriBuilder.Query = "lession=1";
    uriBuilder.Fragment = "xyz";
    Uri uri = uriBuilder.Uri;
    Console.WriteLine(uri);
    // http://xuanthulab.net/path/to/site?lession=1#xyz
}
     

Lớp IPAddress

Lớp IPAddress biểu diễn một địa chỉ IP. Thực chất địa chỉ IP là một mảng byte, có thể lấy mảng byte này bằng GetAddressBytes, tuy nhiên có thể chuyển thành biểu diễn mảng byte đó thành chuỗi các số thập phân, phân cách nhau bởi ký tự . bằng phương thức ToString().

Bạn có thể khởi tạo đối tượng IPAdress bằng cách cung cấp mảng byte biểu diễn IP, hoặc phân tích từ một string biểu diễn IP bằng IPAddress.TryParse

IPAddress ipaddress;
if (IPAddress.TryParse(ips, out ipaddress)) {

}

Một số phương thức, thuộc tính

Thành viên Diễn tả
IPAddress.Broadcast Địa chỉ broadcast của mạng, đây là IP đặc biệt của mạng, gửi gói tin tới IP này nghĩa là gửi tới tất cả các máy trong mạng
IPAddress.Loopback Địa chỉ Loopback, không đia qua thiết bị mạng, biểu diễn hostname tên localhost, trỏ đến chính máy host
MapToIPv4() Convert thành IP4
MapToIPv6() Convert thành IP6
public static void IPAddressExample(string ips) {
    IPAddress ipaddress;
    if (IPAddress.TryParse(ips, out ipaddress)) {
        Console.WriteLine($"Broadcast     {IPAddress.Broadcast}");
        Console.WriteLine($"Loopback      {IPAddress.Loopback}");
        Console.WriteLine($"AddressFamily {ipaddress.AddressFamily}");
        Console.WriteLine($"IP4           {ipaddress.MapToIPv4().ToString()}");
        Console.WriteLine($"IP6           {ipaddress.MapToIPv6().ToString()}");
        /*
            Broadcast     255.255.255.255
            Loopback      127.0.0.1
            AddressFamily InterNetwork
            IP4           192.168.0.66
            IP6           ::ffff:192.168.0.66
        */
    }
}

Lớp IPHostEntry

Lớp IPHostEntry biểu diễn địa chỉ mạng host, nó gắn với một DSN.

IPHostEntry hostInfo = Dns.GetHostEntry("google.com.vn");
Console.WriteLine(hostInfo.HostName);
foreach (var ip in hostInfo.AddressList)
{
   Console.WriteLine(ip);
}
/*
    google.com.vn
    216.58.221.227
    2404:6800:4005:800::2003
*/

Lớp Dns

Lớp để tương tác với máy chủ DNS để phân giải địa chỉ IP. Xem ví dụ trên.

Giao thức TCP

Giao thức TCP (Transmission Control Protocol), đầu tiên client phải mở một kết nối đến server rồi mới có thể gửi các lệnh, thông điệp. Giao thức HTTP mà ta sử dụng với HttpListener, HttpClient chính là dựa trên TCP mặc dù những lớp này đã ẩn đi (tự động) thực hiện các thao tác mở kết nối cho chúng ta.

Các lớp TCP đưa ra những phương thức đơn giản giúp gửi nhận dữ liệu giữa các máy, nó sử dụng thông tin IP và cổng để kết nối với nhau, ví dụ HTTP sử dụng cổng 80, SMTP dùng cổng 25 ... đó là những cổng dành cho những dịch vụ chính thống do tổ chức IANA quy định (xem www.iana.org), nếu muốn chạy dịch vụ của riêng bạn hãy chọn cổng lớn hơn 1024.

Lớp TcpClient cho phép tạo kết nối TCP. Lớp TcpListener cho phép lắng nghe các yêu cầu TCP gửi đến.

Tạo HTTP Client với TCP

Để truy cập đến máy chủ HTTP (trang web ...) đã thực hiện khá dễ dàng với HttpClient, tuy nhiên nếu muốn dùng trực tiếp giao thức TCP với lớp TcpClient bạn cần một chút hiểu chi tiết về TCP.

Ví dụ sau, sẽ dử dụng lớp TcpClient tạo kết nối tới server, gửi thông điệp HTTP, nhận thông điệp HTTP

Source code
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Text;
using System.Net.Sockets;
using System.Net.Http;
using System.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace TCP
{

    class Program
    {
        // Phương thức này gọi bởi RemoteCertificateValidationDelegate trong quá trình xác thức SSL
        // chỉ dùng khi kết nối HTTPS
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
           if (sslPolicyErrors == SslPolicyErrors.None)  return true;
            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }


        public static  async Task ReadHtmlAsync(string url) {

            using (var client = new TcpClient())
            {
                Console.WriteLine($"Start get {url}");
                Uri uri = new Uri(url);
                var hostAdress = await Dns.GetHostAddressesAsync(uri.Host);
                IPAddress ipaddrress = hostAdress[0];
                Console.WriteLine($"Host: {uri.Host}, IP: {ipaddrress}");

                await client.ConnectAsync(ipaddrress.MapToIPv4(), uri.Port); //kết nối đến HOST
                Console.WriteLine("Connected");
                Console.WriteLine();


                Stream stream; // stream để đọc - gửi HTTP Message
                if (uri.Scheme == "https")
                {
                    // SslStream
                    stream = new SslStream(client.GetStream(),false,
                                           new RemoteCertificateValidationCallback (ValidateServerCertificate),
                                           null);
                   (stream as SslStream).AuthenticateAsClient(uri.Host); // Xác thực SSL
                }
                else {
                    // NetworkStream
                    stream = client.GetStream();
                }

                // Tạo HTTP Request Message (text) và gửi lên server
                StringBuilder header = new StringBuilder();
                header.Append($"GET {uri.PathAndQuery} HTTP/1.1\r\n");
                header.Append($"Host: {uri.Host}\r\n");
                header.Append($"\r\n");
                byte[]  bsend  = Encoding.UTF8.GetBytes(header.ToString());
                await stream.WriteAsync(bsend, 0, bsend.Length);
                await stream.FlushAsync();

                Console.WriteLine("Request:");
                Console.WriteLine(header);

                var ms = new MemoryStream();        // Bộ nhớ lưu dữ liệu tải về
                byte [] buffer = new byte[2048];
                int bytes = -1;

                // Đọc dữ liệu tải về (HTTP Response Message) lưu vào ms
                do
                {
                    bytes = await stream.ReadAsync(buffer, 0, buffer.Length);
                    ms.Write(buffer, 0, bytes);
                    Array.Clear(buffer, 0, buffer.Length);

                } while (bytes > 0);

                ms.Seek(0, SeekOrigin.Begin);
                var reader = new StreamReader(ms);
                string html = reader.ReadToEnd();
                Console.WriteLine("Response:");
                Console.WriteLine(html);

            }
        }
        static async Task  Main(string[] args)
        {
            string url = "https://xuanthulab.net/robots.txt";
            await ReadHtmlAsync(url);
        }

    }
}

Lớp TcpListener - dịch vụ TPC ở server

Lớp TcpListener cho phép tạo dịch vụ lắng nghe yêu cầu TCP gửi đến, lắng nghe trên một cổng nào đó. Ví dụ sau tạo dịch vụ, lắng nghe trên cổng 1950, mặc dù có thể gửi nhận từng byte dữ liệu giữa server và client, tuy nhiên để đơn giản hóa sẽ / đọc ghi từng dòng chuỗi dữ liệu với StreamWriterStreamReader. Chương trình này chạy phía server để client kết nối, nó khá giống chương trình chat đơn giản

using System;
using System.Net;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.IO;

namespace TCP
{
    class Program
    {
        public class TpcServerAsyncv {
            readonly int PortNumber;
            public TpcServerAsyncv(int portNumber) => PortNumber = portNumber;

            // Lắng nghe
            public async Task StartLinster()
            {
                try
                {
                    var listener = new TcpListener(IPAddress.Any, PortNumber);
                    Console.WriteLine($"Listener lắng nghe ở cổng {PortNumber}");
                    listener.Start();

                    while (true)
                    {
                        Console.WriteLine("Chờ client kết nối ...");
                        // Một client kết nối đến
                        TcpClient client = await listener.AcceptTcpClientAsync();

                        // Xử lý quá trình giao tiếp giữa Client và Server
                        Task t = RunClientRequestAsync(client);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception of type {ex.GetType().Name}, Message: {ex.Message}");
                }
            }
            private Task RunClientRequestAsync(TcpClient client)
            {
                 // Ham delegate để task thi hành
                 Action action = async () => {
                    try
                    {
                        using (client)
                        {
                            Console.WriteLine("client kết nối");
                            using (NetworkStream stream = client.GetStream())        // tạo các stream
                            using (StreamWriter  writer = new StreamWriter(stream))  // stream để gửi dữ liệu cho client
                            using (StreamReader  reader = new StreamReader(stream))  // stream để đọc dữ liệu từ client
                            {
                                writer.AutoFlush = true;
                                bool exit = false;
                                while (!exit) {
                                    string data = await reader.ReadLineAsync();
                                    switch (data.ToLower())
                                    {
                                        case "time":
                                            await writer.WriteLineAsync(DateTime.Now.ToLongTimeString());
                                        break;
                                        case "exit": // thoát lặp - ngắt kết nối nếu client gửi dòng exit
                                            exit = true;
                                            await writer.WriteLineAsync("exit");
                                        break;
                                        default:
                                            await writer.WriteLineAsync("Không thấy lệnh");
                                        break;
                                    }
                                }

                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Lỗi {ex.GetType().Name}, Message: {ex.Message}");
                    }
                    Console.WriteLine("Client ngắt kế nối");
                };

                Task task = new Task(action); // tạo task
                task.Start();                 // chạy  task trên thread
                return task;
            }
        }
        static async Task  Main(string[] args)
        {
            await (new TpcServerAsyncv(1950)).StartLinster();
        }

    }
}

Kết nối đến TCP server với TcpClient

Để kết nối và giao tiếp với TCP Server mà ta đang chạy ở ví dụ trên bằng TcpListener, có thể dùng lớp TcpClient kết nối, nó cũng đọc ghi dữ liệu với StreamWriter, StreamReader

Source code
using System;
using System.Net;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.IO;

namespace TCP
{
    class Program
    {
        public static  async Task StartConnectAsync(IPAddress iPAddress, int Port) {

            try {
                using (var client = new TcpClient())
                {

                    await client.ConnectAsync(iPAddress, Port);
                    Console.WriteLine("Đã kết nối");

                    using (NetworkStream stream = client.GetStream())
                    using (StreamWriter writer = new StreamWriter(stream))
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        writer.AutoFlush = true;
                        bool quite = false;
                        while (!quite) {
                            Console.Write("Nhập nội dung (time, exit):");
                            string mgs = Console.ReadLine();
                            if (mgs == "exit")
                                quite = true;

                            await writer.WriteLineAsync(mgs);
                            string mgs_receive = await reader.ReadLineAsync();
                            Console.WriteLine(mgs_receive);
                        }

                    }
                }
            } catch (Exception ex)
            {
                Console.WriteLine($"Lỗi {ex.GetType().Name}, Message: {ex.Message}");
            }
        }
        static async Task  Main(string[] args)
        {
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            int       port = 1950;

            await StartConnectAsync(ip, port);
        }

    }
}
Đăng ký theo dõi ủng hộ kênh