diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3c65f56 --- /dev/null +++ b/.clang-format @@ -0,0 +1,57 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignOperands: true +AlignTrailingComments: false +AlwaysBreakTemplateDeclarations: Yes +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBraces: Custom +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ContinuationIndentWidth: 8 +IncludeCategories: + - Regex: '^<.*' + Priority: 1 + - Regex: '^".*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentWidth: 4 +InsertNewlineAtEOF: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +TabWidth: 4 +... diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ee832a..89450be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,15 @@ add_executable(modbus src/pp/payload_builder.c src/pp/payload_parser.c src/modbus.c - src/main.c) + src/main.c + src/stcpsc.c + src/hexdump.c + src/hexdump.h + src/parsehex.c + src/parsehex.h) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +target_link_libraries(modbus PRIVATE Threads::Threads) target_include_directories(modbus PRIVATE src) diff --git a/src/hexdump.c b/src/hexdump.c new file mode 100644 index 0000000..5ea8072 --- /dev/null +++ b/src/hexdump.c @@ -0,0 +1,34 @@ +#include "hexdump.h" + +#include +#include + +/* https://gist.github.com/ccbrown/9722406 */ +void hexdump(const void *data, const size_t size) { + char ascii[17]; + ascii[16] = '\0'; + for (size_t i = 0; i < size; ++i) { + printf("%02X ", ((unsigned char *) data)[i]); + const uint8_t c = ((unsigned char *) data)[i]; + if (c >= ' ' && c <= '~') { + ascii[i % 16] = (char) c; + } else { + ascii[i % 16] = '.'; + } + if ((i + 1) % 8 == 0 || i + 1 == size) { + printf(" "); + if ((i + 1) % 16 == 0) { + printf("| %s \n", ascii); + } else if (i + 1 == size) { + ascii[(i + 1) % 16] = '\0'; + if ((i + 1) % 16 <= 8) { + printf(" "); + } + for (size_t j = (i + 1) % 16; j < 16; ++j) { + printf(" "); + } + printf("| %s \n", ascii); + } + } + } +} diff --git a/src/hexdump.h b/src/hexdump.h new file mode 100644 index 0000000..60ef8f1 --- /dev/null +++ b/src/hexdump.h @@ -0,0 +1,8 @@ +#ifndef HEXDUMP_H +#define HEXDUMP_H + +#include + +void hexdump(const void *data, size_t size); + +#endif //HEXDUMP_H diff --git a/src/main.c b/src/main.c index d9fdf0e..83d2f97 100644 --- a/src/main.c +++ b/src/main.c @@ -1,82 +1,18 @@ +#include #include -#include "modbus.h" - -/* https://gist.github.com/ccbrown/9722406 */ -void hexdump(const void *data, size_t size) -{ - char ascii[17]; - size_t i, j; - ascii[16] = '\0'; - for (i = 0; i < size; ++i) { - printf("%02X ", ((unsigned char *) data)[i]); - const uint8_t c = ((unsigned char *) data)[i]; - if (c >= ' ' && c <= '~') { - ascii[i % 16] = (char) c; - } else { - ascii[i % 16] = '.'; - } - if ((i + 1) % 8 == 0 || i + 1 == size) { - printf(" "); - if ((i + 1) % 16 == 0) { - printf("| %s \n", ascii); - } else if (i + 1 == size) { - ascii[(i + 1) % 16] = '\0'; - if ((i + 1) % 16 <= 8) { - printf(" "); - } - for (j = (i + 1) % 16; j < 16; ++j) { - printf(" "); - } - printf("| %s \n", ascii); - } - } - } -} - -static ssize_t parsehex(const char *s, uint8_t *out, size_t cap) -{ - uint8_t buf = 0; - uint8_t v; - bool first = true; - char c; - size_t sz = 0; - while (0 != (c = *s++)) { - if (c >= '0' && c <= '9') { - v = c - '0'; - } else if (c >= 'a' && c <= 'f') { - v = 10 + (c - 'a'); - } else if (c >= 'A' && c <= 'F') { - v = 10 + (c - 'A'); - } else { - continue; - } - - if (first) { - buf |= (v & 15) << 4; - first = false; - } else { - buf |= (v & 15); - if (cap == 0) { - return -1; - } - out[sz++] = buf; - buf = 0; - first = true; - cap--; - } - } - return (ssize_t) sz; -} +#include +#include +#include "modbus.h" -ModbusException_t soa(ModbusSlave_t *ms, ModbusFunction_t fcx, uint8_t slave_id) { +ModbusException_t startOfAccess(ModbusSlave_t *ms, ModbusFunction_t fcx, uint8_t slave_id) { printf("Start of access: fc%02d, slave %d\n", fcx, slave_id); return 0; } -void eoa(ModbusSlave_t *ms) { - printf("End of access\n"); -} -ModbusException_t rh(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { + +void endOfAccess(ModbusSlave_t *ms) { printf("End of access\n"); } + +ModbusException_t readHolding(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { printf("rh %d\n", i); switch (i) { case 107: @@ -93,7 +29,7 @@ ModbusException_t rh(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { } return 0; } -ModbusException_t ri(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { +ModbusException_t readInput(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { printf("ri %d\n", i); switch (i) { case 8: @@ -104,24 +40,20 @@ ModbusException_t ri(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { } return 0; } -ModbusException_t rc(ModbusSlave_t *ms, uint16_t i, bool *out) { +ModbusException_t readCoil(ModbusSlave_t *ms, uint16_t i, bool *out) { uint8_t store[] = { - 0b11001101, - 0b01101011, - 0b10110010, - 0b00001110, - 0b00011011, + 0b11001101, 0b01101011, 0b10110010, 0b00001110, 0b00011011, }; if (i >= 19 && i <= 55) { - int pos = i-19; - *out = 0 != (store[pos/8] & (1 << (pos%8))); + int pos = i - 19; + *out = 0 != (store[pos / 8] & (1 << (pos % 8))); } else { return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; } return 0; } -ModbusException_t rd(ModbusSlave_t *ms, uint16_t i, bool *out) { +ModbusException_t readDiscrete(ModbusSlave_t *ms, uint16_t i, bool *out) { uint8_t store[] = { 0b10101100, 0b11011011, @@ -130,62 +62,71 @@ ModbusException_t rd(ModbusSlave_t *ms, uint16_t i, bool *out) { if (i >= 196 && i <= 217) { int pos = i - 196; - *out = 0 != (store[pos/8] & (1 << (pos%8))); + *out = 0 != (store[pos / 8] & (1 << (pos % 8))); } else { return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; } return 0; } -ModbusException_t wc(ModbusSlave_t *pSlave, uint16_t i, bool b) { +ModbusException_t writeCoil(ModbusSlave_t *pSlave, uint16_t i, bool b) { printf("Write coil %d <- %d\n", i, b); return 0; } -ModbusException_t wh(ModbusSlave_t *pSlave, uint16_t i, uint16_t i1) { +ModbusException_t writeHolding(ModbusSlave_t *pSlave, uint16_t i, uint16_t i1) { printf("Write reg %d <- %d\n", i, i1); return 0; } -int main() -{ -#define buflen 1024 - uint8_t buf[buflen]; - uint8_t buf2[buflen]; - - // 11 03 006B 0003 7687 - // 11 04 0008 0001 B298 - // 11 01 0013 0025 0E84 - // 11 02 00C4 0016 BAA9 - // 11 05 00AC FF00 4E8B - // 11 06 0001 0003 9A9B - // 11 0F 0013 000A 02 CD01 BF0B - // 11 10 0001 0002 04 000A 0102 C6F0 - ssize_t pldlen = parsehex("11 03 006B 0003 7687", buf, buflen); - - ModbusSlave_t ms = { - .addr = 17, - .proto = MB_PROTO_RTU, - .startOfAccess = soa, - .endOfAccess = eoa, - .readHolding = rh, - .readInput = ri, - .readCoil = rc, - .readDiscrete = rd, - .writeCoil = wc, - .writeHolding = wh, +static ModbusSlave_t ms = { + .addr = 1, + .proto = MB_PROTO_TCP, + .startOfAccess = startOfAccess, + .endOfAccess = endOfAccess, + .readHolding = readHolding, + .readInput = readInput, + .readCoil = readCoil, + .readDiscrete = readDiscrete, + .writeCoil = writeCoil, + .writeHolding = writeHolding, +}; + +int msg_received_fce(int fd, const simple_msg_t *msg) { + uint8_t resp_buf[1024]; + size_t resp_size; + + printf("msg_received_fce\n"); + + ModbusError_t rv = mb_handleRequest(&ms, msg->data, msg->len, resp_buf, sizeof(resp_buf), &resp_size); + + if (rv != MB_OK) + return 1; // kick the client + + simple_msg_t resp = { + .data = resp_buf, + .len = resp_size, }; + simple_tcp_send(fd, &resp); + + return 0; +} - printf("Req:\n"); - hexdump(buf, pldlen); +void atexitcb() { simple_tcp_server_stop(); } - size_t respsize; - ModbusError_t e = mb_handleRequest(&ms, buf, pldlen, buf2, buflen, &respsize); - if (e) { - printf("Err %d\n", e); - } else { - printf("Resp:\n"); - hexdump(buf2, respsize); +int main() { + // ModbusError_t e = mb_handleRequest(&ms, buf, pldlen, buf2, buflen, &respsize); + + atexit(atexitcb); + + printf("Starting TCP server\n"); + simple_tcp_server_start(5002, 1024, msg_received_fce, NULL, NULL); + + printf("Thread created, waiting\n"); + while (!simple_tcp_server_ended()) { + sleep(1); } + printf("Main ended.\n"); + return 0; } diff --git a/src/main_simpletest.c b/src/main_simpletest.c new file mode 100644 index 0000000..053d9a7 --- /dev/null +++ b/src/main_simpletest.c @@ -0,0 +1,125 @@ +#include +#include "modbus.h" +#include "hexdump.h" +#include "parsehex.h" + +ModbusException_t soa(ModbusSlave_t *ms, ModbusFunction_t fcx, uint8_t slave_id) { + printf("Start of access: fc%02d, slave %d\n", fcx, slave_id); + return 0; +} +void eoa(ModbusSlave_t *ms) { + printf("End of access\n"); +} +ModbusException_t rh(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { + printf("rh %d\n", i); + switch (i) { + case 107: + *out = 0xAE41; + break; + case 108: + *out = 0x5652; + break; + case 109: + *out = 0x4340; + break; + default: + return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; + } + return 0; +} +ModbusException_t ri(ModbusSlave_t *ms, uint16_t i, uint16_t *out) { + printf("ri %d\n", i); + switch (i) { + case 8: + *out = 0xa; + break; + default: + return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; + } + return 0; +} +ModbusException_t rc(ModbusSlave_t *ms, uint16_t i, bool *out) { + uint8_t store[] = { + 0b11001101, + 0b01101011, + 0b10110010, + 0b00001110, + 0b00011011, + }; + + if (i >= 19 && i <= 55) { + int pos = i-19; + *out = 0 != (store[pos/8] & (1 << (pos%8))); + } else { + return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; + } + return 0; +} +ModbusException_t rd(ModbusSlave_t *ms, uint16_t i, bool *out) { + uint8_t store[] = { + 0b10101100, + 0b11011011, + 0b00110101, + }; + + if (i >= 196 && i <= 217) { + int pos = i - 196; + *out = 0 != (store[pos/8] & (1 << (pos%8))); + } else { + return MB_EXCEPTION_ILLEGAL_DATA_ADDRESS; + } + return 0; +} + +ModbusException_t wc(ModbusSlave_t *pSlave, uint16_t i, bool b) { + printf("Write coil %d <- %d\n", i, b); + return 0; +} +ModbusException_t wh(ModbusSlave_t *pSlave, uint16_t i, uint16_t i1) { + printf("Write reg %d <- %d\n", i, i1); + return 0; +} + +int main() +{ +#define buflen 1024 + uint8_t buf[buflen]; + uint8_t buf2[buflen]; + + // 11 03 006B 0003 7687 + // 11 04 0008 0001 B298 + // 11 01 0013 0025 0E84 + // 11 02 00C4 0016 BAA9 + // 11 05 00AC FF00 4E8B + // 11 06 0001 0003 9A9B + // 11 0F 0013 000A 02 CD01 BF0B + // 11 10 0001 0002 04 000A 0102 C6F0 + ssize_t pldlen = parsehex("11 03 006B 0003 7687", buf, buflen); + + ModbusSlave_t ms = { + .addr = 17, + .proto = MB_PROTO_RTU, + .startOfAccess = soa, + .endOfAccess = eoa, + .readHolding = rh, + .readInput = ri, + .readCoil = rc, + .readDiscrete = rd, + .writeCoil = wc, + .writeHolding = wh, + }; + + printf("Req:\n"); + hexdump(buf, pldlen); + + size_t respsize; + ModbusError_t e = mb_handleRequest(&ms, buf, pldlen, buf2, buflen, &respsize); + if (e) { + printf("Err %d\n", e); + } else { + printf("Resp:\n"); + hexdump(buf2, respsize); + } + + return 0; +} diff --git a/src/parsehex.c b/src/parsehex.c new file mode 100644 index 0000000..64ff533 --- /dev/null +++ b/src/parsehex.c @@ -0,0 +1,37 @@ +#include "parsehex.h" +#include + +ssize_t parsehex(const char *s, uint8_t *out, size_t cap) +{ + uint8_t buf = 0; + uint8_t v; + bool first = true; + char c; + size_t sz = 0; + while (0 != (c = *s++)) { + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = 10 + (c - 'a'); + } else if (c >= 'A' && c <= 'F') { + v = 10 + (c - 'A'); + } else { + continue; + } + + if (first) { + buf |= (v & 15) << 4; + first = false; + } else { + buf |= (v & 15); + if (cap == 0) { + return -1; + } + out[sz++] = buf; + buf = 0; + first = true; + cap--; + } + } + return (ssize_t) sz; +} \ No newline at end of file diff --git a/src/parsehex.h b/src/parsehex.h new file mode 100644 index 0000000..8ca6308 --- /dev/null +++ b/src/parsehex.h @@ -0,0 +1,9 @@ +#ifndef PARSEHEX_H +#define PARSEHEX_H + +#include +#include + +ssize_t parsehex(const char *s, uint8_t *out, size_t cap); + +#endif //PARSEHEX_H diff --git a/src/stcpsc.c b/src/stcpsc.c new file mode 100644 index 0000000..68d1157 --- /dev/null +++ b/src/stcpsc.c @@ -0,0 +1,419 @@ +/** + * \file stcpsc.c + * \brief Simple-to-use TCP server/client + * \author Petr Svoboda, 2014-10-11 + * + * 2024 - removed protocol handling, just using it to send and receive raw frames. Added static. + */ + +#include "stcpsc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STCP_MAX_MSG_SIZE 2048 +#define STCP_HOST_ADR_LEN 100 + +#define STCP_READ_ERR_NO 0 +#define STCP_READ_ERR_CLOSE STCP_DISCONNECT_REASON_CLOSED +#define STCP_READ_ERR_READ STCP_DISCONNECT_REASON_ERROR +#define STCP_READ_ERR_INTERNAL STCP_DISCONNECT_REASON_INTERNAL +#define STCP_READ_ERR_MTU STCP_DISCONNECT_REASON_MTU +#define STCP_READ_ERR_KICK STCP_DISCONNECT_REASON_KICK + +struct simple_tcp_queue; +typedef struct simple_tcp_queue simple_tcp_queue_t; + +struct simple_tcp_queue { + int cli_fd, mtu; + msg_received_fce_t cli_rec_fce; + simple_tcp_queue_t *next; +}; + +/* Server stuff */ +static volatile int srv_done = 0; +static unsigned short srv_port = 1234; +static int srv_mtu = -1; +static simple_tcp_queue_t *srv_queue = NULL; +static pthread_t server_thread = 0; +static fd_set active_fd_set; + +static msg_received_fce_t srv_rec_fce = NULL; +static cli_connected_fce_t srv_conn_fce = NULL; +static cli_disconnected_fce_t srv_disconn_fce = NULL; + +/* Client stuff */ +static sem_t cli_conn_sem; +static int tcpcli_portno; +static int client_mtu = -1; +static volatile int tcpcli_sockfd = -1; +static volatile int cli_thread_done = 0; +static pthread_t client_thread; +static char cli_host_address[STCP_HOST_ADR_LEN + 1]; + +static msg_received_fce_t cli_rec_fce = NULL; + + +bool simple_tcp_client_ended() { return cli_thread_done; } + +bool simple_tcp_server_ended() { return srv_done; } + +static int make_server_socket() { + int server_sock; + struct sockaddr_in name; + int yes = 1; + + /* Create the socket. */ + server_sock = socket(PF_INET, SOCK_STREAM, 0); + if (server_sock < 0) { + printf("TCPSRV: socket err\n"); + return -1; + } + + // tento radek zpusobi, ze pri opakovanem restartu serveru, bude volani + // funkce bind() uspesne, kdo neveri, at ho zakomentuje :)) + if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { + printf("TCPSRV: setsockopt err\n"); + return -1; + } + + /* Give the socket a name. */ + name.sin_family = AF_INET; + name.sin_port = htons(srv_port); + name.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(server_sock, (struct sockaddr *) &name, sizeof(name)) < 0) { + printf("TCPSRV: bind err\n"); + return -1; + } + + return server_sock; +} + +static void close_client(int fd, int reason) { + simple_tcp_queue_t *toDel = srv_queue; + simple_tcp_queue_t **prev = &srv_queue; + while (toDel) { + if (toDel->cli_fd == fd) { + /* match! */ + simple_tcp_queue_t *next = toDel->next; + free(toDel); + *prev = next; + break; + } + /* iterate */ + prev = &(toDel->next); + toDel = toDel->next; + } + FD_CLR(fd, &active_fd_set); /* Remove the client from the set */ + close(fd); + if (srv_disconn_fce) + srv_disconn_fce(fd, reason); +} + +static int read_from_client(simple_tcp_queue_t *cli) { + if (cli == NULL) { + return STCP_READ_ERR_INTERNAL; + } + + // limitation - it must all come in one frame! + unsigned char buff[cli->mtu]; + int readed = recv(cli->cli_fd, buff, STCP_MAX_MSG_SIZE + 2, 0); + + simple_msg_t msg = { + .data = buff, + .len = readed, + }; + + if (cli->cli_rec_fce(cli->cli_fd, &msg)) { + return STCP_READ_ERR_KICK; + } + + return STCP_READ_ERR_NO; +} + +static int read_from_srv_fd(int filedes) { + simple_tcp_queue_t *cli = srv_queue; + while (cli) { + if (cli->cli_fd == filedes) { + break; + } + cli = cli->next; + } + return read_from_client(cli); +} + +static void *srv_thread_function(void *arg) { + struct timeval tim; + int server_sock; + fd_set read_fd_set; + int fd, s_rv; + struct sockaddr_in clientname; + socklen_t size; + printf("srv_thread_function\n"); + + /* unused */ + (void) arg; + + /* Create the socket and set it up to accept connections. */ + server_sock = make_server_socket(); + if (server_sock < 0) { + printf("TCPSRV: fail to create server socket\n"); + return NULL; + } + printf("Socket bound\n"); + + if (listen(server_sock, 1) < 0) { + printf("TCPSRV: listen ERROR\n"); + return NULL; + } + printf("Server awaiting clients...\n"); + + /* Initialize the set of active sockets. */ + FD_ZERO(&active_fd_set); + FD_SET(server_sock, &active_fd_set); + + while (!srv_done) { + /* 200ms timeout */ + tim.tv_sec = 0; + tim.tv_usec = 200000; + /* Block until input arrives on one or more active sockets. */ + read_fd_set = active_fd_set; + s_rv = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &tim); + + if (s_rv == 0) { + continue; + } + + if (s_rv < 0) { + printf("TCPSRV: server select ERROR\n"); + return NULL; + } + + printf("TCPSRV: client selected\n"); + /* Service all the sockets with input pending. */ + for (fd = 0; fd < FD_SETSIZE; ++fd) { + if (FD_ISSET(fd, &read_fd_set)) { + if (fd == server_sock) { + /* Connection request on original socket. */ + int novy; + size = sizeof(clientname); + novy = accept(server_sock, (struct sockaddr *) &clientname, &size); + if (novy < 0) { + printf("TCPSRV: server accept ERROR\n"); + return NULL; + } + printf("TCPSRV: client accepted\n"); + + FD_SET(novy, &active_fd_set); /* Add new client to the set */ + simple_tcp_queue_t *cliq = (simple_tcp_queue_t *) malloc(sizeof(simple_tcp_queue_t)); + cliq->cli_fd = novy; + cliq->next = srv_queue; + cliq->mtu = srv_mtu; + cliq->cli_rec_fce = srv_rec_fce; + srv_queue = cliq; + + if (srv_conn_fce) { + if (srv_conn_fce(novy, inet_ntoa(clientname.sin_addr))) { + close_client(novy, STCP_DISCONNECT_REASON_KICK); + } + } + } else { + /* Data arriving on an already-connected socket. */ + printf("TCPSRV: receiving data from client\n"); + int crv = read_from_srv_fd(fd); + if (crv != STCP_READ_ERR_NO) { + close_client(fd, crv); + } + } + } + } + } + + /* Close all open sockets */ + for (fd = 0; fd < FD_SETSIZE; ++fd) { + if (FD_ISSET(fd, &read_fd_set)) { + close_client(fd, STCP_DISCONNECT_REASON_SERVER_CLOSE); + } + } + return NULL; +} + +static void *cli_thread_function(void *arg) { + struct timeval tim; + struct sockaddr_in host_addr; + struct hostent *host_ent; + fd_set active_fd_set; + fd_set read_fd_set; + int fd, s_rv; + char host_ip_str[INET_ADDRSTRLEN]; + + /* unused */ + (void) arg; + + tcpcli_sockfd = -1; + + /* Create a socket point */ + tcpcli_sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (tcpcli_sockfd < 0) { + printf("TCPCL: error opening socket\n"); + sem_post(&cli_conn_sem); + return NULL; + } + + host_ent = gethostbyname(cli_host_address); + if (host_ent == NULL) { + printf("TCPCL: error '%s': no such host\n", cli_host_address); + tcpcli_sockfd = -1; + sem_post(&cli_conn_sem); + return NULL; + } + + memset(&host_addr, 0, sizeof(host_addr)); + host_addr.sin_family = AF_INET; + host_addr.sin_port = htons(tcpcli_portno); + memcpy(&host_addr.sin_addr.s_addr, host_ent->h_addr, host_ent->h_length); + inet_ntop(AF_INET, &(host_addr.sin_addr), host_ip_str, INET_ADDRSTRLEN); + + printf("TCPCL: connecting to %s:%d\n", host_ip_str, tcpcli_portno); + /* Now connect to the server */ + if (connect(tcpcli_sockfd, (struct sockaddr *) &host_addr, sizeof(host_addr)) < 0) { + printf("TCPCL: error connect to: %s:%d\n", host_ip_str, tcpcli_portno); + printf("error: %s\n", strerror(errno)); + tcpcli_sockfd = -1; + sem_post(&cli_conn_sem); + return NULL; + } + simple_tcp_queue_t *client_que; + + printf("TCPCL: connected to %s:%d\n", host_ip_str, tcpcli_portno); + sem_post(&cli_conn_sem); + + client_que = (simple_tcp_queue_t *) malloc(sizeof(simple_tcp_queue_t)); + client_que->cli_fd = tcpcli_sockfd; + client_que->next = NULL; + client_que->mtu = client_mtu; + client_que->cli_rec_fce = cli_rec_fce; + + /* Initialize the set of active sockets. */ + FD_ZERO(&active_fd_set); + FD_SET(tcpcli_sockfd, &active_fd_set); + + while (!cli_thread_done) { + /* 200ms timeout */ + tim.tv_sec = 0; + tim.tv_usec = 200000; + /* Block until input arrives on one or more active sockets. */ + read_fd_set = active_fd_set; + s_rv = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &tim); + + if (s_rv == 0) { + continue; + } + + if (s_rv < 0) { + printf("TCPSRV: server select ERROR\n"); + return NULL; + } + + /* Service all the sockets with input pending. */ + for (fd = 0; fd < FD_SETSIZE; ++fd) { + if (FD_ISSET(fd, &read_fd_set) && fd == tcpcli_sockfd) { + if (read_from_client(client_que) != STCP_READ_ERR_NO) { + cli_thread_done = 1; + } + } + } + } + + free(client_que); + close(tcpcli_sockfd); + tcpcli_sockfd = -1; + return NULL; +} + +int simple_tcp_server_start(unsigned short port, int mtu, msg_received_fce_t rec, cli_connected_fce_t con, + cli_disconnected_fce_t dis) { + srv_done = 0; + srv_port = port; + srv_mtu = mtu; + srv_rec_fce = rec; + srv_conn_fce = con; + srv_disconn_fce = dis; + srv_queue = NULL; + if (pthread_create(&server_thread, NULL, srv_thread_function, NULL)) { + printf("Fail to create server thread\n"); + srv_done = 1; + return -1; + } + return 0; +} + +int simple_tcp_server_stop() { + if (srv_done || !server_thread) + return 0; + srv_done = 1; + if (pthread_join(server_thread, NULL)) + return -1; + return 0; +} + +int simple_tcp_client_start(const char *address_port, int mtu, int def_port, msg_received_fce_t rec) { + char *addrtempbuf = strdup(address_port); + + char *portp = strchr(addrtempbuf, ':'); + if (portp) { + *portp = '\0'; + portp++; + tcpcli_portno = atoi((portp)); + } else { + tcpcli_portno = def_port; + } + + cli_thread_done = 0; + client_mtu = mtu; + cli_rec_fce = rec; + strncpy(cli_host_address, addrtempbuf, STCP_HOST_ADR_LEN); + + free(addrtempbuf); + + if (sem_init(&cli_conn_sem, 0, 0) < 0) { + printf("TCPCL: sem_init\n"); + return -1; + } + + if (pthread_create(&client_thread, NULL, cli_thread_function, NULL)) { + cli_thread_done = 1; + printf("Failed to create pthread"); + return -1; + } + + sem_wait(&cli_conn_sem); + sem_destroy(&cli_conn_sem); + + return tcpcli_sockfd; +} + +int simple_tcp_client_done() { + if (cli_thread_done) { + return 0; + } + cli_thread_done = 1; + if (pthread_join(client_thread, NULL)) { + return -1; + } + return 0; +} + +int simple_tcp_send(int fd, const simple_msg_t *msg) { + write(fd, msg->data, msg->len); + return 0; +} diff --git a/src/stcpsc.h b/src/stcpsc.h new file mode 100644 index 0000000..a6be910 --- /dev/null +++ b/src/stcpsc.h @@ -0,0 +1,39 @@ +/** + * \file stcpsc.h + * \brief Simple-to-use TCP server/client + * \author Petr Svoboda, 2014-10-11 + */ + +#ifndef SIMPLE_TCP_SERVER_CLI_H +#define SIMPLE_TCP_SERVER_CLI_H + +#define STCP_DISCONNECT_REASON_CLOSED -1 +#define STCP_DISCONNECT_REASON_ERROR -2 +#define STCP_DISCONNECT_REASON_INTERNAL -3 +#define STCP_DISCONNECT_REASON_MTU -4 +#define STCP_DISCONNECT_REASON_KICK -5 +#define STCP_DISCONNECT_REASON_SERVER_CLOSE -6 + +#include + +typedef struct { + unsigned len; + unsigned char* data; +} simple_msg_t; + +typedef int (*msg_received_fce_t)(int fd, const simple_msg_t* msg); +typedef int (*cli_connected_fce_t)(int fd, const char* address); +typedef void (*cli_disconnected_fce_t)(int fd, int reason); + +int simple_tcp_server_start(unsigned short port, int mtu, msg_received_fce_t rec, cli_connected_fce_t con, cli_disconnected_fce_t dis); +int simple_tcp_server_stop(); + +int simple_tcp_client_start(const char* address_port, int mtu, int def_port, msg_received_fce_t rec); +int simple_tcp_client_done(); + +int simple_tcp_send(int fd, const simple_msg_t* msg); + +bool simple_tcp_client_ended(); +bool simple_tcp_server_ended(); + +#endif // SIMPLE_TCP_SERVER_CLI_H