Kết nối client và server trong thư viện ezyfox-server-flutter-client (Connect client and server in ezyfox-server-flutter-client library)

12 tháng 09, 2024 - 376 lượt xem

Giới thiệu

Mô hình Client - Server là một kiến trúc phổ biến trong ngành công nghệ thông tin, mô hình này được sử dụng rộng rãi trong việc xây dựng các ứng dụng và hệ thống mạng. Mô hình này bao gồm hai thành phần chính là Client (khách hàng) và Server (máy chủ). Server chính là nơi giúp lưu trữ tài nguyên cũng như cài đặt các chương trình dịch vụ theo đúng như yêu cầu của client. Ngược lại, Client bao gồm máy tính cũng như các loại thiết bị điện tử nói chung sẽ tiến hành gửi yêu cầu đến server.

moHinhClientServer
moHinhClientServer

Nguyên tắc hoạt động của Client - Server

Client

Client chính là khách hàng sử dụng dịch vụ. Nó có thể là một tổ chức hay cá nhân cụ thể nào đó. Và khi khái niệm này được sử dụng trong lĩnh vực kỹ thuật số thì cũng mang ý nghĩa tương tự như vậy. Trong Client Server thì Client chính là một máy tính (Host). Chúng có khả năng nhận thông tin từ nhà cung cấp và sử dụng dịch vụ cụ thể (Server).

Server

Server là từ dùng để nói về một máy chủ hoặc một phương tiện được sử dụng để phục vụ các dịch vụ nào đó. Khi khái niệm này được sử dụng trong lĩnh vực công nghệ thì Server là một máy tính từ xa. Chúng có chức năng là cung cấp các thông tin (dữ liệu) cho một dịch vụ cụ thể nào đó hoặc quyền truy cập đối với dịch vụ. Trong mô hình Client - Server, Server ở đây là máy chủmáy chủ phần mềm.Với máy chủ phần mềm (Software Sever): server ở đây là phần mềm được cài đặt trên một máy chủ vật lý hoặc một máy ảo (virtual machine), có nhiệm vụ xử lý các yêu cầu của client. Ví dụ: một server phần mềm phổ biến là Apache (cho dịch vụ web), hoặc Nginx. Phần mềm này có thể chạy trên một máy chủ vật lý hoặc trên máy ảo được triển khai trên hạ tầng đám mây.

Tuy nhiên ngoài máy chủ là phần mềm ra còn rất nhiều các loại máy chủ khác: Máy chủ vật lý, máy chủ ảo (Virtual Server), Máy chủ trên đám mây (Cloud Server). Trong đó:
*Máy chủ vật lý: Máy chủ vật lý là phần cứng cung cấp tài nguyên để xử lý các yêu cầu từ client. Một server phần mềm có thể được cài đặt trên máy chủ vật lý này.
*Máy chủ ảo (Virtual Server): Đây là các máy chủ được ảo hóa, chạy trên máy chủ vật lý thông qua phần mềm ảo hóa (như VMware, Hyper-V). Nhiều máy chủ ảo có thể được triển khai trên cùng một máy chủ vật lý.
*Máy chủ trên đám mây (Cloud Server): Đây là các server được cung cấp bởi các nhà cung cấp dịch vụ đám mây (như AWS, Azure, Google Cloud). Thực tế, đây là các máy chủ ảo hóa nhưng được quản lý trên hạ tầng đám mây.
*Máy chủ phần mềm (Software Server): Là phần mềm xử lý và quản lý các yêu cầu từ client (ví dụ: Apache, Nginx, hoặc các dịch vụ web như REST API). Phần mềm này có thể chạy trên máy chủ vật lý, máy ảo hoặc trên đám mây.
server

Ưu nhược điểm của mô hình Client Server

