- Những lớp tiện ích trong Networking
- Lớp Uri và UriBuilder
- Lớp IPAddress
- Lớp IPHostEntry
- Lớp Dns
- Sử dụng giao thức TCP
- Tạo HTTP Client với TCP
- Lớp TcpListener
- Lớp TcpClient
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
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
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) { Console.WriteLine("ValidateServerCertificate"); if (sslPolicyErrors == SslPolicyErrors.None) return true; Console.WriteLine("Certificate error: {0}", sslPolicyErrors); // Do not allow this client to communicate with unauthenticated servers. return false; } // Kết nối đến server Tpc bằng TcpClient, đọc nội dung trả về 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}:{uri.Port}"); await client.ConnectAsync(ipaddrress.MapToIPv4(), uri.Port); Console.WriteLine("Connected"); Console.WriteLine(); Stream stream; if (uri.Scheme == "https") { // SslStream stream = new SslStream(client.GetStream(),false, new RemoteCertificateValidationCallback (ValidateServerCertificate), null); (stream as SslStream).AuthenticateAsClient(uri.Host); } else { // NetworkStream stream = client.GetStream(); } Console.WriteLine($"Get Stream OK: {stream.GetType().Name}"); // Xem: /psr-7-chuan-giao-dien-thong-diep-http.html#HTTPRequest StringBuilder header = new StringBuilder(); header.Append($"GET {uri.PathAndQuery} HTTP/1.1\r\n"); // header.Append($"GET {uri.PathAndQuery} HTTP/2\r\n"); header.Append($"Host: {uri.Host}\r\n"); header.Append($"\r\n"); Console.WriteLine("Request:"); Console.WriteLine(header); byte[] bsend = Encoding.UTF8.GetBytes(header.ToString()); await stream.WriteAsync(bsend, 0, bsend.Length); await stream.FlushAsync(); Console.WriteLine("Send Message OK"); var ms = new MemoryStream(); byte [] buffer = new byte[255]; int bytes = -1; do { bytes = await stream.ReadAsync(buffer, 0, buffer.Length); // Lưu dữ liệu tải về vào ms ms.Write(buffer, 0, bytes); Array.Clear(buffer, 0, buffer.Length); } while (bytes != 0); Console.WriteLine($"Read OK"); 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://google.com.vn"; await ReadHtmlAsync(url); } } }
Tham khảo mã nguồn TcpClient (git) hoặc tải về ex036
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 StreamWriter
và
StreamReader
. 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(); } } }
Tham khảo mã nguồn TcpListener (git) hoặc tải về ex037
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
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); } } }
Tham khảo mã nguồn TcpClient (git) hoặc tải về ex038