服务端代码:bool worker_snapshot_preview_handle(WORKER_CTX *worker_ctx, struct tpsocket_fd *sock, struct list_head *buf)
{
struct timespec cur_time;
clock_gettime(CLOCK_MONOTONIC, &cur_time);
MYDEBUG("#### local snapshot producer reached : %ld.%ld\n", cur_time.tv_sec, cur_time.tv_nsec/1000/1000);
SNAPSHOT_PREVIEW_PRODUCER *producer;
SNAPSHOT_PREVIEW_CONSUMER *local_consumer, *nlocal_consumer;
SNAPSHOT_PREVIEW_SERVER *snapshot_preview_server;
DEV_INFO *dev_info = NULL;
MEDIACENTER_CTX *mediacenter_ctx = worker_ctx->top_ctx;
char *dev_id = NULL;
if (!worker_ctx || !sock || !buf) {
DBG_ERR("arg NULL\n");
return false;
}
if (!(producer = (SNAPSHOT_PREVIEW_PRODUCER *)snapshot_preview_producer_new(sock, worker_ctx->snapshot_preview_server))) {
DBG_ERR("snapshot_preview_producer_new failed\n");
return false;
}
snapshot_preview_producer_set_content_len(producer, sock->parser.http.length);
snapshot_preview_producer_set_dev_ip(producer, sock->addr);
list_for_each_entry(dev_info, &mediacenter_ctx->dev_info, list) {
if (!strncmp(dev_info->dev_ip, producer->dev_ip, sizeof(dev_info->dev_ip))) {
dev_id = dev_info->dev_id;
break;
}
}
snapshot_preview_producer_set_dev_id(producer, dev_id);
sock->handler.cb = tpsocket_event_snapshot_preview_producer_cb;
sock->handler.priv = producer;
snapshot_preview_cache_init(producer, producer->content_len);
snapshot_preview_start(worker_ctx, producer);
/* find local consumer */
snapshot_preview_server = worker_ctx->snapshot_preview_server;
list_for_each_entry_safe(local_consumer, nlocal_consumer, &snapshot_preview_server->consumer, list) {
if (!strncmp(local_consumer->dev_id, producer->dev_id, LEN_MAX_ID)) {
list_del(&local_consumer->list);
snapshot_preview_consumer_set_content_len(local_consumer, producer->content_len);
list_add_tail(&local_consumer->list, &producer->local_waiting);
}
}
return true;
}
bool worker_file_log_handle(WORKER_CTX *worker_ctx, struct tpsocket_fd *sock, struct list_head *buf)
{
// 1. 获取设备MAC地址
char *mac = common_find_key_from_buf(buf, "Device-Mac");
if (!mac || strlen(mac) < 12) {
DBG_ERR("Invalid MAC address\n");
return false;
}
DBG_ERR("mac: %s\n", mac);
// 2. MAC格式化处理(冒号转下划线)
char processed_mac[18] = {0};
strncpy(processed_mac, mac, sizeof(processed_mac)-1);
for (char *p = processed_mac; *p; p++) {
if (*p == ':') *p = '_';
}
DBG_ERR("processed_mac: %s\n", processed_mac);
// 3. 获取文件名
char *filename = common_find_key_from_buf(buf, "FileName");
if (!filename || filename[0] == '\0') {
DBG_ERR("Missing filename\n");
return false;
}
// 4. 创建目标目录
char dirpath[128];
snprintf(dirpath, sizeof(dirpath), "/mnt/sd_card/%s", processed_mac);
// 检创建父目录(/mnt/sd_card)
if (access("/mnt/sd_card", F_OK) != 0) {
if (mkdir("/mnt/sd_card", 0755) != 0) {
DBG_ERR("SD card mount failed: %s\n", strerror(errno));
return false;
}
}
// 创建设备目录(/mnt/sd_card/<processed_mac>)
if (access(dirpath, F_OK) != 0) {
if (mkdir(dirpath, 0755) != 0) {
DBG_ERR("Device dir '%s' creation failed: %s\n",
dirpath, strerror(errno));
return false;
}
}
// 5. 构建文件路径
char filepath[256];
snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, filename);
DBG_ERR("filepath: %s\n", filepath);
// 6. 获取内容长度(
char *content_length_str = common_find_key_from_buf(buf, "Content-Length");
if (!content_length_str || *content_length_str == '\0') {
DBG_ERR("Missing Content-Length header\n");
return false;
}
size_t content_len = atol(content_length_str);
if (content_len <= 0) {
DBG_ERR("Invalid content length: %s\n", content_length_str);
return false;
}
FILE *fp = fopen(filepath, "wb");
if (!fp) {
DBG_ERR("File open failed: %s\n", strerror(errno));
return false;
}
char buffer[8192];
size_t remaining = content_len;
ssize_t bytes_read = 0;
while (remaining > 0) {
size_t to_read = (remaining > sizeof(buffer)) ? sizeof(buffer) : remaining;
bytes_read = recv(sock->fd.fd, buffer, to_read, 0);
DBG_ERR("sock->fd.fd: %d\n", sock->fd.fd);
if (bytes_read <= 0) {
DBG_ERR("Socket read error: %zd\n", bytes_read);
fclose(fp);
return false;
}
if (fwrite(buffer, 1, bytes_read, fp) != bytes_read) {
DBG_ERR("File write error: %s\n", strerror(errno));
fclose(fp);
return false;
}
remaining -= bytes_read;
}
fsync(fileno(fp));
fclose(fp);
return true;
}客户端代码:# -*- coding: utf-8 -*-
import socket
import ssl
import time
import os
import logging
from urllib.parse import urlparse
def send_file(url: str, mac_address: str, file_path: str, chunk_size: int = 8192):
"""
Send file to server, placing file content in HTTP request body
:param url: Server URL (https://host:port/path)
:param mac_address: Device MAC address (format: A1:B2:C3:D4:E5:F6)
:param file_path: File path to send
:param chunk_size: Chunk size for transfer (default: 8KB)
:return: Returns True on success, False on failure
"""
# Parse URL
try:
parsed_url = urlparse(url)
host = parsed_url.hostname
port = parsed_url.port or 443 # Default HTTPS port
path = parsed_url.path
if not host:
raise ValueError("Missing host in URL")
except Exception as e:
logging.error(f"URL parsing failed: {e}")
return False
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[logging.FileHandler('transfer.log'), logging.StreamHandler()]
)
# 1. Prepare file information
try:
file_size = os.path.getsize(file_path)
filename = os.path.basename(file_path)
logging.info(f"Preparing to send file to {path}: {file_path} ({file_size} bytes)")
except Exception as e:
logging.error(f"File loading failed: {e}")
return False
# 2. Create SSL connection (with retry mechanism)
MAX_RETRIES = 3
RETRY_DELAY = 2 # seconds
ssl_sock = None
for attempt in range(MAX_RETRIES):
try:
context = ssl.create_default_context()
context.check_hostname = False # Can disable hostname verification in test environments
context.verify_mode = ssl.CERT_NONE # Can disable certificate verification in test environments
# context.load_verify_locations(cafile="/path/to/ca_cert.pem")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(120) # Set longer timeout
ssl_sock = context.wrap_socket(sock, server_hostname=host)
ssl_sock.connect((host, port))
logging.info(f"SSL connection established to {host}:{port}")
break
except Exception as e:
if attempt < MAX_RETRIES - 1:
logging.warning(f"SSL connection failed ({attempt+1}/{MAX_RETRIES}): {e}, retrying in {RETRY_DELAY} seconds...")
time.sleep(RETRY_DELAY)
else:
logging.error(f"Maximum retries reached: {e}")
return False
# 3. Build and send HTTP request headers
try:
# Construct HTTP request headers
headers = [
f"POST {path} HTTP/1.1",
f"Host: {host}",
"Cache-Control: no-cache",
"Connection: keep-alive", # Server requires connection closure
f"Device-Mac: {mac_address}", # MAC address header required by server
f"FileName: {filename}", # Filename header required by server
f"Content-Length: {file_size}", # Key: content length
"\r\n" # Empty line ends the headers
]
header_str = "\r\n".join(headers)
logging.debug(f"Request headers:\n{header_str}")
ssl_sock.sendall(header_str.encode('utf-8'))
except Exception as e:
logging.error(f"Request header send error: {e}")
if ssl_sock:
ssl_sock.close()
return False
# 4. Send file content (request body)
bytes_sent = 0
start_time = time.time()
last_progress_update = start_time
try:
with open(file_path, 'rb') as f:
while bytes_sent < file_size:
# Read and send data chunk
chunk = f.read(chunk_size)
if not chunk:
break
ssl_sock.sendall(chunk)
bytes_sent += len(chunk)
# Smart progress update (maximum once per second)
current_time = time.time()
if current_time - last_progress_update > 1.0:
progress = bytes_sent / file_size * 100
elapsed = current_time - start_time
speed = bytes_sent / (elapsed * 1024) # KB/s
logging.info(
f"Sending: {bytes_sent}/{file_size} bytes "
f"({progress:.1f}%), Speed: {speed:.2f} KB/s"
)
last_progress_update = current_time
logging.info(f"File transfer completed: {bytes_sent}/{file_size} bytes")
except Exception as e:
logging.error(f"Transfer interrupted: {e}")
if ssl_sock:
ssl_sock.close()
return False
# 5. Receive server response
try:
response = b""
while True:
chunk = ssl_sock.recv(1024)
if not chunk:
break
response += chunk
logging.info(f"Server response: {response.decode('utf-8', errors='ignore')}")
except Exception as e:
logging.error(f"Response receive error: {e}")
# 6. Close connection and calculate performance
ssl_sock.close()
elapsed = time.time() - start_time
if bytes_sent == file_size:
speed = file_size / (elapsed * 1024) # KB/s
logging.info(f"Transfer successful! Time: {elapsed:.2f} seconds, Speed: {speed:.2f} KB/s")
return True
logging.warning(f"Transfer incomplete: {bytes_sent}/{file_size} bytes")
return False
if __name__ == "__main__":
# Configure server information
SERVER_URL = "https://192.168.137.96:21443/log/file_log" # Ensure path matches server
TEST_MAC = "A1:B2:C3:D4:E5:F6" # MAC address format
TEST_FILE = "test_file.log" # File to send
print("="*50)
send_file(SERVER_URL, TEST_MAC, TEST_FILE)
print("="*50)
服务端错误码:[1014 15:42:55][Error] mediacenter: [worker.c:5933][tpsocket_event_worker_listen_cb] WORKERC: 192.168.137.1:59469 connected
[1014 15:42:55][Error] mediacenter: [worker.c:3665][worker_file_log_handle] mac: A1:B2:C3:D4:E5:F6
[1014 15:42:55][Error] mediacenter: [worker.c:3672][worker_file_log_handle] processed_mac: A1_B2_C3_D4_E5_F6
[1014 15:42:55][Error] mediacenter: [worker.c:3700][worker_file_log_handle] filepath: /mnt/sd_card/A1_B2_C3_D4_E5_F6/test_file.log
[1014 15:42:55][Error] mediacenter: [worker.c:3726][worker_file_log_handle] Socket read error: -1
客户端错误码:==================================================
2025-10-14 15:42:54,060 [INFO] Preparing to send file to /log/file_log: test_file.log (2097152 bytes)
2025-10-14 15:42:54,235 [INFO] SSL connection established to 192.168.137.96:21443
2025-10-14 15:42:54,239 [ERROR] Transfer interrupted: [WinError 10053] ��������е�������ֹ��һ���ѽ��������ӡ�
==================================================
最新发布