Ưu điểm

  • Tập trung: Đối với mô hình Client - Server mọi thông tin cần thiết sẽ được tập trung và đặt tại 1 vị trí duy nhất. Nó được gọi là khả năng kiểm soát tập trung (Centralization).
  • Bảo mật: Tất cả các dữ liệu đều sẽ được bảo vệ một cách tối đa nhờ vào hệ thống kiến trúc tập trung của mạng. Thông qua đó, nó sẽ giúp người dùng kiểm soát truy cập để chỉ có những ai được cấp quyền truy cập thì mới được thực hiện các thao tác cần thiết.
  • Khả năng mở rộng: Chỉ cần người dùng cần sử dụng bất cứ lúc nào thì họ cũng có thể tăng được số lượng tài nguyên của mình. Ví dụ như số Client hoặc Server. Nhờ đó mà chúng ta có thể tăng kích thước của Server một cách dễ dàng mà không bị gián đoạn nhiều.
  • Khả năng truy cập: Tất cả mọi Client đều có khả năng đăng nhập được vào hệ thống mạng máy tính. Điều này sẽ giúp cho tất cả các nhân viên đều có thể truy cập thông tin của công ty một cách dễ dàng mà không cần phải dùng một terminal mode hoặc một bộ xử lý nào khác.

Nhược điểm

*Tắc nghẽn lưu lượng: Một trong những nhược điểm lớn nhất của mô hình Client - Server đó là tắc nghẽn lưu lượng. Vì trong trường hợp có nhiều Client tạo request lên Server thì sẽ khiến cho kết nối chậm, phản hồi lâu hơn. Trong trường hợp xấu nhất Server có thể bị quá tải và bị crash.
*Độ bền: Client Server là mạng tập trung chính vì thế, khi Server chính xảy ra sự cố hoặc bị nhiễu thì cũng đồng nghĩa với việc toàn bộ hệ thống mạng sẽ bị gián đoạn.
*Chi phí: Chi phí được sử dụng để thiết lập và bảo trì Server trong Client Server thường sẽ khá cao. Lý do là vì các hệ thống mạng có sức mạnh rất lớn cũng đồng nghĩa với việc giá để chi cho việc này là rất đắt.
*Bảo trì: Khi các Server thực hiện triển khai để làm việc thì nó cũng sẽ hoạt động một cách không ngừng nghỉ. Điều này đồng nghĩa với việc chúng ta cần phải quan tâm đến việc bảo trì hệ thống đúng mức. Khi xảy ra bất cứ vấn đề gì cũng cần phải giải quyết ngay lập tức.
*Tài nguyên: Một điều rất cần phải lưu ý đó chính là không phải tất cả tài nguyên hiện có trên Server đều sử dụng được.

Client-Server trong thư viện Ezyfox-server-flutter-client được kết nối với nhau như thế nào

Để hiểu rõ hơn về thư viện ezyfox-server-flutter-client thì bạn có thể tham khảo bài viết về thư viện ezyfox-server-flutter-client mà mình đã giới thiệu đến các bạn ở bài viết trước đó.

const ZONE_NAME = "freechat";
const APP_NAME = "freechat";

class SocketProxy {
  bool settedUp = false;
  late String username;
  late String password;
  late EzyClient _client;
  late Function(String)? _greetCallback;
  late Function(String)? _secureChatCallback;
  late Function? _disconnectedCallback;
  late Function? _connectionCallback;
  late Function? _connectionFailedCallback;
  late Function? _requestCallback;
  late Function? _loginErrorCallback;
  late Function(String)? _chatBotResponseCallback; // Callback cho ChatBot
  late Function(List<String>)?
      _userListCallback; //callback cho danh sách người dùng
  late Function(Map)?
      _connectUserCallback; // Callback cho phản hồi kết nối đến người dùng khác
  late Function(List<String>)?
      _suggestionsCallback; // Callback cho gợi ý người dùng
  late Function(String)?
      _addContactCallback; // Callback cho thêm người dùng vào danh sách liên hệ

