#define _POSIX_C_SOURCE 200809L #include "sim808.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* -------------------- Внутреннее состояние (аналог глобалей из прошивки) -------------------- */ static int g_sock = -1; static char* g_packet_buf = NULL; static size_t g_packet_len = 0; static size_t g_packet_cap = 0; static const size_t PACKET_INIT_CAP = 4096; /* стартовая ёмкость буфера */ static const size_t PACKET_MAX_CAP = (size_t)32 * 1024 * 1024; /* ограничение от дурака: 32 МБ */ /* -------------------- Утилиты -------------------- */ static sim800_stat_t ensure_capacity(size_t need_extra) { if (g_packet_len + need_extra <= g_packet_cap) return SIM808_OK; size_t new_cap = g_packet_cap ? g_packet_cap : PACKET_INIT_CAP; while (new_cap < g_packet_len + need_extra) { if (new_cap > PACKET_MAX_CAP / 2) { /* защита от переполнения и чрезмерного роста */ new_cap = PACKET_MAX_CAP; break; } new_cap *= 2; } if (new_cap < g_packet_len + need_extra) return SIM808_ERR; char* p = (char*)realloc(g_packet_buf, new_cap); if (!p) return SIM808_ERR; g_packet_buf = p; g_packet_cap = new_cap; return SIM808_OK; } static void packet_reset_buffer(void) { free(g_packet_buf); g_packet_buf = NULL; g_packet_len = 0; g_packet_cap = 0; } /* Получить строковый IP из sockaddr (локальный/удалённый) */ static sim800_stat_t sockaddr_to_ipstr(const struct sockaddr* sa, char* out, size_t outsz) { if (!sa || !out || outsz == 0) return SIM808_ERR; void* addr_ptr = NULL; if (sa->sa_family == AF_INET) { addr_ptr = &((struct sockaddr_in*)sa)->sin_addr; } else if (sa->sa_family == AF_INET6) { addr_ptr = &((struct sockaddr_in6*)sa)->sin6_addr; } else { return SIM808_ERR; } const char* r = inet_ntop(sa->sa_family, addr_ptr, out, (socklen_t)outsz); return (r ? SIM808_OK : SIM808_ERR); } /* -------------------- Реализация API -------------------- */ sim800_stat_t sim808_init(void) { /* На ПК ничего инициализировать не нужно */ return SIM808_OK; } sim800_stat_t sim808_open_connection(char* apn, char* host, char* port, char* self_ip) { (void)apn; /* на ПК APN не используется */ /* Если сокет уже открыт — закроем */ if (g_sock >= 0) { close(g_sock); g_sock = -1; } struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; /* IPv4 или IPv6 */ hints.ai_protocol = IPPROTO_TCP; struct addrinfo* res = NULL; int gai = getaddrinfo(host, port, &hints, &res); if (gai != 0 || !res) { return SIM808_ERR; } int sockfd = -1; struct addrinfo* it = res; for (; it; it = it->ai_next) { sockfd = (int)socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (sockfd < 0) continue; /* Можно поставить таймауты (опционально) */ struct timeval tv; tv.tv_sec = 10; tv.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); if (connect(sockfd, it->ai_addr, (socklen_t)it->ai_addrlen) == 0) { g_sock = sockfd; break; } close(sockfd); sockfd = -1; } freeaddrinfo(res); if (g_sock < 0) return SIM808_ERR; /* По запросу — вернуть локальный IP интерфейса, через который установилось соединение */ if (self_ip) { struct sockaddr_storage sa_local; socklen_t slen = (socklen_t)sizeof(sa_local); if (getsockname(g_sock, (struct sockaddr*)&sa_local, &slen) == 0) { if (sockaddr_to_ipstr((struct sockaddr*)&sa_local, self_ip, 64) != SIM808_OK) { /* если не смогли — просто оставим как есть */ self_ip[0] = '\0'; } } else { self_ip[0] = '\0'; } } return SIM808_OK; } sim800_stat_t sim808_close_connection(void) { if (g_sock >= 0) { close(g_sock); g_sock = -1; } return SIM808_OK; } sim800_stat_t packet_start(void) { packet_reset_buffer(); /* выделим стартовую ёмкость сразу, чтобы меньше реаллокаций */ g_packet_buf = (char*)malloc(PACKET_INIT_CAP); if (!g_packet_buf) { g_packet_cap = 0; return SIM808_ERR; } g_packet_cap = PACKET_INIT_CAP; g_packet_len = 0; return SIM808_OK; } sim800_stat_t packet_data(char* msg) { if (!msg) return SIM808_ERR; if (g_sock < 0) return SIM808_ERR; /* нет соединения */ if (!g_packet_buf && packet_start() != SIM808_OK) /* безопасности ради */ return SIM808_ERR; size_t n = strlen(msg); /* как и в вашей версии — работаем со строками */ if (n == 0) return SIM808_OK; if (ensure_capacity(n) != SIM808_OK) return SIM808_ERR; memcpy(g_packet_buf + g_packet_len, msg, n); g_packet_len += n; return SIM808_OK; } sim800_stat_t packet_end(void) { if (g_sock < 0) { packet_reset_buffer(); return SIM808_ERR; } size_t total = g_packet_len; size_t sent = 0; while (sent < total) { ssize_t r = send(g_sock, g_packet_buf + sent, total - sent, 0); if (r < 0) { if (errno == EINTR) continue; packet_reset_buffer(); return SIM808_ERR; } if (r == 0) { /* неожиданное закрытие */ packet_reset_buffer(); return SIM808_ERR; } sent += (size_t)r; } packet_reset_buffer(); return SIM808_OK; } sim800_stat_t sim808_send_msg(char* msg) { sim800_stat_t ret = SIM808_OK; ret = packet_start(); if (ret != SIM808_OK) return ret; ret = packet_data(msg); if (ret != SIM808_OK) { packet_reset_buffer(); return ret; } ret = packet_end(); return ret; } /* -------------------- Заглушки “модемных” функций -------------------- */ sim800_stat_t reset(void) { return SIM808_OK; } sim800_stat_t echo_off(void) { return SIM808_OK; } sim800_stat_t check_sim_connection(void) { return SIM808_OK; } sim800_stat_t chech_sim_card(void) { return SIM808_OK; } sim800_stat_t check_network_registration(void) { return SIM808_OK; } sim800_stat_t check_gprs_registration(void) { return SIM808_OK; } sim800_stat_t close_ip_sessions(void) { return SIM808_OK; } sim800_stat_t set_single_ip_mode(void) { return SIM808_OK; } sim800_stat_t set_apn(char* apn, char* user, char* pass) { (void)apn; (void)user; (void)pass; return SIM808_OK; } sim800_stat_t start_gprs(void) { return SIM808_OK; } /* Возвращаем локальный IP уже открытого сокета */ sim800_stat_t get_ip(char* ip_ret) { if (!ip_ret) return SIM808_ERR; if (g_sock < 0) { ip_ret[0] = '\0'; return SIM808_ERR; } struct sockaddr_storage sa_local; socklen_t slen = (socklen_t)sizeof(sa_local); if (getsockname(g_sock, (struct sockaddr*)&sa_local, &slen) != 0) { ip_ret[0] = '\0'; return SIM808_ERR; } return sockaddr_to_ipstr((struct sockaddr*)&sa_local, ip_ret, 64); } /* Непосредственные TCP-обёртки (оставлены для совместимости) */ sim800_stat_t open_tcp(char* host, char* port) { return sim808_open_connection(NULL, host, port, NULL); } sim800_stat_t close_tcp(void) { return sim808_close_connection(); } /* -------------------- Заглушки обработчиков/таймеров -------------------- */ void sim808_gprs_usart_handler(void) { /* на ПК не используется */ } void sim808_timeout_processing(void) { /* на ПК не используется */ } /* -------------------- Демонстрационный стейт-машин (как в вашем исходнике) -------------------- */ static unsigned char self_ip_buf[64] = {0}; enum { CMD_NONE = 0, CMD_SIM808_OPEN_CONNECTION, CMD_SIM808_SEND_SMG, CMD_SIM808_CLOSE_CONNECTION, }; static unsigned char cmd = 1; static sim800_stat_t conn_open = SIM808_ERR; static sim800_stat_t conn_clos = SIM808_ERR; static sim800_stat_t msg_sent = SIM808_ERR; void sim808_demo_send(void) { switch (cmd) { case CMD_SIM808_OPEN_CONNECTION: cmd = CMD_NONE; conn_open = SIM808_ERR; conn_open = sim808_open_connection( "unused-apn", /* игнорируется */ (char*)"158.160.43.29", /* или домен */ (char*)"9090", (char*)self_ip_buf /* вернём локальный IP интерфейса */ ); break; case CMD_SIM808_SEND_SMG: cmd = CMD_NONE; msg_sent = SIM808_ERR; msg_sent = sim808_send_msg("biba"); break; case CMD_SIM808_CLOSE_CONNECTION: cmd = CMD_NONE; conn_clos = SIM808_ERR; conn_clos = sim808_close_connection(); break; default: break; } }