Witajcie.
Napisałem prosty server sock. Potrzebuję opinii kogoś kto ma doświadczenie w pisaniu socketów wielowątkowych tryb blokujący.
Czy poniższe rozwiązanie jest ok? może warto coś zmienić...
Projekt do pobrania: https://craws.pl/srv.7z
server diała pod linuxem, można testować telnetem łącząc się na port 2121
main.hpp
#ifndef _CRAWS_MAIN_HPP_
#define _CRAWS_MAIN_HPP_
//----------------------------------------------------------------------------------------------------
#include <thread>
#include "server_socket.hpp"
#include "client_socket.hpp"
//----------------------------------------------------------------------------------------------------
#endif
//----------------------------------------------------------------------------------------------------
main.cpp
#include "main.hpp"
//----------------------------------------------------------------------------------------------------
#define DEF_SERWER_PORT 2121
#define DEF_SERWER_IP "127.0.0.1"
#define DEF_MAX_CONNECTION 16 // --- Maksymalna ilość połączeń oczekujących
//----------------------------------------------------------------------------------------------------
int main()
{
ServerSock serv_sock(DEF_SERWER_IP, DEF_SERWER_PORT, DEF_MAX_CONNECTION);
while(true)
{
std::cout << "--- while ---" << std::endl;
struct sockaddr_in client = { };
int new_sock = accept(serv_sock.sock,(struct sockaddr *) &client, &serv_sock.len);
//---------------------------------------------------------------------------------------------
if(new_sock < 0)
{
perror( "accept() ERROR" );
continue;
}
//---------------------------------------------------------------------------------------------
else
{
ClientSock *c_s = new ClientSock(new_sock);
(void)c_s; // disable - warning: unused variable...
}
//---------------------------------------------------------------------------------------------
}
}
//----------------------------------------------------------------------------------------------------
server_socket.hpp
#ifndef _CRAWS_SERVER_SOCKET_HPP_
#define _CRAWS_SERVER_SOCKET_HPP_
//----------------------------------------------------------------------------------------------------
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
//----------------------------------------------------------------------------------------------------
class ServerSock
{
public:
ServerSock(const std::string &ip_addr, const unsigned int &port, const unsigned int &max_conn);
~ServerSock();
int sock; // <--- Gniazdo/Socket
socklen_t len;
};
//----------------------------------------------------------------------------------------------------
#endif
//----------------------------------------------------------------------------------------------------
server_socket.cpp
#include "server_socket.hpp"
//----------------------------------------------------------------------------------------------------
ServerSock::ServerSock(const std::string &ip_addr, const unsigned int &port, const unsigned int &max_conn)
{
struct sockaddr_in serwer =
{
.sin_family = AF_INET,
.sin_port = htons(port)
};
if(inet_pton(AF_INET, ip_addr.c_str(), &serwer.sin_addr) <= 0)
{
std::cout << "err:server_socket:1:socket create" << std::endl;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(sock < 0)
{
std::cout << "err:server_socket:2:socket create" << std::endl;
}
len = sizeof(serwer);
if(bind(sock,(struct sockaddr *) &serwer, len) < 0)
{
std::cout << "err:server_socket:4:socket create" << std::endl;
}
if(listen(sock, max_conn) < 0)
{
std::cout << "err:server_socket:5:socket create" << std::endl;
}
}
//----------------------------------------------------------------------------------------------------
ServerSock::~ServerSock()
{
shutdown(sock, SHUT_RDWR);
close(sock);
}
//----------------------------------------------------------------------------------------------------
client_socket.hpp
#ifndef _CRAWS_CLIENT_SOCKET_HPP_
#define _CRAWS_CLIENT_SOCKET_HPP_
//----------------------------------------------------------------------------------------------------
#include <cstring>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
#include <iostream>
#include <sstream>
//----------------------------------------------------------------------------------------------------
class ClientSock
{
public:
ClientSock(const int &new_sock);
int fd;
std::string msg; // <--- Bufor msg z postfixa
int validate(const std::string &msg);
~ClientSock();
};
//----------------------------------------------------------------------------------------------------
#endif
//----------------------------------------------------------------------------------------------------
client_socket.cpp
#include "client_socket.hpp"
//----------------------------------------------------------------------------------------------------
/*
* Maksymalny rozmiar tablicy odczytu
*/
#define DEF_MAX_MSG_LEN 2048
//----------------------------------------------------------------------------------------------------
ClientSock::ClientSock(const int &new_sock)
{
fd = dup(new_sock); // <--- Skopiuj deskryptor plik/gniazdo
close(new_sock);
std::cout << "--- discriptor --- ---" << fd << std::endl;
std::thread th([this]()
{
/*
* Koniecznie zastosuj break gdy ERR
*/
while(true)
{
char buffer[DEF_MAX_MSG_LEN] = { };
if(recv(fd, buffer, sizeof(buffer)-1, 0) <= 0)
{
std::cout << "--- disconnected --- ---" << std::endl;
delete this;
break;
}
msg += buffer;
std::cout << buffer << std::endl;
/*
*
*
*/
if (msg.find("\n\n") != std::string::npos) // --- pobrano cały bufor socket
{
/*
* Opis zwracanego kodu - int ret = validate(msg);
*
* 0 = walidacja ok
*
*/
//int ret = validate(msg); // --- walidacja
//----------------------------------------------------------------------------
//const char* msgsnd = "action=dunno\n\n";
std::string msg_snd = "action=dunno\n\n";
send(fd, msg_snd.c_str(), msg_snd.size(), 0);
std::cout << msg << std::endl;
msg.clear(); // --- wyczyść zmienną msg globalna dla klasy
}
}
});
std::cout << "--- odłączenie wątku" << std::endl;
th.detach();
}
//----------------------------------------------------------------------------------------------------
int ClientSock::validate(const std::string &msg)
{
int ret = 0;
std::istringstream s_str(msg);
std::string line;
while (std::getline(s_str, line))
{
std::string token = line.substr(0, line.find("=")); // token is "scott"
std::cout << "1 " << token << std::endl;
}
return ret;
}
//----------------------------------------------------------------------------------------------------
ClientSock::~ClientSock()
{
shutdown(fd, SHUT_RDWR);
close(fd);
std::cout << "--- destruktor " << std::endl;
}
//----------------------------------------------------------------------------------------------------