  static final SocketProxy _INSTANCE = SocketProxy._();

  SocketProxy._();

  static SocketProxy getInstance() {
    return _INSTANCE;
  }

  void addContact(String username) {
    var app = EzyClients.getInstance()
        .getDefaultClient()
        .zone
        ?.appManager
        .getAppByName("freechat");
    if (app != null) {
      app.send("2", {"username": username}); // Gửi yêu cầu thêm người dùng
    }
  }

  void printAllUsers() {
    print("Danh sách tất cả người dùng:");
    for (var user in contacts) {
      print(user);
    }
  }

  void sendMessage(Map<String, dynamic> message) {
    // Ensure you have a connected client before sending a message
    final client = EzyClients.getInstance().getDefaultClient();
    if (client != null && client.zone != null) {
      var app = client.zone!.appManager.getAppByName("freechat");
      if (app != null) {
        app.send("6", message);
      }
    }
  }

  void _setup() {
    EzyConfig config = EzyConfig();
    config.clientName = ZONE_NAME;
    config.enableSSL =
        false; // SSL is not active by default using freechat server
    config.ping.maxLostPingCount = 3;
    config.ping.pingPeriod = 1000;
    config.reconnect.maxReconnectCount = 3;
    config.reconnect.reconnectPeriod = 1000;
    EzyClients clients = EzyClients.getInstance();
    _client = clients.newDefaultClient(config);
    _client.setup.addEventHandler(EzyEventType.DISCONNECTION,
        _DisconnectionHandler(_disconnectedCallback!));
    _client.setup.addEventHandler(EzyEventType.CONNECTION_SUCCESS,
        _ConnectionHandler(_connectionCallback!, _client));
    _client.setup.addEventHandler(EzyEventType.CONNECTION_FAILURE,
        _ConnectionFailureHandler(_connectionFailedCallback!, _client));
    _client.setup.addDataHandler(EzyCommand.HANDSHAKE, _HandshakeHandler());
    _client.setup.addDataHandler(EzyCommand.LOGIN, _LoginSuccessHandler());
    _client.setup
        .addDataHandler(EzyCommand.APP_ACCESS, _AppAccessHandler(_client));
    // _client.setup.addDataHandler(
    //     EzyCommand.APP_REQUEST, _RequestHandler(_requestCallback!, _client));
    _client.setup.addDataHandler(
        EzyCommand.LOGIN_ERROR, _LoginErrorHandler(_loginErrorCallback!));
    var appSetup = _client.setup.setupApp(APP_NAME);

    // Xử lý dữ liệu cho câu hỏi chatbot
    appSetup.addDataHandler("4", _ChatBotQuestionHandler((question) {
      print(' cau hoi: $question');
      _chatBotResponseCallback!(question);
    }));

    // Xử lý dữ liệu cho danh sách người dùng
    appSetup.addDataHandler("1", _UsersListHandler((users) {
      print('data from userlist ${users}');
      _userListCallback!.call(users);
    }));

    // Xử lý dữ liệu cho gợi ý người dùng
    appSetup.addDataHandler("10", _SuggestionsHandler((suggestions) {
      _suggestionsCallback!(suggestions);
    }));

    // Xử lý dữ liệu cho thêm người dùng vào danh sách liên hệ
    appSetup.addDataHandler("2", _AddContactHandler((message) {
      _addContactCallback!(message);
    }));
  }

  void fetchSuggestions() {
    var app = EzyClients.getInstance()
        .getDefaultClient()
        .zone
        ?.appManager
        .getAppByName("freechat");
    if (app != null) {
      app.send("10", {}); // Gửi yêu cầu lấy danh sách gợi ý
    }
  }

  void fetchUsersList() {
    var app = EzyClients.getInstance()
        .getDefaultClient()
        .zone
        ?.appManager
        .getAppByName("freechat");
    if (app != null) {
      app.send("1", {}); // Gửi yêu cầu lấy danh sách người dùng
    }
  }

