#include #include #include #include #include #include #include #include #include "tcpnet.h" void error(const char *msg) { // perror(msg); throw std::runtime_error(msg); } // TODO do it as RAII TcpNet::TcpNet() {} #define TCPNET_BUFFER_SIZE 16256 int TcpNet::server(int portno, std::function(std::string)> process_request) const { int sockfd, newsockfd; socklen_t clilen; char buffer[TCPNET_BUFFER_SIZE]; struct sockaddr_in serv_addr, cli_addr; int n; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; 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"); listen(sockfd,5); clilen = sizeof(cli_addr); int requests_processed = 0; bool shutdown = false; while (!shutdown) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) error("ERROR on accept"); 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; n = write(newsockfd, response_str.c_str(), response_str.size()); if (n < 0) error("ERROR writing to socket"); requests_processed++; } close(newsockfd); } close(sockfd); return requests_processed; } 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]; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); server = gethostbyname(address.c_str()); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(0); } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); 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 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]; }