Pages

25 thg 2, 2010

Sử dụng TCP một cách bất đồng bộ

Bạn cần ghi dữ liệu ra network stream từng khối một, mà không phải block phần mã lệnh còn lại. Kỹ thuật này có thể được sử dụng nếu bạn muốn “stream” một file lớn trên mạng. Khi đó, chúng ta sẽ tạo một lớp riêng để xử lý kỹ thuật streaming bất đồng bộ...

Bạn cần ghi dữ liệu ra network stream từng khối một, mà không phải block phần mã lệnh còn lại. Kỹ thuật này có thể được sử dụng nếu bạn muốn “stream” một file lớn trên mạng. Khi đó, chúng ta sẽ tạo một lớp riêng để xử lý kỹ thuật streaming bất đồng bộ. Bạn có thể bắt đầu “stream” một khối dữ liệu bằng phương thức NetworkStream.BeginWrite và cung cấp một phương thức callback. Khi callback được kích hoạt thì gửi khối kế tiếp.



Lớp NetworkStream hỗ trợ việc sử dụng bất đồng bộ thông qua phương thức BeginReadBeginWrite. Sử dụng các phương thức này, bạn có thể gửi hay nhận một khối dữ liệu trên một trong các tiểu trình do thread pool của bộ thực thi .NET cung cấp, mà không block mã lệnh của bạn. Mục này trình bày kỹ thuật ghi bất đồng bộ.



Khi gửi dữ liệu một cách bất đồng bộ, bạn phải gửi dữ liệu nhị phân thô (một mảng byte). Và bạn cần chọn kích thước mỗi lần gửi hay nhận. Ví dụ dưới đây tạo server sao cho mỗi lớp ClientHandler gửi một lượng lớn dữ liệu được đọc từ một file. Dữ liệu này được gửi một cách bất đồng bộ, nghĩa là ClientHandler có thể tiếp tục thực hiện các tác vụ khác (trong ví dụ này, nó chỉ việc lấy các thông điệp được gửi từ client).


Một thuận lợi của cách tiếp cận này là toàn bộ nội dung của file chẳng bao giờ nằm trong bộ nhớ một lượt. Thay vào đó, nó được thu lấy ngay trước khi một khối mới được gửi. Một thuận lợi khác nữa là server có thể hủy bỏ thao tác vào bất cứ lúc nào. Ví dụ, nếu client chỉ đọc đến khối dữ liệu thứ ba thì ngắt kết nối, server sẽ thiết lập một biến thành viên luận lý có tên là fileStop để báo cho callback không gửi dữ liệu nữa.


Dưới đây là lớp ClientHandler đã được sửa đổi (lớp TcpServerTest không cần thay đổi gì):


  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.IO;
  5. using SharedComponent;
  6. public class ClientHandler {
  7. private TcpClient client;
  8. private string ID;
  9. // Kích thước một khối dữ liệu (2 KB).
  10. private int bufferSize = 2048;
  11. // Bộ đệm dùng để chứa dữ liệu.
  12. private byte[] buffer;
  13. // Dùng để đọc dữ liệu từ một file.
  14. private FileStream fileStream;
  15. // Dùng để giao tiếp với client.
  16. private NetworkStream networkStream;
  17. // Dấu hiệu ngừng gửi dữ liệu.
  18. private bool fileStop = false;
  19. public ClientHandler(TcpClient client, string ID) {
  20. this.buffer = new byte[bufferSize];
  21. this.client = client;
  22. this.ID = ID;
  23. }
  24. public void Start() {
  25. // Thu lấy network stream.
  26. networkStream = client.GetStream();
  27. // Tạo các đối tượng dùng để gửi và nhận text.
  28. BinaryWriter w = new BinaryWriter(networkStream);
  29. BinaryReader r = new BinaryReader(networkStream);
  30. if (r.ReadString() == ClientMessages.RequestConnect) {
  31. w.Write(ServerMessages.AcknowledgeOK);
  32. Console.WriteLine(ID + ": Connection completed.");
  33. string message = "";
  34. while (message != ClientMessages.Disconnect) {
  35. message = r.ReadString();
  36. if (message == ClientMessages.RequestData) {
  37. // Tên file có thể do client cung cấp, nhưng
  38. // trong ví dụ này, file thử nghiệm là mã cứng.
  39. fileStream =new FileStream("test.bin", FileMode.Open);
  40. // Gửi kích thước file.
  41. w.Write(fileStream.Length.ToString());
  42. // Khởi chạy thao tác bất đồng bộ.
  43. StreamData(null);
  44. }
  45. }
  46. fileStop = true;
  47. Console.WriteLine(ID + ": Disconnect request received.");
  48. } else {
  49. Console.WriteLine(ID + ": Could not complete connection.");
  50. }
  51. // Đóng kết nối.
  52. client.Close();
  53. Console.WriteLine(ID + ": Client connection closed.");
  54. Console.ReadLine();
  55. }
  56. private void StreamData(IAsyncResult asyncResult) {
  57. // Hủy bỏ nếu client ngừng kết nối.
  58. if (fileStop == true) {
  59. fileStop = false;
  60. return;
  61. }
  62. if (asyncResult != null) {
  63. // Một khối đã được ghi một cách bất đồng bộ.
  64. networkStream.EndWrite(asyncResult);
  65. }
  66. // Lấy khối kế tiếp từ file.
  67. int bytesRead = fileStream.Read(buffer, 0, buffer.Length);
  68. // Nếu không đọc được byte nào, stream đã đến cuối file.
  69. if (bytesRead > 0) {
  70. Console.WriteLine("Streaming new block.");
  71. // Ghi khối kế tiếp ra network stream.
  72. networkStream.BeginWrite(buffer, 0, buffer.Length,new AsyncCallback(StreamData), null);
  73. } else {
  74. // Kết thúc thao tác.
  75. Console.WriteLine("File streaming complete.");
  76. fileStream.Close();
  77. }
  78. }
  79. }

Bạn có thể sử dụng một mẫu tương tự để đọc dữ liệu một cách bất đồng bộ phía client.


[HaiPhong-Aptech st]

0 nhận xét:

Đăng nhận xét

Powered By Blogger