  void connectToUser(String username) {
    var app = EzyClients.getInstance()
        .getDefaultClient()
        .zone
        ?.appManager
        .getAppByName("freechat");
    if (app != null) {
      app.send("5", {"username": username});
    }
  }

  void onUserList(Function(List<String>) callback) {
    if (callback == null) {
      print('callback is null');
    }
    print('onuserlist $callback');
    _userListCallback = callback;
  }

  void handleAddContact(Map data) {
    // Xử lý dữ liệu khi thêm liên hệ
    contacts = data[1][1] + contacts;
  }

  void onConnectUserResponse(Function(Map) callback) {
    _connectUserCallback = callback;
  }

  void connectToServer(String username, String password) {
    if (!settedUp) {
      settedUp = true;
      _setup();
    }
    this.username = username;
    this.password = password;
    _client.connect("10.0.2.2",
        3005); // Android emulator localhost-10.0.2.2 for ios it may be 127.0.0.1
    // _client.connect(
    //     "192.168.31.88", 3005); // computer is server and use your real phone
  }

  void disconnect() {
    _client.disconnect();
  }

  void onGreet(Function(String) callback) {
    _greetCallback = callback;
  }

  void onSecureChat(Function(String) callback) {
    _secureChatCallback = callback;
  }

  void onDisconnected(Function callback) {
    _disconnectedCallback = callback;
  }

  void onConnection(Function callback) {
    _connectionCallback = callback;
  }

  void onConnectionFailed(Function callback) {
    _connectionFailedCallback = callback;
  }

  void onContacts(Function callback) {
    _connectionFailedCallback = callback;
  }

  void onData(Function callback) {
    _requestCallback = callback;
  }

  void onLoginError(Function callback) {
    _loginErrorCallback = callback;
  }

  void sendMessageToChatBot(String message) {
    // Đảm bảo bạn có một ứng dụng và có thể gửi tin nhắn
    var app = EzyClients.getInstance()
        .getDefaultClient()
        .zone
        ?.appManager
        .getAppByName("freechat");
    if (app != null) {
      app.send("4", {"message": message});
    }
  }

  void onChatBotResponse(Function(String) callback) {
    _chatBotResponseCallback = callback;
  }
}

//chatbot messages

class _ChatBotQuestionHandler extends EzyAppDataHandler<Map> {
  late Function(String) _callback;

  _ChatBotQuestionHandler(Function(String) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    String question = data["message"] ?? "Khong co cau hoi nao tu may chu";
    _callback(question);
  }
}

class _HandshakeHandler extends EzyHandshakeHandler {
  @override
  List getLoginRequest() {
    var request = [];
    request.add(ZONE_NAME);
    request.add(SocketProxy.getInstance().username);
    request.add(SocketProxy.getInstance().password);
    request.add([]);
    return request;
  }
}

class _LoginSuccessHandler extends EzyLoginSuccessHandler {
  @override
  void handleLoginSuccess(responseData) {
    client.send(EzyCommand.APP_ACCESS, [APP_NAME]);
  }
}

class _AppAccessHandler extends EzyAppAccessHandler {
  late EzyClient _client;

  _AppAccessHandler(EzyClient client) {
    _client = client;
  }

  @override
  void postHandle(EzyApp app, List data) {
    var _data = {};
    _data["limit"] = 50;
    _data["skip"] = 0;
    app.send("5", _data);
  }
}

class _GreetResponseHandler extends EzyAppDataHandler<Map> {
  late Function(String) _callback;

  _GreetResponseHandler(Function(String) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    _callback(data["message"]);
    app.send("secureChat", {"who": "Young Monkey"}, true);
  }
}

class _SecureChatResponseHandler extends EzyAppDataHandler<Map> {
  late Function(String) _callback;

  _SecureChatResponseHandler(Function(String) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    _callback(data["secure-message"]);
  }
}

