I would like to create a multithreaded socket server. I have the server working fine, but when i try to move my code into a worker function the server stops working when reading the client data.
original code:
main.cpp
int sock;
main(){
SocketServer *ss = new SocketServer(8888);
pthread_t thread;
if(ss != NULL){
while(true){
sock = ss->Accept();
char* out;
ss->GetRequest(sock, out);
}
}
}
SocketServer.cpp
void SocketServer::GetRequest(int msgsock, char* out){
char buf[1024];
int rval;
std::cout<<"before read\n";
if ((rval = read(msgsock, buf, 1024)) < 0){
perror("reading socket");
}else{
strcpy(out,buf);
}
std::cout<<"after read\n";
}
After adding threads:
main.cpp
int sock;
main(){
SocketServer *ss = new SocketServer(8888);
pthread_t thread;
if(ss != NULL){
while(true){
sock = ss->Accept();
pthread_create(&thread, NULL, SocketThread, &(*ss));
pthread_detach(thread);
}
}
}
static void* SocketThread(void* lp){
SocketServer *ss = (SocketServer*) lp;
char* out;
ss->GetRequest(sock, out);
}
Original outputs:
before read
after read
New outputs:
before read
This is broken:
if ((rval = read(msgsock, buf, 1024)) < 0){
perror("reading socket");
}else{
strcpy(out,buf);
You are ignoring rval unless it signals an error. It should be:
if ((rval = read(msgsock, buf, 1024)) < 0){
perror("reading socket");
else if (rval == 0) {
// peer closed the connection
close(msgsock); // or closesocket(), depending on your platform
break;
}else{
strncpy(out,buf,rval);
and this is also broken:
sock = ss->Accept();
pthread_create(&thread, NULL, SocketThread, &(*ss));
The thread started to handle the client has no interest in the listening socket. What it needs is the accepted socket sock, and it needs to get it in such a way that it won't be overridden on the next call. Typically sock is a local variable in the accept loop and is passed via pthread_create().
Related
Hi everyone i have a little problem, i supposed to transfer a file from a server( a tcp server with threads to a client). The problems appers at the end of transmision the file is recived by client but it stucks and I can't longer communicate with it.
This is the server
int main(int argc, char *argv[])
{
int socket_desc, client_sock, c;
struct sockaddr_in server, client;
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(2025);
//Bind
if (bind(socket_desc, (struct sockaddr *) &server, sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc, 5);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
int enable = 1;
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
pthread_t thread_id;
while ((client_sock = accept(socket_desc,
(struct sockaddr *) &client,
(socklen_t*) &c)))
{
puts("Connection accepted");
if (setsockopt(client_sock,
SOL_SOCKET,
SO_REUSEADDR,
&enable,
sizeof(int)) < 0)
error("setsockopt(SO_REUSEADDR) failed");
if (pthread_create(&thread_id,
NULL,
connection_handler,
(void*) &client_sock) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
pthread_join(thread_id, NULL);
puts("Handler assigned");
}
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
return 0;
}
void *connection_handler(void *socket_desc)
{
printf("Enter in handler");
//Get the socket descriptor
int sock = *(int*) socket_desc;
send_problemo(sock);
return 0;
}
This is the sending function where I think is the real problem
int send_problemo(int *sock)
{
ssize_t read_return;
char *file_path = "Problems/1.txt";
char buffer[BUFSIZ];
int filefd;
filefd = open(file_path, O_RDONLY);
char end[2] = "1";
if (filefd == -1)
{
perror("open");
exit (EXIT_FAILURE);
}
while (1)
{
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
{
printf("este 0 \n");
break;
}
if (read_return == -1)
{
perror("read");
exit (EXIT_FAILURE);
}
if (write(sock, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
}
// close(sock);
close(filefd);
}
The client is connecting normally and receives the file in this function
int recive_problemo(int *sockfd)
{
char *file_path = "path.c";
char buffer[BUFSIZ];
ssize_t read_return;
int filefd;
filefd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (filefd == -1)
{
perror("open");
exit (EXIT_FAILURE);
}
do
{
read_return = read(sockfd, buffer, BUFSIZ);
if (read_return == -1)
{
perror("read");
exit (EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
} while (read_return > 0);
close(filefd);
}
I kind of managed how to solve this. If i shutdown(SHUT_WR) from server the client isnt stuck anymore, but i want to communicate with it further.
Also with the same function if i transfer from client to server, it works perfectly, so can anyone help me please?
do
{
read_return = read(sockfd, buffer, BUFSIZ);
// error handling
// file write
} while (read_return > 0);
Will keep looping until the socket closes or there's an error. It has no way to tell if a file has finished.
Common solutions are to close the socket (but you don't want that) and establish a communication protocol so that you know when the file is done and can exit the loop.
To keep things very simple, I recommend sending the length of the file before sending the file. The loop now looks something like:
uint64_t file_len;
read_return = recv(sockfd, &file_len, sizeof(file_len), MSG_WAITALL);
if (read_return == sizeof(file_len))
{
// Strongly consider handling the endian of file_len here
while (file_len)
{
size_t readmax = std::min(file_len, BUFSIZ);
read_return = read(sockfd, buffer, readmax);
if (read_return > 0)
{
if (write(filefd, buffer, read_return) == -1)
{
perror("write");
exit (EXIT_FAILURE);
}
file_len -= read_return;
}
else
{
// handle error
// exit loop if not recoverable
}
}
}
The server end picks up the responsibility of getting and sending the length of the file. I won't get into that because there are too many different ways to get the length of a file. Pick your favourite.
Documentation on recv and MSG_WAITALL.
I'm trying to start QLocalServer in my local OSX 10.11.
I have client which tries connection to server in loop:
int connect(const char* filename) {
int sock;
struct sockaddr_un serv_addr;
memset(&serv_addr, 0x00, sizeof(serv_addr));
serv_addr.sun_family = AF_LOCAL;
strncpy(serv_addr.sun_path, filename, sizeof(serv_addr.sun_path) - 1);
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
return sock;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
close(sock);
sock = -1;
return sock;
}
return sock;
}
int main() {
int sock;
while((sock = connect("my_socket_server")) == -1) {
usleep(3000);
}
// The code never reaches this line
const char* buffer = "hello";
if (send(sock, buffer, strlen(buffer), 0) < 0) {
exit(1);
}
return 0;
}
When this code is running, I try to start QLocalServer in another application:
// Starting server:
QString socket_path = "my_socket_server";
QLocalServer::removeServer(socket_path);
if (!server.listen(socket_path)) {
return false;
}
connect(&server, &QLocalServer::newConnection, this, &MyServerClass::newConnection);
...
void MyServerClass::newConnection() {
socket = server.nextPendingConnection(); // socket - member of MyServerClass
connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater);
connect(socket, &QLocalSocket::readyRead, this, &MyServerClass::readyRead);
}
...
void MyServerClass::readyRead() {
if (!socket->bytesAvailable()) {
exit(1); // THIS CODE WAS CALLED. WHY?
}
...
}
Why when readyRead was called, bytes are not available?
You might be getting multiple connections, in which case the socket variable might be pointing to a different object from the one which emits the readyRead signal. Either use QObject::sender to get the correct object in the slot, or use the QSignalMapper. If you are using Qt5, you can also use a lambda function and capture the socket object.
In a project I am currently doing in group, we have to build a card game from scratch that uses sockets (Linux). We also have to build a chat room that every player can use.
So far so good. The chat is implemented using three separate threads, one that receives incoming connections (up to 50) and stores them in a client list, one that constantly waits for messages from all connected clients, and one that is created each time a client sends a message, sending that message to all clients in the client list. All of this works, except when a single client disconnects.
I managed to keep the server alive (with a sig handler for SIGPIPE) when a client disconnects, but now, when a client disconnects, I keep getting the error Bad file descriptor. But that's not the only problem, since the server keeps receiving empty messages and sends them to the remaining clients, effectively flooding the whole chat in a matter of milliseconds with empty messages.
I believe that if I can fix the problem on the server side, there won't be any problems on the client side.
So my question is: What is the right way (or any way) to manage a Bad file descriptor in my case. I've already tried closing the socket FD and setting the value to -1 in the client list, but that created even more problems and didn't fix the initial ones.
Here is the code, if necessary. The most important function (for the chat) are reception_thread, chat_thread, receive_string, send_string and connect_to_chat on the client side.
Here is the client:
//includes
const int PORT = 2477;
const int CHAT_PORT = 2478;
#define DEBUG
//error()
// Sets up the connection to the server.
//connect_to_server()
int connect_to_chat(char * hostname)
{
#ifdef DEBUG
printf("[DEBUG] Initiating connection to chat server.\n");
#endif
struct sockaddr_in serv_addr;
struct hostent *server;
// Get a socket.
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("Error opening socket for server.");
// Get the address of the server.
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr, "ERROR, no such host\n");
exit(0);
}
// Zero out memory for server info.
memset(&serv_addr, 0, sizeof (serv_addr));
// Set up the server info.
serv_addr.sin_family = AF_INET;
memmove(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(CHAT_PORT);
// Make the connection.
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
error("Error connecting to chat server");
#ifdef DEBUG
printf("[DEBUG] Connected to server.\n");
#endif
return sockfd;
}
//-------------------------------- Messages ------------------------------------
// Bunch of send/recv functions that are not important to chat
int send_string(int sockfd, std::string myString)
{
#ifdef DEBUG
printf("[DEBUG] Sending string: %s.\n", myString.c_str());
#endif
//send size
uint32_t stringLen = myString.size();
uint32_t sendLen = htonl(stringLen);
int n = send(sockfd, &sendLen, sizeof (uint32_t), 0);
if (n < 0) {
error("Error sending message (string size). Removing client from list.");
return -1;
}
//send string
n = send(sockfd, myString.c_str(), stringLen, 0);
if (n < 0) {
error("Error sending message (string). Removing client from list.");
return -1;
}
return 0;
}
std::string receive_string(int sockfd)
{
//get string length
uint32_t stringLen;
int n = recv(sockfd, &stringLen, sizeof (uint32_t), 0);
if (n < 0) {
perror("Error receiving message(string size).");
}
stringLen = ntohl(stringLen);
std::vector<uint8_t> buffer;
buffer.resize(stringLen, 0x00);
//get string
n = recv(sockfd, &(buffer[0]), stringLen, 0);
if (n < 0) {
perror("Error receiving message(string).");
}
std::string returnString;
returnString.assign(reinterpret_cast<const char*> (&(buffer[0])), buffer.size()); //might be a bad idea, but it works
#ifdef DEBUG
printf("[DEBUG] Received message: %s\n", returnString.c_str());
#endif
return returnString;
}
//----------------------------- Printing functions------------------------------
void print_menu_guest()
{
// some visual function
}
void print_menu_user()
{
// some visual function
}
void print_info()
{
std::cout << " No information available on the game yet." << std::endl;
}
//---------------------------- Account functions -------------------------------
// Not necessary for chat functions
//--------------------------- Chat thread functions ----------------------------
void reception_thread(int sockfd)
{
#ifdef DEBUG
printf("[DEBUG] Reception thread started.\n");
#endif
std::string stringToPrint;
while (1) {
stringToPrint = receive_string(sockfd);
std::cout << stringToPrint << std::endl;
}
}
void chat_thread(int sockfd, char* host)
{
#ifdef DEBUG
printf("[DEBUG] Chat thread started.\n");
#endif
std::string myString, myUsername, blank;
std::cout << "Enter your username (NO SPACES): ";
std::cin >> myUsername;
myUsername += ": ";
int chat_sockfd = connect_to_chat(host);
std::thread reception_thr(reception_thread, chat_sockfd);
reception_thr.detach();
while (1) {
getline(std::cin, myString);
if (!myString.empty()) {
if (myString != "/quit") {
send_string(chat_sockfd, (myUsername + myString));
}
else {
printf("On peut pas encore quitter :( ");
}
}
}
}
//---------------------- Menu management functions -----------------------------
// Main menu function
//---------------------------- Main function -----------------------------------
int main(int argc, char** argv)
{
/* Make sure host and port are specified. */
if (true) {
char* hostname = "localhost";
/* Connect to the server. */
int sockfd = connect_to_server(hostname);
#ifdef DEBUG
printf("[DEBUG] Client ID: Not yet implemented. ");
#endif
login_prompt(sockfd);
user_menu_loop(sockfd);
}
return 0;
}
And here is the server: Its most important functions (for the chat) are setup_user_fetcher, message_receiver, send_string_to_all, receive_string, send_string, get_chat_user, setup_chat_listener.
// Bunch of includes
const int PORT = 2477;
const int CHAT_PORT = 2478;
const int BACKLOG = 10;
const int MAX_CLIENTS = 20;
int clients_list[50] = {-1};
#define DEBUG
void error(const char *msg)
{
perror(msg);
}
/* Catch Signal Handler functio */
void signal_callback_handler(int signum){
printf("Caught signal SIGPIPE %d\n",signum);
}
//-------------------------- Server set-up functions ---------------------------
// Not necessary for chat
//--------------------------- Chat server functions ---------------------------
int setup_chat_listener()
{
int sockfd;
struct sockaddr_in serv_addr;
// Get a socket to listen on
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening listener socket.");
// Zero out the memory for the server information
memset(&serv_addr, 0, sizeof (serv_addr));
// set up the server info
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(CHAT_PORT);
// Bind the server info to the listener socket.
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0)
error("Error binding listener socket.");
#ifdef DEBUG
printf("[DEBUG] Chat listener set.\n");
#endif
// Return the socket number.
return sockfd;
}
int get_chat_user(int sockfd)
{
#ifdef DEBUG
printf("[DEBUG] Getting chat user.\n");
#endif
struct sockaddr_in their_addr;
socklen_t sin_size;
if (listen(sockfd, BACKLOG) < 0) {
perror("Error while listening.");
exit(EXIT_FAILURE);
}
sin_size = sizeof (struct sockaddr_in);
// Mise a zero de la memoire pour le client.
memset(&their_addr, 0, sin_size);
int new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size);
if (new_fd < 0)
error("Error while accepting.");
printf("Chat server: Connection received from: %s\n",
inet_ntoa(their_addr.sin_addr));
return new_fd;
}
int send_string(int sockfd, std::string myString)
{
#ifdef DEBUG
printf("[DEBUG] Sending string to client %d.\n", sockfd);
#endif
uint32_t stringLen = myString.size();
uint32_t sendLen = htonl(stringLen);
int n = send(sockfd, &sendLen, sizeof (uint32_t), 0);
if (n < 0) {
error("Error sending message (string size). Removing client from list.");
return -1;
}
//send string
n = send(sockfd, myString.c_str(), stringLen, 0);
if (n < 0) {
error("Error sending message (string). Removing client from list.");
return -1;
}
return 0;
}
std::string receive_string(int sockfd)
{
#ifdef DEBUG
printf("[DEBUG] Receiving string.\n");
printf("Current chat user sockfd: %d\n", sockfd);
#endif
uint32_t stringLen;
int n = recv(sockfd, &stringLen, sizeof (uint32_t), 0);
#ifdef DEBUG
printf("[DEBUG] String size received: %d.\n", stringLen);
#endif
if (n < 0) {
perror("Error receiving message(string size).");
}
stringLen = ntohl(stringLen);
std::vector<uint8_t> buffer;
buffer.resize(stringLen, 0x00);
//get string
n = recv(sockfd, &(buffer[0]), stringLen, 0);
if (n < 0) {
perror("Error receiving message(string).");
close(sockfd);
}
std::string returnString;
returnString.assign(reinterpret_cast<const char*> (&(buffer[0])), buffer.size()); //might be a bad idea, but it works
#ifdef DEBUG
printf("[DEBUG] Received message: %s\n", returnString.c_str());
#endif
return returnString;
}
void send_string_to_all(std::string myString)
{
#ifdef DEBUG
printf("[DEBUG] Sending string to all clients.\n");
#endif
int n;
for (int i = 0; i < 50; ++i) {
if (clients_list[i] != -1) {
n = send_string(clients_list[i], myString);
if (n < 0) {
close(clients_list[i]);
clients_list[i] = -1;
}
}
}
}
void message_receiver(int sockfd)
{
#ifdef DEBUG
printf("[DEBUG] Setting up message receiver.\n");
printf("Current chat user sockfd: %d", sockfd);
#endif
std::string message;
int n;
while (1) {
message = receive_string(sockfd);
std::thread t1(send_string_to_all, message);
t1.detach();
}
}
//------------------------------------------------------------------------------
// Bunch of send/recv functions, not necessary to chat
//----------------------------Account Functions---------------------------------
// Not necessary to chat
//------------------------------------------------------------------------------
// Main menu function
void setup_user_fetcher(int lis_chat_sockfd)
{
#ifdef DEBUG
printf("[DEBUG] Gotta catch'em all.\n");
#endif
while (1) {
int chat_user_sockfd = get_chat_user(lis_chat_sockfd);
for (int i = 0; i < 50; ++i)
if (clients_list[i] == -1) {
clients_list[i] = chat_user_sockfd;
break;
}
std::thread message_receiver_thread(message_receiver, chat_user_sockfd);
message_receiver_thread.detach();
}
}
int main(int argc, char** argv)
{
signal(SIGPIPE, signal_callback_handler);
int lis_sockfd = setup_listener();
int lis_chat_sockfd = setup_chat_listener();
std::thread chat_thread(setup_user_fetcher, lis_chat_sockfd);
chat_thread.detach();
while (1) {
int user_sockfd = get_user(lis_sockfd);
int* user_sockfd_ptr = (int*) malloc(sizeof (int));
memset(user_sockfd_ptr, 0, sizeof (int));
user_sockfd_ptr[0] = user_sockfd;
#ifdef DEBUG
printf("[DEBUG] Starting main menu...\n");
#endif
pthread_t thread;
int result = pthread_create(&thread, NULL, main_menu,
(void *) user_sockfd_ptr);
if (result) {
printf("Thread creation failed with return code %d\n", result);
exit(-1);
}
#ifdef DEBUG
printf("[DEBUG] New main menu thread started.\n");
#endif
}
close(lis_sockfd);
pthread_exit(NULL);
return 0;
}
If you wish to reproduce the error, you could compile the code using the following lines
g++ client.cpp -o client -std=c++14 -pthread
g++ server.cpp -o server -std=c++14 -pthread
and run both without any arguments. The client is set to connect on "localhost".
I would be really glad if anyone could help me out with this.
I recomment getting rid of the SIGPIPE signal itself.
signal(SIGPIPE, SIG_IGN);
Now, write()s on killed sockets will simply return -1. It should be easier to deal with that, instead of an asynchronous signal.
If you need SIGPIPE for other reasons, replace write()s with sendto()s with the MSG_NOSIGNAL option. See the sendto(2) manual page for more information.
You have UB. &(buffer[0]) will fail if the number of bytes read is 0 (which I believe will happen if client disconnects). You should test for 0 and return early before building your string.
Also you do not return after finding errors so you build your string from bad data in case of errors.
Maybe something more like:
std::string receive_string(int sockfd)
{
uint32_t stringLen;
int n = recv(sockfd, &stringLen, sizeof (uint32_t), 0);
if (n < 0) {
close(sockfd);
// exit early
throw std::runtime_error("Error receiving message(string size): "
+ std::string(std::strerror(errno)));
}
// test for zero
if(!n)
return {}; // empty string
stringLen = ntohl(stringLen);
std::vector<uint8_t> buffer(stringLen);
// buffer.resize(stringLen, 0x00);
//get string
n = recv(sockfd, &(buffer[0]), stringLen, 0);
if (n < 0) {
close(sockfd);
// exit early
throw std::runtime_error("Error receiving message(string): "
+ std::string(std::strerror(errno)));
}
// only build string if no errors
return {buffer.begin(), buffer.begin() + n};
}
I am working on a application which works by received commands (API calls) over a socket connection. The application has two socket server with two different port numbers. As said the application is acting on the received commands (string). What is the best way to setup the socket server(s)? The socket server object is been build in a class file and contains two threads (listen for connections, handling the new connected client). The question is, what will be the best way to use the received commands/data so that the application will respond to it. Should I buildin a buffer (vector of string) to store the received data, read this buffer in the main loop of the application and after the reading clear the vector? Or is there a better/ other way?
The code of the socket server class is:
void SocketServer::Startup()
{
WSADATA wsa;
WSAStartup(0x0202, &wsa);
this->WorkerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
this->EndPnt.sin_addr.s_addr = INADDR_ANY;
this->EndPnt.sin_family = AF_INET;
this->EndPnt.sin_port = htons(this->m_port);
this->Enabled = true;
this->ID = 0;
bind(this->WorkerSocket, (SOCKADDR*)&this->EndPnt, sizeof(this->EndPnt));
printf("[SocketServer]Bound on %d..\n", this->m_port);
listen(this->WorkerSocket, this->Backlog);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerMainThread, this, NULL, NULL);
}
void SocketServer::WaitForConnections(SocketServer * Ser)
{
while(Ser->Enabled)
{
SOCKET accptsock = accept(Ser->WorkerSocket, NULL, NULL);
printf("[SocketServer]Client connected.\n");
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ServerDataThread, (LPVOID)accptsock, NULL, NULL);
}
}
void SocketServer::WaitForData(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
char *message = "ECHO Daemon v1.0 \r\n";
send(sock, message, strlen(message), 0);
int res;
char buffer[255];
while(true)
{
res = recv(sock, buffer, sizeof(buffer), 0);
if( res == SOCKET_ERROR)
{
int error_code = WSAGetLastError();
if(error_code == WSAECONNRESET)
{
//Somebody disconnected , get his details and print
cout << "[SocketServer]Client disconnected" << endl;
//Close the socket and mark as 0 in list for reuse
closesocket(sock);
ExitThread(0);
}
else
{
closesocket(sock);
ExitThread(0);
}
}
if(res == 0)
{
closesocket(sock);
ExitThread(0);
}
else
{
cout << buffer << endl;
// insert data to vector here
send(sock , buffer , sizeof(buffer) , 0 );
}
}
}
DWORD WINAPI SocketServer::ServerMainThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForConnections((SocketServer*)lParam);
return 0;
}
DWORD WINAPI SocketServer::ServerDataThread(LPVOID lParam)
{
((SocketServer*)lParam)->WaitForData((LPVOID)lParam);
return 0;
}
The vector it needs to push the data to is called "Buffer", "Buffer.push_back(buffer)";
I'm trying to understand this Libevent c++ code I got from this page.
I'm a bit confused - am I correct to think that this code might have memory leaks?
It seems like ConnectionData pointer is created in on_connect() callback, but delete() is only called on bad read or after write is complete.
What if connection was accept()ed - but there were no reads or writes? so is that pointer just stays in daemon memory?
#include <event.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
// Read/write buffer max length
static const size_t MAX_BUF = 512;
typedef struct {
struct event ev;
char buf[MAX_BUF];
size_t offset;
size_t size;
} ConnectionData;
void on_connect(int fd, short event, void *arg);
void client_read(int fd, short event, void *arg);
void client_write(int fd, short event, void *arg);
int main(int argc, char **argv)
{
// Check arguments
if (argc < 3) {
std::cout << "Run with options: <ip address> <port>" << std::endl;
return 1;
}
// Create server socket
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock == -1) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
sockaddr_in sa;
int on = 1;
char * ip_addr = argv[1];
short port = atoi(argv[2]);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(ip_addr);
// Set option SO_REUSEADDR to reuse same host:port in a short time
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
std::cerr << "Failed to set option SO_REUSEADDR" << std::endl;
return 1;
}
// Bind server socket to ip:port
if (bind(server_sock, reinterpret_cast<const sockaddr*>(&sa), sizeof(sa)) == -1) {
std::cerr << "Failed to bind server socket" << std::endl;
return 1;
}
// Make server to listen
if (listen(server_sock, 10) == -1) {
std::cerr << "Failed to make server listen" << std::endl;
return 1;
}
// Init events
struct event evserver_sock;
// Initialize
event_init();
// Set connection callback (on_connect()) to read event on server socket
event_set(&evserver_sock, server_sock, EV_READ, on_connect, &evserver_sock);
// Add server event without timeout
event_add(&evserver_sock, NULL);
// Dispatch events
event_dispatch();
return 0;
}
// Handle new connection {{{
void on_connect(int fd, short event, void *arg)
{
sockaddr_in client_addr;
socklen_t len = 0;
// Accept incoming connection
int sock = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &len);
if (sock < 1) {
return;
}
// Set read callback to client socket
ConnectionData * data = new ConnectionData;
event_set(&data->ev, sock, EV_READ, client_read, data);
// Reschedule server event
event_add(reinterpret_cast<struct event*>(arg), NULL);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client request {{{
void client_read(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
int len = read(fd, data->buf, MAX_BUF - 1);
if (len < 1) {
close(fd);
delete data;
return;
}
data->buf[len] = 0;
data->size = len;
data->offset = 0;
// Set write callback to client socket
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client responce {{{
void client_write(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
// Send data to client
int len = write(fd, data->buf + data->offset, data->size - data->offset);
if (len < data->size - data->offset) {
// Failed to send rest data, need to reschedule
data->offset += len;
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
close(fd);
delete data;
}
//}}}
The documentation for event_set says that the only valid event types are EV_READ or EV_WRITE, but the callback will be invoked with EV_TIMEOUT, EV_SIGNAL, EV_READ, or EV_WRITE. The documentation is not clear, but I expect the read callback will be invoked when the socket is closed by the client. I expect the delete in the failure branch in client_read will handle this situation.
Note that that is only the case if the client sends a FIN or RST packet. A client could establish a connection and leave it open forever. For this reason, this code should be modified to have a timeout (perhaps via event_once) and require the client send a message within that timeout.