From 080a1d81e2e38b989f16a2507febc167d858de76 Mon Sep 17 00:00:00 2001 From: pictures23333 Date: Mon, 15 Dec 2025 20:00:32 +0800 Subject: [PATCH] feat(tcp_server): receive image via TCP --- .gitignore | 3 +- main/CMakeLists.txt | 2 +- main/main.cpp | 40 +-- main/main.h | 15 +- main/mesh_main.c | 54 +--- main/mqtt_app.c | 83 ------ main/tcphelper.c | 308 ++++++++++++++++++++ main/tcphelper.h | 12 + mqtt_client.py | 21 -- sdkconfig | 2 +- tcp_client.py | 38 +++ mqtt_client_image.py => tcp_client_image.py | 0 12 files changed, 396 insertions(+), 182 deletions(-) delete mode 100644 main/mqtt_app.c create mode 100644 main/tcphelper.c create mode 100644 main/tcphelper.h delete mode 100644 mqtt_client.py create mode 100644 tcp_client.py rename mqtt_client_image.py => tcp_client_image.py (100%) diff --git a/.gitignore b/.gitignore index c64e043..1eaf759 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ __pycache__ .devcontainer .vscode -main/old \ No newline at end of file +main/old +main/drop_mqtt \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 9a2f4a0..46d89fb 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -5,6 +5,6 @@ file(GLOB_RECURSE MY_LIB_SOURCES ) idf_component_register( - SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "mqtt_app.c" ${MY_LIB_SOURCES} + SRCS "main.cpp" "EPD.cpp" "mesh_main.c" "mesh_netif.c" "tcphelper.c" ${MY_LIB_SOURCES} INCLUDE_DIRS "" "ESP32epdx/src" "ESP32epdx/src/GUI" ) diff --git a/main/main.cpp b/main/main.cpp index 4f3c734..965a0fe 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -6,6 +6,7 @@ #include "ESP32epdx.h" #include "EPD.h" #include "mesh_netif.h" +#include "tcphelper.h" #include "main.h" std::mutex epd_mtx; @@ -15,30 +16,34 @@ SPI: SCK = 6 ; MISO = 2 ; MOSI = 7 ; SS = 16 EPD: RES = 22 ; DC = 23 ; CS = 1 ; BUSY = 0 */ -extern "C" void mqtt_event_data_callback_func(char* topic, int topic_len, char* data, int data_len) -{ - if (strncmp(topic, APP_MQTT_TOPIC, topic_len)) - return; - - // process - if (data_len == 5624) - { - printf("TOPIC (len: %d) -> %.*s\nMESSAGE (len: %d)\n", topic_len, topic_len, topic, data_len); - if (epd_mtx.try_lock()) +#define IMAGE_BUF_SIZE 5624 +uint8_t *image_buf = nullptr; + +extern "C" int tcp_server_callback(char *buf, size_t len) { + if (!strncmp(buf, "ping", 4)) { // ping + return 0; + } else if (!strncmp(buf, "image", 5) && len == 5 + 5624) { // update image + if (epd_mtx.try_lock()) { // work EPD_HW_Init_Fast(); - EPD_Display((unsigned char *)data); + EPD_Display((unsigned char *)(buf+5)); EPD_DeepSleep(); + //vTaskDelay(pdMS_TO_TICKS(1000)); + // unlock epd_mtx.unlock(); + + return 0; } else { // locked - return; + return -2; } + } else { // invalid request + return -3; } } @@ -46,6 +51,10 @@ extern "C" void app_main() { initArduino(); + // init image_buf + image_buf = (uint8_t *)malloc(IMAGE_BUF_SIZE); + memset(image_buf, 0, IMAGE_BUF_SIZE); + //Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -60,11 +69,8 @@ extern "C" void app_main() mesh_main(); - // Connect to WiFi - // connect_wifi(); - - // Connect to MQTT broker - // connect_mqtt(); + // tcp server + start_tcp_server(); // Arduino-like setup() diff --git a/main/main.h b/main/main.h index b725cf8..f543239 100644 --- a/main/main.h +++ b/main/main.h @@ -12,16 +12,19 @@ #define CONFIG_MESH_CHANNEL 0 // change these -static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 }; -#define CONFIG_MESH_ROUTER_SSID "FBK_the_cutest_fox" -#define CONFIG_MESH_ROUTER_PASSWD "zsfv3210" +static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x76 }; +//#define CONFIG_MESH_ROUTER_SSID "FBK_the_cutest_fox" +//#define CONFIG_MESH_ROUTER_PASSWD "zsfv3210" +#define CONFIG_MESH_ROUTER_SSID "FastCyberRoom" +#define CONFIG_MESH_ROUTER_PASSWD "dyes1107@" #define CONFIG_MESH_AP_AUTHMODE WIFI_AUTH_WPA2_PSK #define CONFIG_MESH_AP_PASSWD "RASRASRAS" #define CONFIG_MESH_AP_CONNECTIONS 6 // number of nodes #define CONFIG_MESH_NON_MESH_AP_CONNECTIONS 0 // number of non-node devices -#define APP_MQTT_URI "mqtt://10.0.0.1:1883" -#define APP_MQTT_TOPIC "adboard" +#define CONFIG_TCP_SERVER_BIND_ADDRESS "0.0.0.0" +#define CONFIG_TCP_SERVER_BIND_PORT "8888" +#define CONFIG_TCP_SERVER_RXBUFFER_SIZE 8192 /******************************************************* * Function Declarations @@ -31,7 +34,7 @@ static const uint8_t MESH_ID[6] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77 }; extern "C" { #endif -void mqtt_event_data_callback_func(char* topic, int topic_len, char* data, int data_len); +int tcp_server_callback(char *buf, size_t len); #ifdef __cplusplus } diff --git a/main/mesh_main.c b/main/mesh_main.c index 2773bf4..06701f0 100644 --- a/main/mesh_main.c +++ b/main/mesh_main.c @@ -52,9 +52,6 @@ static uint8_t s_mesh_tx_payload[CONFIG_MESH_ROUTE_TABLE_SIZE*6+1]; /******************************************************* * Function Declarations *******************************************************/ -// interaction with public mqtt broker -void mqtt_app_start(void); -void mqtt_app_publish(char* topic, char *publish_string); /******************************************************* * Function Definitions @@ -88,52 +85,6 @@ void static recv_cb(mesh_addr_t *from, mesh_data_t *data) } } -void esp_mesh_mqtt_task(void *arg) -{ - is_running = true; - char *print; - mesh_data_t data; - esp_err_t err; - mqtt_app_start(); - while (is_running) { - //asprintf(&print, "layer:%d IP:" IPSTR, esp_mesh_get_layer(), IP2STR(&s_current_ip)); - //ESP_LOGI(MESH_TAG, "Tried to publish %s", print); - //mqtt_app_publish("/topic/ip_mesh", print); - //free(print); - //if (esp_mesh_is_root()) { - // esp_mesh_get_routing_table((mesh_addr_t *) &s_route_table, - // CONFIG_MESH_ROUTE_TABLE_SIZE * 6, &s_route_table_size); - // data.size = s_route_table_size * 6 + 1; - // data.proto = MESH_PROTO_BIN; - // data.tos = MESH_TOS_P2P; - // s_mesh_tx_payload[0] = CMD_ROUTE_TABLE; - // memcpy(s_mesh_tx_payload + 1, s_route_table, s_route_table_size*6); - // data.data = s_mesh_tx_payload; - // for (int i = 0; i < s_route_table_size; i++) { - // err = esp_mesh_send(&s_route_table[i], &data, MESH_DATA_P2P, NULL, 0); - // ESP_LOGI(MESH_TAG, "Sending routing table to [%d] " - // MACSTR ": sent with err code: %d", i, MAC2STR(s_route_table[i].addr), err); - // } - //} - vTaskDelay(2 * 1000 / portTICK_PERIOD_MS); - } - vTaskDelete(NULL); -} - -esp_err_t esp_mesh_comm_mqtt_task_start(void) -{ - static bool is_comm_mqtt_task_started = false; - - s_route_table_lock = xSemaphoreCreateMutex(); - - if (!is_comm_mqtt_task_started) { - xTaskCreate(esp_mesh_mqtt_task, "mqtt task", 4096, NULL, 5, NULL); - //xTaskCreate(check_button, "check button task", 3072, NULL, 5, NULL); - is_comm_mqtt_task_started = true; - } - return ESP_OK; -} - void mesh_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -325,7 +276,6 @@ void ip_event_handler(void *arg, esp_event_base_t event_base, ESP_ERROR_CHECK(esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns)); mesh_netif_start_root_ap(esp_mesh_is_root(), dns.ip.u_addr.ip4.addr); //#endif - esp_mesh_comm_mqtt_task_start(); } @@ -369,11 +319,11 @@ void mesh_main(void) cfg.channel = CONFIG_MESH_CHANNEL; cfg.router.ssid_len = strlen(CONFIG_MESH_ROUTER_SSID); memcpy((uint8_t *) &cfg.router.ssid, CONFIG_MESH_ROUTER_SSID, cfg.router.ssid_len); + memcpy((uint8_t *) &cfg.router.password, CONFIG_MESH_ROUTER_PASSWD, strlen(CONFIG_MESH_ROUTER_PASSWD)); - //memset((uint8_t *) &cfg.router.ssid, 0, 32); //memset((uint8_t *) &cfg.router.password, 0, 64); - //cfg.router.ssid_len = 0; + /* mesh softAP */ ESP_ERROR_CHECK(esp_mesh_set_ap_authmode(CONFIG_MESH_AP_AUTHMODE)); cfg.mesh_ap.max_connection = CONFIG_MESH_AP_CONNECTIONS; diff --git a/main/mqtt_app.c b/main/mqtt_app.c deleted file mode 100644 index a88736c..0000000 --- a/main/mqtt_app.c +++ /dev/null @@ -1,83 +0,0 @@ -/* Mesh IP Internal Networking Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include "esp_log.h" -#include "esp_system.h" -#include "esp_netif.h" -#include "esp_tls.h" - -#include "mqtt_client.h" -#include "main.h" - -static const char *TAG = "mesh_mqtt"; -static esp_mqtt_client_handle_t s_client = NULL; - -static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) -{ - switch (event->event_id) { - case MQTT_EVENT_CONNECTED: - ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); - if (esp_mqtt_client_subscribe(s_client, APP_MQTT_TOPIC, 0) < 0) { - // Disconnect to retry the subscribe after auto-reconnect timeout - esp_mqtt_client_disconnect(s_client); - } - break; - case MQTT_EVENT_DISCONNECTED: - ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); - break; - - case MQTT_EVENT_SUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_PUBLISHED: - ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); - break; - case MQTT_EVENT_DATA: - ESP_LOGI(TAG, "MQTT_EVENT_DATA"); - mqtt_event_data_callback_func(event->topic, event->topic_len, event->data, event->data_len); - //ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic); - //ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data); - break; - case MQTT_EVENT_ERROR: - ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); - break; - default: - ESP_LOGI(TAG, "Other event id:%d", event->event_id); - break; - } - return ESP_OK; -} - -static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { - ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRId32 "", base, event_id); - mqtt_event_handler_cb(event_data); -} - -void mqtt_app_publish(char* topic, char *publish_string) -{ - if (s_client) { - int msg_id = esp_mqtt_client_publish(s_client, topic, publish_string, 0, 1, 0); - ESP_LOGI(TAG, "sent publish returned msg_id=%d", msg_id); - } -} - -void mqtt_app_start(void) -{ - esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = APP_MQTT_URI, - .buffer.size = 8192 - }; - - s_client = esp_mqtt_client_init(&mqtt_cfg); - esp_mqtt_client_register_event(s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, s_client); - esp_mqtt_client_start(s_client); -} \ No newline at end of file diff --git a/main/tcphelper.c b/main/tcphelper.c new file mode 100644 index 0000000..c364706 --- /dev/null +++ b/main/tcphelper.c @@ -0,0 +1,308 @@ +/* BSD non-blocking socket example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sys/socket.h" +#include "netdb.h" +#include "errno.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "tcphelper.h" +#include "main.h" + +/** + * @brief Indicates that the file descriptor represents an invalid (uninitialized or closed) socket + * + * Used in the TCP server structure `sock[]` which holds list of active clients we serve. + */ +#define INVALID_SOCK (-1) + +/** + * @brief Time in ms to yield to all tasks when a non-blocking socket would block + * + * Non-blocking socket operations are typically executed in a separate task validating + * the socket status. Whenever the socket returns `EAGAIN` (idle status, i.e. would block) + * we have to yield to all tasks to prevent lower priority tasks from starving. + */ +#define YIELD_TO_ALL_MS 50 + +/** + * @brief Utility to log socket errors + * + * @param[in] tag Logging tag + * @param[in] sock Socket number + * @param[in] err Socket errno + * @param[in] message Message to print + */ +static void log_socket_error(const char *tag, const int sock, const int err, const char *message) +{ + ESP_LOGE(tag, "[sock=%d]: %s\n" + "error=%d: %s", sock, message, err, strerror(err)); +} + +/** + * @brief Tries to receive data from specified sockets in a non-blocking way, + * i.e. returns immediately if no data. + * + * @param[in] tag Logging tag + * @param[in] sock Socket for reception + * @param[out] data Data pointer to write the received data + * @param[in] max_len Maximum size of the allocated space for receiving data + * @return + * >0 : Size of received data + * =0 : No data available + * -1 : Error occurred during socket read operation + * -2 : Socket is not connected, to distinguish between an actual socket error and active disconnection + */ +static int try_receive(const char *tag, const int sock, char * data, size_t max_len) +{ + int len = recv(sock, data, max_len, 0); + if (len < 0) { + if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; // Not an error + } + if (errno == ENOTCONN) { + ESP_LOGW(tag, "[sock=%d]: Connection closed", sock); + return -2; // Socket has been disconnected + } + log_socket_error(tag, sock, errno, "Error occurred during receiving"); + return -1; + } + + return len; +} + +/** + * @brief Sends the specified data to the socket. This function blocks until all bytes got sent. + * + * @param[in] tag Logging tag + * @param[in] sock Socket to write data + * @param[in] data Data to be written + * @param[in] len Length of the data + * @return + * >0 : Size the written data + * -1 : Error occurred during socket write operation + */ +static int socket_send(const char *tag, const int sock, const char * data, const size_t len) +{ + int to_write = len; + while (to_write > 0) { + int written = send(sock, data + (len - to_write), to_write, 0); + if (written < 0 && errno != EINPROGRESS && errno != EAGAIN && errno != EWOULDBLOCK) { + log_socket_error(tag, sock, errno, "Error occurred during sending"); + return -1; + } + to_write -= written; + } + return len; +} + +/** + * @brief Returns the string representation of client's address (accepted on this server) + */ +static inline char* get_clients_address(struct sockaddr_storage *source_addr) +{ + static char address_str[128]; + char *res = NULL; + // Convert ip address to string + if (source_addr->ss_family == PF_INET) { + res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str) - 1); + } +#ifdef CONFIG_LWIP_IPV6 + else if (source_addr->ss_family == PF_INET6) { + res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str) - 1); + } +#endif + if (!res) { + address_str[0] = '\0'; // Returns empty string if conversion didn't succeed + } + return address_str; +} + +static void tcp_server_task(void *pvParameters) +{ + static char rx_buffer[CONFIG_TCP_SERVER_RXBUFFER_SIZE]; + static const char *TAG = "nonblocking-socket-server"; + SemaphoreHandle_t *server_ready = pvParameters; + struct addrinfo hints = { .ai_socktype = SOCK_STREAM }; + struct addrinfo *address_info; + int listen_sock = INVALID_SOCK; + const size_t max_socks = CONFIG_LWIP_MAX_SOCKETS - 1; + static int sock[CONFIG_LWIP_MAX_SOCKETS - 1]; + + // Prepare a list of file descriptors to hold client's sockets, mark all of them as invalid, i.e. available + for (int i=0; iai_family, address_info->ai_socktype, address_info->ai_protocol); + + if (listen_sock < 0) { + log_socket_error(TAG, listen_sock, errno, "Unable to create socket"); + goto error; + } + ESP_LOGI(TAG, "Listener socket created"); + + // Marking the socket as non-blocking + int flags = fcntl(listen_sock, F_GETFL); + if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) { + log_socket_error(TAG, listen_sock, errno, "Unable to set socket non blocking"); + goto error; + } + ESP_LOGI(TAG, "Socket marked as non blocking"); + + // Binding socket to the given address + int err = bind(listen_sock, address_info->ai_addr, address_info->ai_addrlen); + if (err != 0) { + log_socket_error(TAG, listen_sock, errno, "Socket unable to bind"); + goto error; + } + ESP_LOGI(TAG, "Socket bound on %s:%s", CONFIG_TCP_SERVER_BIND_ADDRESS, CONFIG_TCP_SERVER_BIND_PORT); + + // Set queue (backlog) of pending connections to one (can be more) + err = listen(listen_sock, 1); + if (err != 0) { + log_socket_error(TAG, listen_sock, errno, "Error occurred during listen"); + goto error; + } + ESP_LOGI(TAG, "Socket listening"); + xSemaphoreGive(*server_ready); + + // Main loop for accepting new connections and serving all connected clients + while (1) { + struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6 + socklen_t addr_len = sizeof(source_addr); + + // Find a free socket + int new_sock_index = 0; + for (new_sock_index=0; new_sock_index print it's address + ESP_LOGI(TAG, "[sock=%d]: Connection accepted from IP:%s", sock[new_sock_index], get_clients_address(&source_addr)); + + // ...and set the client's socket non-blocking + flags = fcntl(sock[new_sock_index], F_GETFL); + if (fcntl(sock[new_sock_index], F_SETFL, flags | O_NONBLOCK) == -1) { + log_socket_error(TAG, sock[new_sock_index], errno, "Unable to set socket non blocking"); + goto error; + } + ESP_LOGI(TAG, "[sock=%d]: Socket marked as non blocking", sock[new_sock_index]); + } + } + + // We serve all the connected clients in this loop + for (int i=0; i try to serve it + memset(rx_buffer, 0, sizeof(rx_buffer)); + int len = try_receive(TAG, sock[i], rx_buffer, sizeof(rx_buffer)); + if (len < 0) { + // Error occurred within this client's socket -> close and mark invalid + ESP_LOGI(TAG, "[sock=%d]: try_receive() returned %d -> closing the socket", sock[i], len); + close(sock[i]); + sock[i] = INVALID_SOCK; + } else if (len > 0) { + // Received some data -> echo back + //ESP_LOGI(TAG, "[sock=%d]: Received %.*s", sock[i], len, rx_buffer); + ESP_LOGI(TAG, "[sock=%d]: Received message (len=%d)", sock[i], len); + + char tx_buffer[128] = {0}; + int result = tcp_server_callback(rx_buffer, len); + switch (result) { + case 0: + strcpy(tx_buffer, "ok\n"); + break; + case -1: + strcpy(tx_buffer, "invalid data\n"); + break; + case -2: + strcpy(tx_buffer, "locked\n"); + break; + case -3: + strcpy(tx_buffer, "invalid request\n"); + break; + default: + strcpy(tx_buffer, "unknown error\n"); + break; + } + + len = socket_send(TAG, sock[i], tx_buffer, strlen(tx_buffer)); + if (len < 0) { + // Error occurred on write to this socket -> close it and mark invalid + ESP_LOGI(TAG, "[sock=%d]: socket_send() returned %d -> closing the socket", sock[i], len); + close(sock[i]); + sock[i] = INVALID_SOCK; + } else { + // Successfully echoed to this socket + ESP_LOGI(TAG, "[sock=%d]: Written %.*s", sock[i], strlen(tx_buffer), tx_buffer); + } + } + + } // one client's socket + } // for all sockets + + // Yield to other tasks + vTaskDelay(pdMS_TO_TICKS(YIELD_TO_ALL_MS)); + } + +error: + if (listen_sock != INVALID_SOCK) { + close(listen_sock); + } + + for (int i=0; i ")) - if choice == 1: - message = bytes(mqtt_client_image.image1) - elif choice == 2: - message = bytes(mqtt_client_image.image2) - client.publish(TOPIC, message) - print(f"send") \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index e249d79..d0b8192 100644 --- a/sdkconfig +++ b/sdkconfig @@ -1754,7 +1754,7 @@ CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y CONFIG_LWIP_TIMERS_ONDEMAND=y CONFIG_LWIP_ND6=y # CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set -CONFIG_LWIP_MAX_SOCKETS=10 +CONFIG_LWIP_MAX_SOCKETS=32 # CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set # CONFIG_LWIP_SO_LINGER is not set CONFIG_LWIP_SO_REUSE=y diff --git a/tcp_client.py b/tcp_client.py new file mode 100644 index 0000000..ded8bc5 --- /dev/null +++ b/tcp_client.py @@ -0,0 +1,38 @@ +import socket + +import tcp_client_image + +SERVER_ADDRESS = ('172.16.0.249', 8888) + +client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +client_socket.connect(SERVER_ADDRESS) + +try: + while True: + message = "" + choice = int(input("image > ")) + if choice == 1: + message = b"image" + bytes(tcp_client_image.image1) + elif choice == 2: + message = b"image" + bytes(tcp_client_image.image2) + elif choice == 3: + message = b"ping" + else: + message = b"something_invalid" + + # 4. 傳送資料 (需要編碼成 bytes) + client_socket.sendall(message) + + # 5. 接收伺服器的回應 + # 接收最多 1024 bytes + data = client_socket.recv(1024) + if data: + print(f"收到伺服器訊息: {data.decode('utf-8')}") + else: + print("伺服器已關閉連線。") + break +except ConnectionResetError: + print("連線被伺服器重置。") +finally: + client_socket.close() + print("客戶端連線已關閉。") \ No newline at end of file diff --git a/mqtt_client_image.py b/tcp_client_image.py similarity index 100% rename from mqtt_client_image.py rename to tcp_client_image.py