class _DisconnectionHandler extends EzyDisconnectionHandler {
  late Function _callback;

  _DisconnectionHandler(Function callback) {
    _callback = callback;
  }

  @override
  void postHandle(Map event) {
    _callback();
  }
}

class _ConnectionFailureHandler extends EzyConnectionFailureHandler {
  late Function _callback;
  late EzyClient _client;

  _ConnectionFailureHandler(Function callback, EzyClient client) {
    _callback = callback;
    _client = client;
  }

  @override
  void onConnectionFailed(Map event) {
    _callback();
  }
}

class _ConnectionHandler extends EzyConnectionSuccessHandler {
  late Function _callback;
  late EzyClient _client;

  _ConnectionHandler(Function callback, EzyClient client) {
    _callback = callback;
    _client = client;
  }

  @override
  void handle(Map event) {
    sendHandshakeRequest();
    postHandle();
    _callback();
  }
}

class _LoginErrorHandler extends EzyAbstractDataHandler {
  late Function _callback;

  _LoginErrorHandler(Function callback) {
    _callback = callback;
  }

  @override
  void handle(List data) {
    client.disconnect();
    _callback();
  }
}

class _UserListHandler extends EzyAppDataHandler<Map> {
  late Function(List<String>) _callback;

  _UserListHandler(Function(List<String>) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    List<String> users = [];
    for (var user in data["users"]) {
      users.add(user["username"].toString());
    }
    _callback(users);
  }
}

class _ConnectUserHandler extends EzyAppDataHandler<Map> {
  late Function(Map) _callback;

  _ConnectUserHandler(Function(Map) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    _callback(data);
  }
}

class _UsersListHandler extends EzyAppDataHandler<Map> {
  late Function(List<String>) _callback;

  _UsersListHandler(Function(List<String>) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    List<String> users = [];
    print('cac user tu sever: $users');
    for (var user in data["users"]) {
      print('cac username" $user');
      users.add(user["username"].toString());
    }

    _callback(users);
  }
}

class _SuggestionsHandler extends EzyAppDataHandler<Map> {
  late Function(List<String>) _callback;

  _SuggestionsHandler(Function(List<String>) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    List<String> suggestions = [];
    for (var element in data["users"]) {
      suggestions.add(element["username"].toString());
    }
    _callback(suggestions);
  }
}

class _AddContactHandler extends EzyAppDataHandler<Map> {
  late Function(String) _callback;

  _AddContactHandler(Function(String) callback) {
    _callback = callback;
  }

  @override
  void handle(EzyApp app, Map data) {
    String message = data["message"] ?? "Không thể thêm liên hệ.";
    _callback(message);
  }
}

class _RequestHandler extends EzyAbstractDataHandler {
  late Function _callback;
  late EzyClient _client;

  _RequestHandler(Function callback, EzyClient client) {
    _callback = callback;
    _client = client;
  }

  @override
  handle(List data) {
    print('data sucess `${data}`');
    // Handle requests
    if (data[1][0] == '4') {
      messages = messages +
          [
            {'from': data[1][1]['from'], 'message': data[1][1]['message']}
          ];
    } else if (data[1][0] == '5') {
      // Get contacts
      contacts = data[1][1] + contacts;
    } else if (data[1][0] == '2') {
      // Add contact
      contacts = data[1][1] + contacts;
    } else if (data[1][0] == '6') {
      // User message
      messages = messages +
          [
            {'from': data[1][1]['from'], 'message': data[1][1]['message']}
          ];
      EzyApp? app = _client.getApp(); // Sử dụng getApp để lấy ứng dụng
      app?.send("getChatBotQuestion", {"message": data[1][1]['message']});
    }
    // else if (data[1][0] == '1') {
    //   // Suggest Contacts
    //   suggestions = [];
    //   for (var element in data[1][1]['users']) {
    //     suggestions = suggestions + [element['username'].toString()];
    //   }
    // }
    else if (data[1][0] == '10') {
      // Suggest Contacts

      suggestions = [];
      for (var element in data[1][1]['users']) {
        suggestions = suggestions + [element['username'].toString()];
      }
    }
    _callback();
  }
}

