From 865e198a5b28bf871362585167d98f09ec4dfcca Mon Sep 17 00:00:00 2001 From: VaclavT Date: Fri, 5 Nov 2021 15:27:21 +0100 Subject: [PATCH] tcp-server/client a bit improved --- clib/tcpnet.cpp | 77 +++++++++++++++++++++++++++++++++---------------- clib/tcpnet.h | 10 +++---- debug.lsp | 8 ++++- ml.cpp | 34 +++++++++++++--------- ml.h | 1 + tests/test.lsp | 2 -- 6 files changed, 85 insertions(+), 47 deletions(-) diff --git a/clib/tcpnet.cpp b/clib/tcpnet.cpp index 2a16187..bfbf6b9 100644 --- a/clib/tcpnet.cpp +++ b/clib/tcpnet.cpp @@ -23,7 +23,7 @@ TcpNet::TcpNet() {} #define TCPNET_BUFFER_SIZE 16256 -int TcpNet::server(int portno, std::function(std::string)> process_request) { +int TcpNet::server(int portno, std::function(std::string)> process_request) const { int sockfd, newsockfd; socklen_t clilen; @@ -40,6 +40,15 @@ int TcpNet::server(int portno, std::function(std::s serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); + // this allows immediate bind after exit of ml + int reuse = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) + error("setsockopt(SO_REUSEADDR) failed"); +#ifdef SO_REUSEPORT + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) + error("setsockopt(SO_REUSEPORT) failed"); +#endif + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("ERROR on binding"); @@ -53,22 +62,26 @@ int TcpNet::server(int portno, std::function(std::s if (newsockfd < 0) error("ERROR on accept"); - bzero(buffer,TCPNET_BUFFER_SIZE); - - n = read(newsockfd,buffer,TCPNET_BUFFER_SIZE - 1); - if (n < 0) error("ERROR reading from socket"); + while (true) { + bzero(buffer,TCPNET_BUFFER_SIZE); + n = read(newsockfd,buffer,TCPNET_BUFFER_SIZE - 1); + if (n == 0) break; // nothing to read from client anymore + if (n < 0) error("ERROR reading from socket"); - std::string request{buffer}; - std::pair response = process_request(request); - shutdown = response.first; - std::string response_str = response.second; + std::string request{buffer}; + std::pair response = process_request(request); + + shutdown = response.first; + std::string response_str = response.second; + + n = write(newsockfd, response_str.c_str(), response_str.size()); + if (n < 0) + error("ERROR writing to socket"); + + requests_processed++; + } - n = write(newsockfd, response_str.c_str(), response_str.size()); - if (n < 0) - error("ERROR writing to socket"); - close(newsockfd); - requests_processed++; } close(sockfd); @@ -76,10 +89,12 @@ int TcpNet::server(int portno, std::function(std::s return requests_processed; } -std::string TcpNet::client(const std::string address, int portno, const std::string &content) { + +std::vector TcpNet::client(const std::string &address, int portno, const std::vector &requests) const { int sockfd, n; struct sockaddr_in serv_addr; struct hostent *server; + std::vector responses; char buffer[TCPNET_BUFFER_SIZE]; @@ -102,17 +117,29 @@ std::string TcpNet::client(const std::string address, int portno, const std::str error("ERROR connecting"); - n = write(sockfd, content.c_str(), strlen(content.c_str())); - if (n < 0) - error("ERROR writing to socket"); - - bzero(buffer, TCPNET_BUFFER_SIZE); - n = read(sockfd,buffer, TCPNET_BUFFER_SIZE - 1); - if (n < 0) - error("ERROR reading from socket"); - + responses.reserve(requests.size()); + for(const auto &req : requests) { + n = write(sockfd, req.c_str(), strlen(req.c_str())); + if (n < 0) + error("ERROR writing to socket"); + + bzero(buffer, TCPNET_BUFFER_SIZE); + n = read(sockfd,buffer, TCPNET_BUFFER_SIZE - 1); + if (n < 0) + error("ERROR reading from socket"); + + responses.push_back(std::string(buffer)); + } + close(sockfd); - return std::string(buffer); + return responses; } + +std::string TcpNet::client(const std::string &address, int portno, const std::string &request) const { + std::vector c{request}; + auto response = client(address, portno, c); + + return response[0]; +} \ No newline at end of file diff --git a/clib/tcpnet.h b/clib/tcpnet.h index 1023d3c..b994dce 100644 --- a/clib/tcpnet.h +++ b/clib/tcpnet.h @@ -1,6 +1,7 @@ #pragma once #include +#include class TcpNet { @@ -9,12 +10,11 @@ public: TcpNet(); // starts listening on port - int server(int port, std::function(std::string)> process_request); + int server(int port, std::function(std::string)> process_request) const; - // writes content to server on address:port and returns response - std::string client(const std::string address, int port, const std::string &content); + // writes request to server on address:port and returns response + std::string client(const std::string &address, int portno, const std::string &request) const; - // TODO add support for vector of strings to be sent to server - // std::vector client(const std::string address, int port, const std::vector &content); + std::vector client(const std::string &address, int portno, const std::vector &requests) const; }; diff --git a/debug.lsp b/debug.lsp index 8211e97..24d56a1 100644 --- a/debug.lsp +++ b/debug.lsp @@ -1,11 +1,17 @@ (thread-create - (tcp-server 7777 (lambda (str) (list #t (+ "(print \"" (string-upcase str) "\")")))) + (tcp-server 7777 (lambda (str) (list #f (+ "(print \"" (string-upcase str) "\")")))) ) (thread-sleep 1) (thread-create + (define code (tcp-client "127.0.0.1" 7777 ("abcd" "xyz"))) + (for c code + (print "executing code:" c) + (eval (parse c)) + ) + (define code (tcp-client "127.0.0.1" 7777 "abcd")) (print "executing code:" code) (eval (parse code)) diff --git a/ml.cpp b/ml.cpp index 6501545..bf20ab3 100644 --- a/ml.cpp +++ b/ml.cpp @@ -83,6 +83,12 @@ MlValue::MlValue(bool b) { MlValue::MlValue(const std::vector &list) : type(LIST), list(list) {} +MlValue::MlValue(const std::vector &slist) : type(LIST) { + list.reserve(slist.size()); + for (size_t i = 0; i < slist.size(); i++) + list.push_back(MlValue::string(slist[i])); +} + MlValue MlValue::quote(const MlValue "ed) { MlValue result; result.type = QUOTE; @@ -1346,8 +1352,17 @@ MlValue tcp_client(std::vector args, MlEnvironment &env) { throw MlError(MlValue("tcp-client", tcp_client), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); TcpNet tcpclient; - std::string response = tcpclient.client(args[0].as_string(), args[1].as_int(), args[2].as_string()); - return MlValue::string(response); + if (args[2].is_list()) { + auto request_list = args[2].as_list(); + std::vector requests;// PERF reserve + + std::transform(request_list.begin(), request_list.end(), back_inserter(requests), std::mem_fn(&MlValue::as_string)); + std::vector response = tcpclient.client(args[0].as_string(), args[1].as_int(), requests); + return MlValue(response); + } else { + std::string response = tcpclient.client(args[0].as_string(), args[1].as_int(), args[2].as_string()); + return MlValue::string(response); + } } // Read a file and execute its code @@ -1640,7 +1655,7 @@ MlValue tail(std::vector args, MlEnvironment &env) { throw MlError(MlValue("tail", tail), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector result, list = args[0].as_list(); - + // PERF reserve result for (size_t i = 1; i < list.size(); i++) result.push_back(list[i]); @@ -1721,11 +1736,7 @@ MlValue string_regex_list(std::vector args, MlEnvironment &env) { auto found_matches = regexp_search2(args[0].as_string(), args[1].as_string(), match_mode, ignore_case); std::vector list; for(auto &l : found_matches) { - std::vector sublist; - for(auto &v : l) { - sublist.push_back(MlValue::string(v)); - } - list.push_back(sublist); + list.push_back(l); } return MlValue(list); } @@ -1739,12 +1750,7 @@ MlValue string_split(std::vector args, MlEnvironment &env) { // TODO do it more efficient std::vector elements = regexp_strsplit(args[0].as_string(), args[1].as_string()); - std::vector result{}; - - for (size_t i = 0; i < elements.size(); i++) - result.push_back(MlValue::string(elements[i])); - - return MlValue(result); + return MlValue(elements); } // converts string to upper or lower case diff --git a/ml.h b/ml.h index 6f31764..5aac218 100644 --- a/ml.h +++ b/ml.h @@ -103,6 +103,7 @@ public: MlValue(double f); MlValue(bool b); MlValue(const std::vector &list); // Constructs a list + MlValue(const std::vector &slist); // Constructs a list from vector of strings static MlValue quote(const MlValue "ed); // Construct a quoted value static MlValue atom(const std::string &s); diff --git a/tests/test.lsp b/tests/test.lsp index b95294b..52a1aa9 100644 --- a/tests/test.lsp +++ b/tests/test.lsp @@ -135,8 +135,6 @@ (ut::define-test "result of create table" '(ut::assert-equal ((0 "table created" 0)) (usql "create table a (i integer not null, s varchar(64), f float null, d date null, b boolean)"))) - - (ut::define-test "result tcp-client" '(ut::assert-equal "(print \"ABCD\")" (tcp-client "127.0.0.1" 7777 "abcd"))) ;(ut::define-test "result of " '(ut::assert-true )