change demo to be a real TCP server
This commit is contained in:
@@ -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
|
||||
...
|
||||
+10
-1
@@ -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)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "hexdump.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef HEXDUMP_H
|
||||
#define HEXDUMP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void hexdump(const void *data, size_t size);
|
||||
|
||||
#endif //HEXDUMP_H
|
||||
+61
-120
@@ -1,82 +1,18 @@
|
||||
#include <stcpsc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
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];
|
||||
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,
|
||||
};
|
||||
|
||||
// 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);
|
||||
int msg_received_fce(int fd, const simple_msg_t *msg) {
|
||||
uint8_t resp_buf[1024];
|
||||
size_t resp_size;
|
||||
|
||||
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("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);
|
||||
|
||||
printf("Req:\n");
|
||||
hexdump(buf, pldlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
void atexitcb() { simple_tcp_server_stop(); }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "parsehex.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef PARSEHEX_H
|
||||
#define PARSEHEX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
ssize_t parsehex(const char *s, uint8_t *out, size_t cap);
|
||||
|
||||
#endif //PARSEHEX_H
|
||||
+419
@@ -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 <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <stdbool.h>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user