Trong lớp SocketProxy, hàm _setup() có nhiệm vụ thiết lập kết nối giữa client (ứng dụng) và server bằng cách cấu hình các sự kiện và xử lý dữ liệu mà server gửi về.

  • Cấu hình Ezyclient: Tạo một cấu hình EzyConfig để thiết lập các thông số cần thiết như tên client, bật/tắt SSL, thời gian ping/pong để giữ kết nối, và số lần thử kết nối lại khi mất kết nối.
cauHinhEzyclient
cauHinhEzyclient
  • Khởi tạo client: Sử dụng cấu hình trên để tạo ra một đối tượng client mới (EzyClient) từ thư viện EzyClients.
khoiTaoClient
khoiTaoClient
  • Đăng ký các sự kiện kết nối:
    Thiết lập các sự kiện mà client sẽ lắng nghe và xử lý trong quá trình hoạt động:
    • DISCONNECTION: Xử lý khi bị mất kết nối.
    • CONNECTION_SUCCESS: Xử lý khi kết nối thành công.
    • CONNECTION_FAILURE: Xử lý khi kết nối thất bại.
    • HANDSHAKE: Xử lý quá trình bắt tay với server.
    • LOGIN: Xử lý khi đăng nhập thành công.
    • APP_ACCESS: Xử lý khi ứng dụng truy cập vào server.
    • LOGIN_ERROR: Xử lý khi xảy ra lỗi trong quá trình đăng nhập.
dangKyCacSuKienKetNoi
dangKyCacSuKienKetNoi
  • Thiết lập xử lý dữ liệu ứng dụng:
    Thiết lập các handler cho dữ liệu được server gửi về theo từng loại thông điệp:
    • “4”: Xử lý câu hỏi từ chatbot.
    • “1”: Xử lý danh sách người dùng.
    • “10”: Xử lý gợi ý người dùng.
    • “2”: Xử lý khi thêm người dùng vào danh sách liên hệ.
    • “6”: Xử lý gợi ý gửi tin nhắn đến người dùng khác.
thietLapXuLyDuLieu
thietLapXuLyDuLieu

Tổng kết

Mô hình client-server là một kiến trúc phổ biến trong phát triển ứng dụng, nơi client (máy khách) gửi yêu cầu và nhận phản hồi từ server (máy chủ). Server chịu trách nhiệm xử lý các yêu cầu và quản lý dữ liệu, trong khi client tương tác trực tiếp với người dùng. Để đảm bảo giao tiếp hiệu quả giữa client và server, các bước thiết lập kết nối cần được thực hiện cẩn thận, bao gồm định cấu hình các thông số kết nối, lắng nghe và phản hồi các sự kiện mạng.

Hàm _setup() trong thư viện ezyfox-server-flutter-client là một bước quan trọng trong quá trình kết nối, giúp chuẩn bị các cấu hình cần thiết và thiết lập các trình xử lý sự kiện để client có thể giao tiếp với server một cách hiệu quả. Mặc dù nó không trực tiếp kết nối ngay lập tức, nhưng hàm này đảm bảo rằng khi kết nối được thực hiện, client sẽ xử lý đúng các phản hồi và sự kiện từ server, giúp duy trì hoạt động của ứng dụng một cách ổn định.

Bình luận

avatar
Giang Đặng Ngọc 2024-09-17 10:41:14.757522 +0000 UTC

Nice

Giang Đặng Ngọc
Giang Đặng Ngọc 2024-09-17 10:41:24.020647 +0000 UTC

<3

Avatar
* Vui lòng trước khi bình luận.
Ảnh đại diện
  +2 Thích
+2