I'm trying to find a solution to a question I posted earlier about synchronizing chat messages, and one member pointed me in the direction of the select() function. I read this section under Beej's Network Guide and tried to write the sample given under windows. It compiles fine but when I go to test it, The program crashes and displays my error message "-Select error"- after inputting the port number into the program. I'm uncertain of how to get this working, please advise.
server.cpp
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
using namespace std;
const int winsockver = 2;
int PORT;
int fdmax;
char buffer[80];
int main(void){
//*********************************************
fd_set master;
fd_set temp;
SOCKET newfd;
struct sockaddr_in connected_client;
//*********************************************
WSADATA wsadata;
if (WSAStartup(MAKEWORD(winsockver,0),&wsadata) == 0 ){
cout<<"-Winsock Initialized." << endl;
cout<<"Enter PORT:";
cin>> PORT;
//--------------------------------------------------------------------
struct sockaddr_in server_info;
server_info.sin_family = AF_INET;
server_info.sin_port = htons(PORT);
server_info.sin_addr.s_addr = INADDR_ANY;
SOCKET serv_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if ( serv_sock != INVALID_SOCKET){
if ( bind(serv_sock,(sockaddr*)&server_info,sizeof(server_info)) != -1 ){
char yes = '1';
if ( setsockopt(serv_sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) != SOCKET_ERROR){
cout<<"-Binding Successful." << endl;
}
}
if ( listen(serv_sock,5) != -1 ){
cout<<"-Listening for incoming connections." << endl;
}
FD_SET(serv_sock,&master);
fdmax = serv_sock; // keeping track of the largers sockfd, at this moment its serv_sock
//#########################################################
for(;;){
temp = master; // copying to temp the master set
if (select(fdmax+1,&temp,NULL,NULL,NULL) == -1 ){
cout<<"-select error." << endl;
}
//run through existing connections looking for data to read
for (int i = 0; i <= fdmax; i++){
if (FD_ISSET(i,&temp)){//we have one
if (i == serv_sock){//handle new connections
int size =sizeof(connected_client);
newfd = accept(serv_sock,(sockaddr*)&connected_client,&size);
if ( newfd == -1 ){
cout<<"-Accepted an invalid socket from newfd." << endl;
}else{//accept has returned a valid socket descriptor and we add it to the master
FD_SET(newfd,&master);
if (newfd > fdmax ){
fdmax = newfd;
}
char *connected_ip= inet_ntoa(connected_client.sin_addr);
cout<<"-Select server new connection from " << connected_ip << " " << endl;
}
}else{
//handle data from a client
int bytes_in;
bytes_in = recv(i,buffer,sizeof(buffer),0);
if ( bytes_in <= 0 ){
if (bytes_in == 0 ){
cout<<"-Connected socket " << i << ",disconnected " << endl;
}else{
cout<<"-Socket error." << endl;
}
closesocket(i);
FD_CLR(i,&master); //remove from master set.
}else{
//we get data from a client
for (int j=0; j <= fdmax; j++ ){
//send to everyone
if (FD_ISSET(j,&master)){
//except the listener and ourself
if ( (j != serv_sock) && (j != i) ){
if ( send(j,buffer,sizeof(buffer),0) == -1 ){
cout<<"-Sending error" << endl;
}
}
}
}
}
}
}
}
}
//#########################################################
}
//--------------------------------------------------------------------
}else{
cout<<"-Unable to Initialize." << endl;
}
if ( WSACleanup() != -1 ){
cout<<"-Cleanup Successful." << endl;
}
return 0;
}
Your file descriptor sets are not properly initialized, so they still contain garbage when you call FD_SET() on them.
You should call FD_ZERO() to initialize them before you start using them:
FD_ZERO(&master);
FD_SET(serv_sock, &master);
fdmax = serv_sock;
Related
I'm trying to create an app which would accept many connections from clients at the same time and it works for me, but it also should download those files at the same time. In this version of server, even if clients are connected simultaneously, files are written one by one.
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
int const max_clients = 100;
int client_socket[max_clients];
for (int i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
int server_sizeof = sizeof(server);
int opt = TRUE;
if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(bind(server_socket,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(listen(server_socket, 5) == SOCKET_ERROR)
{
std::cout << "Nasluchiwanie portu nieudane." << std::endl;
}
else
{
std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
}
int const buffer_size = 512;
char buffer[buffer_size];
int max_socket_descriptor, socket_descriptor;
int downloaded_files = 1;
fd_set readfds;
while(true)
{
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
max_socket_descriptor = server_socket;
for (int i = 0 ; i < max_clients ; i++)
{
socket_descriptor = client_socket[i];
if(socket_descriptor > 0)
{
FD_SET( socket_descriptor, &readfds);
}
if(socket_descriptor > max_socket_descriptor)
{
max_socket_descriptor = socket_descriptor;
}
}
if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
{
std::cout << "Blad funkcji select." << std::endl;
}
if (FD_ISSET(server_socket, &readfds))
{
int new_sockfd;
if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
{
std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
}
else
{
for (int i = 0; i < max_clients; i++)
{
if( client_socket[i] == 0 )
{
client_socket[i] = new_sockfd;
std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;
break;
}
}
}
}
for (int i = 0; i < max_clients; i++)
{
socket_descriptor = client_socket[i];
if (FD_ISSET( socket_descriptor, &readfds))
{
struct sockaddr_in client_address;
char filename[buffer_size];
std::stringstream ip_filename;
ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
strcpy(filename, ip_filename.str().c_str());
std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;
FILE* file;
file = fopen(filename, "wb");
const clock_t begin_time = clock();
int received_size;
do
{
memset(buffer, 0, buffer_size);
received_size = recv(socket_descriptor, buffer, buffer_size, 0);
if (received_size == 0 || received_size == -1)
{
break;
}
fwrite(buffer, sizeof(char), received_size, file);
}
while (received_size != 0);
fclose(file);
std::cout << "Czas wysylania pliku: " << float( clock () - begin_time ) / CLOCKS_PER_SEC << " sekund." << std::endl << std::endl;
closesocket(socket_descriptor);
client_socket[i] = 0;
downloaded_files++;
}
}
}
closesocket(server_socket);
WSACleanup();
system("pause");
return 0;
}
What should I do to make them write many at the same time? I've tried many modifications of the code above but every time I can't get wanted result.
For example:
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <ctime>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(winsock_result != 0)
{
exit(1);
}
SOCKET server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server_socket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
int const max_clients = 100;
int client_socket[max_clients];
for (int i = 0; i < max_clients; i++)
{
client_socket[i] = 0;
}
char* ip_address = "127.0.0.1";
int port = 6666;
SOCKADDR_IN server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_address);
int server_sizeof = sizeof(server);
int opt = TRUE;
if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(bind(server_socket,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
{
closesocket(server_socket);
WSACleanup();
exit(1);
}
if(listen(server_socket, 5) == SOCKET_ERROR)
{
std::cout << "Nasluchiwanie portu nieudane." << std::endl;
}
else
{
std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
}
int const buffer_size = 512;
char buffer[buffer_size];
int max_socket_descriptor;
int downloaded_files = 1;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
max_socket_descriptor = server_socket;
while(true)
{
if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
{
std::cout << "Blad funkcji select." << std::endl;
}
for (int i = 0 ; i < max_clients ; i++)
{
if(FD_ISSET(server_socket, &readfds))
{
int new_sockfd;
if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
{
std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
}
else
{
for (int i = 0; i < max_clients; i++)
{
if( client_socket[i] == 0 )
{
client_socket[i] = new_sockfd;
FD_SET( client_socket[i], &readfds);
if(client_socket[i] > max_socket_descriptor)
{
max_socket_descriptor = client_socket[i];
}
std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;
break;
}
}
}
}
if(FD_ISSET(client_socket[i], &readfds))
{
struct sockaddr_in client_address;
char filename[buffer_size];
std::stringstream ip_filename;
ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
strcpy(filename, ip_filename.str().c_str());
std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;
FILE* file;
memset(buffer, 0, buffer_size);
int received_size;
received_size = recv(client_socket[i], buffer, buffer_size, 0);
if (received_size <= 0)
{
closesocket(client_socket[i]);
FD_CLR(client_socket[i], &readfds);
client_socket[i] = 0;
break;
}
else
{
file = fopen(filename, "ab");
fwrite(buffer, sizeof(char), received_size, file);
fclose(file);
}
downloaded_files++;
}
}
}
closesocket(server_socket);
WSACleanup();
system("pause");
return 0;
}
I thought about opening and closing those files every received packet and appending every packet to them, but I really don't have idea how to do it. The example of modified code was meant to do it, but it doesn't work.
I'm forbidden to use other processes and threads than the main one, so I'm kinda helpless now. Thanks for your help.
You have the basic loop with select in place, which is good.
accept is already (mostly) non-blocking. You just need to turn on non-blocking mode on the client sockets and then you'll be able to handle multiple client reads, writes and accepts in your main select loop.
You can have a vector of client-specific data per client, with each entry containing the client socket, the opened file and any other client-specific state.
After the accept, you create a new client entry and add it to the vector. Then in the main loop you do FD_SET for accept and all client's reads and writes. After the select, you inspect the the FD sets and handle them one by one. For best performance you will want your file I/O also in non-blocking mode, but for this assignment that's probably overkill.
The system I have to do has one tcp server and about 1000 tcp clients.
1000 clients will send data to tcp server every second.
To simulate this situation, At first I connected to tcp server with 50 sockets from a single pc with below code.
int main() {
const char *hello = "Hello from client";
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.39");
vector<int> vec;
for ( uint8_t i = 0; i < 50; i++ ) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if ( sock < 0 ) {
cout << "... Cant Allocated Socket\n";
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
cout << "... Connection Failed \n";
return -1;
}
vec.push_back(sock);
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
send(vec[i], hello, strlen(hello), 0);
cout << "Message Send\n";
}
for ( uint8_t i = 0; i < vec.size(); i++ ) {
shutdown(vec[i], 0);
close(vec[i]);
}
return 0;
}
After the tcp clients connect to the tcp server, they send the data to the tcp server and close the socket. I can see from terminal that tcp clients can send packet without waiting(less than 10ms)
The above tcp client code can work successfully and send the data to tcp server successfully.
I show the data from the tcp client with the tcp server code below.
#define _DEF_TCP_SERVER_PORT 8080
#define _DEF_TCP_SERVER_MAX_QUEUE_LISTEN 12
bool finish_app = false;
struct TcpClient {
int clientSocket;
struct in_addr clientAddr;
};
vector<TcpClient> TcpClients;
struct _ServiceTcpServer {
bool enable;
int sock;
uint16_t connectedClient;
uint32_t sockLen;
sockaddr_in tcpServerAddr;
sockaddr_in remoteAddr;
};
struct _ServiceTcpServer _serviceTcpServer;
void init_tcp_server_socket() {
_serviceTcpServer.tcpServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
_serviceTcpServer.tcpServerAddr.sin_family = AF_INET;
_serviceTcpServer.tcpServerAddr.sin_port = htons(_DEF_TCP_SERVER_PORT);
_serviceTcpServer.sockLen = sizeof(_serviceTcpServer.remoteAddr);
int flag = 1;
for ( ;; ) {
_serviceTcpServer.sock = socket(AF_INET, SOCK_STREAM, 0);
if ( _serviceTcpServer.sock < 0 ) {
cout << "... Failed to allocate socket.\n";
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( setsockopt(_serviceTcpServer.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) ) {
cout << "... Set SockOpt failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if( bind(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.tcpServerAddr, sizeof(_serviceTcpServer.tcpServerAddr)) == -1 ) {
cout << "... Socket bind failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
if ( listen(_serviceTcpServer.sock, _DEF_TCP_SERVER_MAX_QUEUE_LISTEN) != 0 ) {
cout << "... Socket listen failed.\n";
close(_serviceTcpServer.sock);
this_thread::sleep_for(chrono::seconds(1));
continue;
}
break;
}
cout << "Socket init done \n";
}
void tcp_user_accept_task() {
while ( finish_app == false ) {
int temp_sck = -1;
temp_sck = accept(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.remoteAddr, &_serviceTcpServer.sockLen);
if ( temp_sck == -1 ) {
this_thread::sleep_for(chrono::seconds(2));
continue;
}
TcpClient tcpClient;
tcpClient.clientAddr = _serviceTcpServer.remoteAddr.sin_addr;
tcpClient.clientSocket = temp_sck;
TcpClients.push_back( tcpClient );
cout << "... New connection request: " << temp_sck << endl;
++_serviceTcpServer.connectedClient;
this_thread::sleep_for(chrono::milliseconds(50));
}
}
uint8_t temp_recv[100];
void tcp_server_run() {
while ( finish_app == false ) {
for(uint16_t i = 0 ; i < _serviceTcpServer.connectedClient; i++ ) {
int temp_cs = TcpClients[i].clientSocket;
fcntl(temp_cs, F_SETFL, O_NONBLOCK);
int temp_recvLen = recv(temp_cs, temp_recv, 20, 0);
if( temp_recvLen > 0 ) {
time_t _time = chrono::system_clock::to_time_t(chrono::system_clock::now());
cout << "Message Received At:" << ctime(&_time) << " :";
cout << temp_recv << endl;
break;
} else {
this_thread::sleep_for(chrono::milliseconds(10));
}
}
if ( temp_recv[0] == 'q' ) {
finish_app = true;
}
}
close(_serviceTcpServer.sock);
}
int main() {
thread init_thread(init_tcp_server_socket);
init_thread.join();
thread accept_thread(tcp_user_accept_task);
thread run_thread(tcp_server_run);
accept_thread.join();
run_thread.join();
return 0;
}
But the problem is about 3-4 packets received in only 1 second as in the screen image.
Note:
When the code this_thread::sleep_for(chrono::milliseconds(10)); commented, the problem was solved. But since the processor is not sleep, the processor is working at 100%.
When the client is accepted, I added 10 us timeout to client recv with the code below and comment and fcntl(temp_cs, F_SETFL, O_NONBLOCK);
struct timeval _timeval;
_timeval.tv_sec = 0;
_timeval.tv_usec = 10;
setsockopt(tcpClient.clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &_timeval, sizeof(_timeval));
The problem continues as in "this_thread::sleep_for".
You should receive the socket simultaneously rather than querying every socket and sleeping for 10ms each time data is not yet ready.
The proper way to do it depends on the platform
posix - select
linux - poll, epoll, io_submit
windows - I/O Completion Ports
Usually, select which is a posix standard, will be sufficient for your needs.
If you want multiplatform you might also want to explorer 3rd party libraries such as libevent and libev which already wraps theses platform depent calls for you.
Happy Coding!
I have been trying to understand why on my server the accept() call is still blocking when the client has a successful connect() call?
server.cpp
#include <errno.h>
#include <strings.h>
#include <iostream>
#include "globals.h"
using namespace std;
/* Declaring errno */
extern int errno;
/* Function for printing error */
void report_error(char *s)
{
printf("receiver: error in%s, errno = %d\n", s, errno);
exit(1);
}
int main(int argc, char *argv[])
{
int s,c;
int res;
struct sockaddr_in socket_address = {0}, client_sa = {0};
unsigned int client_sa_len = sizeof(client_sa);
/* Creating the socket and returns error if unsuccesfull */
if((s= socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)) == -1)
report_error("socket");
socket_address.sin_family = AF_INET;
socket_address.sin_addr.s_addr=INADDR_ANY;
socket_address.sin_port = htons(5318 + 2000);
/* Binding the socket and returns error if unsuccesfull */
if(bind(s, (struct sockaddr *)&socket_address, sizeof(socket_address))== -1)
report_error("bind");
listen(s, 10);
cout << "listening on port\n";
while(1)
{
/*The server just hangs here*/
c = accept(s, (struct sockaddr*)&client_sa, &client_sa_len);
if (c > 0)
{
cout << "LOG: Was the accept successful" << endl;
res = fork();
if (res < 0)
{
perror("Forking of child failed");
}
}
if(res == 0)
{
//close(s);
char msg[MSGL], reply[50], args[MSGL];
char command[MSGL];
cout << "LOG: Get message?" << endl;
GetRequest(msg, c, &client_sa);
if( (msg[0] == 'c') && (msg[1] == 'd') && (msg[2] == ' '))
{
strncpy(command, "cd", sizeof(command));
int arg_i = 0;
for(int i = 3; msg[i] != '\n'; ++i)
{
args[arg_i] = msg[i];
++arg_i;
}
}
else
{
for(int i = 0; msg[i] != '\n'; ++i)
{
command[i] = msg[i];
}
}
else
{
if(c > 0)
{
//close(c);
}
}
}
return 0;
}
When I run this server it prints that it is listening, then when I initialize the client it does not say that the client has connected
client.cpp
#include <errno.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include "globals.h"
using namespace std;
/* Declaring errno */
extern int errno;
int main(int argc, char *argv[])
{
int s;
char* server_address = argv[1];
char command[MSGL];
char reply[MSGL];
int connect_success;
struct sockaddr_in sa = {0} ,cli_sa = {0};
int length = sizeof(sa);
struct hostent *hp;
cli_sa.sin_family = AF_INET;
cli_sa.sin_addr.s_addr=INADDR_ANY;
cli_sa.sin_port = htons(5318 + 2001);
/* FILL SOCKET ADDRESS*/
if((hp = gethostbyname(server_address))==NULL)
report_error("gethostbyname");
bcopy((char*)hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
sa.sin_family = hp->h_addrtype;
//memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
sa.sin_port = htons(5318 + 2000); /* define port number based on student ID*/
/* Creating the socket and returns error if unsuccessfull */
if((s=socket(AF_INET, SOCK_DGRAM, PF_UNSPEC))== -1)
report_error("socket");
/* Binding the socket and returns error if unsuccesfull */
if(bind(s, (struct sockaddr *)&cli_sa, sizeof(cli_sa))== -1)
report_error("bind");
connect_success = connect(s,(struct sockaddr*) &sa, length);
cout << connect_success << endl;
if (connect_success < 0)
{
report_error("connect");
cout << "LOG: is there an error?" << endl;
}
cout << "LOG: is the connection made?" << endl;
while(1)
{
cout << "myRPC>>";
fgets(command,MSGL,stdin);
if (DoOperation(command,reply,s,sa) == SEND_FAILURE)
{
cout << "Error: sending command\n";
}
}
return 0;
}
I'm fairly certain that your server process has undefined behavior.
accept() and connect() is for TCP sockets. You are creating UDP sockets. For UDP sockets, all that connect() does is set the default address for send(), and it always succeeds immediately. This is explained in the manual page for connect(2), which you should definitely read:
If the socket sockfd is of type SOCK_DGRAM, then addr is the address
to which datagrams are sent by default, and the only address from
which datagrams are received.
I expect accept() to fail for UDP (SOCK_DGRAM) sockets, most likely with EINVAL. If you review the logic in your server code, when accept() fails, res never gets initialized before its value is tested in the if() statement.
Undefined behavior.
I want to make a server and client program with TCP protocol using C++. The server must be able to handle multiple client at once. But the problem is for example, after starting the server, I run 2 clients with the server 's IP address and port as parameters. Next, both clients are sending data to server. At first, both clients could send data to server and the server was able read the data. But, once the server has received data from the second client, it seems that it stopped receiving from the first client. Do you have any solution?
Here is the server code
using namespace std;
void *task1(void *);
static int connFd;
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char* argv[])
{
int pId, portNo, listenFd;
socklen_t len; //store size of the address
bool loop = false;
struct sockaddr_in svrAdd, clntAdd;
pthread_t threadA[3];
if (argc < 2)
{
cerr << "Syntam : ./server <port>" << endl;
return 0;
}
portNo = atoi(argv[1]);
if((portNo > 65535) || (portNo < 2000))
{
cerr << "Please enter a port number between 2000 - 65535" << endl;
return 0;
}
//create socket
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenFd < 0)
{
cerr << "Cannot open socket" << endl;
return 0;
}
bzero((char*) &svrAdd, sizeof(svrAdd));
svrAdd.sin_family = AF_INET;
svrAdd.sin_addr.s_addr = INADDR_ANY;
svrAdd.sin_port = htons(portNo);
//bind socket
if(bind(listenFd, (struct sockaddr *)&svrAdd, sizeof(svrAdd)) < 0)
{
cerr << "Cannot bind" << endl;
return 0;
}
listen(listenFd, 5);
int noThread = 0;
while (noThread < 3)
{
socklen_t len = sizeof(clntAdd);
cout << "Listening" << endl;
//this is where client connects. svr will hang in this mode until client conn
connFd = accept(listenFd, (struct sockaddr *)&clntAdd, &len);
if (connFd < 0)
{
cerr << "Cannot accept connection" << endl;
return 0;
}
else
{
cout << "Connection successful" << endl;
}
pthread_create(&threadA[noThread], NULL, task1, NULL);
noThread++;
}
for(int i = 0; i < 3; i++)
{
pthread_join(threadA[i], NULL);
}
}
void *task1 (void *dummyPt)
{
cout << "Thread No: " << pthread_self() << endl;
char test[256];
bzero(test, 256);
bool loop = false;
while(!loop)
{
bzero(test, 256);
int n = read(connFd, test, 255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",test);
}
cout << "\nClosing thread and conn" << endl;
close(connFd);
}
And the client code
using namespace std;
int main (int argc, char* argv[])
{
int listenFd, portNo;
bool loop = false;
struct sockaddr_in svrAdd;
struct hostent *server;
if(argc < 3)
{
cerr<<"Syntax : ./client <host name> <port>"<<endl;
return 0;
}
portNo = atoi(argv[2]);
if((portNo > 65535) || (portNo < 2000))
{
cerr<<"Please enter port number between 2000 - 65535"<<endl;
return 0;
}
//create client skt
listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listenFd < 0)
{
cerr << "Cannot open socket" << endl;
return 0;
}
server = gethostbyname(argv[1]);
if(server == NULL)
{
cerr << "Host does not exist" << endl;
return 0;
}
bzero((char *) &svrAdd, sizeof(svrAdd));
svrAdd.sin_family = AF_INET;
bcopy((char *) server -> h_addr, (char *) &svrAdd.sin_addr.s_addr, server -> h_length);
svrAdd.sin_port = htons(portNo);
int checker = connect(listenFd,(struct sockaddr *) &svrAdd, sizeof(svrAdd));
if (checker < 0)
{
cerr << "Cannot connect!" << endl;
return 0;
}
//send stuff to server
for(;;)
{
char s[300];
//cin.clear();
//cin.ignore(256, '\n');
cout << "Enter stuff: ";
bzero(s, 300);
cin.getline(s, 300);
write(listenFd, s, strlen(s));
}
}
Yor connFd is a global variable, which you access from your main thread and all handling threads. This will not do! Imagine that - you've accepted the first connection and set the variable to the receiving socket. You've spawn the handling thread, which started reading. Next thing you know, another connection is coming along and you are receiving it as well! This very moment connFd points to the new connection, so the thread which is already using it will suddenly switch to the new connection! Of course it is not good.
The way to fix this problem is to pass the connection to the thread in such a way that is is not shared across threads. And easiest way of doing so is to use C++ thread class.
For example, this is code fragment illustrating the above idea:
void handle_connection(int fd) {
... <your task1 code>
}
...
std::vector<std::thread> threads;
...
int conn = accept(listenFd, (struct sockaddr *)&clntAdd, &len);
threads.push_back(std::thread(&handle_connection, conn));
...
... (in the end)
for (auto&& t : threads)
t.join();
I am trying to implement a socket server by using epoll. I have 2 threads doing 2 tasks:
listening to incoming connection
writing on screen the data the client is sending.
For my test I have the client and the server on the same machine with 3 or 4 clients running.
The server works fine until I don't kill one of the client by issuing a CTRL-C: as soon I do that the server starts looping and printing at a very fast rate data from other client. The strange thing is that
the client sends data each 2 seconds but the rate of the server is higher
epoll_wait is also supposed to print something when one of the client disconnects as it is checking also for EPOLLHUP or EPOLLERR
epoll_wait should wait a bit before printing since I gave him a timeout of 3000 milliseconds.
Can you help? Could it be that I am passing in a wrong way the epoll descriptor to the other thread? I cannot understand since the code looks similar to many examples around.
Thanks a lot
Mn
// server.cpp
#include <iostream>
#include <cstdio>
#include <cstring>
extern "C" {
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <pthread.h>
}
#define MAX_BACKLOG 10
void* readerthread(void* args){
int epfd = *((int*)args);
epoll_event outwait[10];
while(true){
int retpw = epoll_wait( epfd, outwait,20, 3000 );
if( retpw == -1 ){
printf("epoll error %m\n");
}else if( retpw == 0 ){
printf("nothing is ready yet\n");
continue;
}else{
for( int i=0;i<retpw;i++){
if( outwait[i].events & EPOLLIN ){
int fd = outwait[i].data.fd;
char buf[64];
if( -1 == read(fd,buf,64) ){
printf("error reading %m\n");
}
printf("%s\n",buf);
}else{
std::cout << "other event" << std::endl;
}
}
}
}
}
int main(){
int epfd = epoll_create(10);
if( -1 == epfd ){
std::cerr << "error creating EPOLL server" << std::endl;
return -1;
}
pthread_t reader;
int rt = pthread_create( &reader, NULL, readerthread, (void*)&epfd );
if( -1 == rt ){
printf("thread creation %m\n");
return -1;
}
struct addrinfo addr;
memset(&addr,0,sizeof(addrinfo));
addr.ai_family = AF_INET;
addr.ai_socktype = SOCK_STREAM;
addr.ai_protocol = 0;
addr.ai_flags = AI_PASSIVE;
struct addrinfo * rp,* result;
getaddrinfo( "localhost","59000",&addr,&result );
for( rp = result; rp != NULL; rp = rp->ai_next ){
// we want to take the first ( it could be IP_V4
// or IP_V6 )
break;
}
int sd = socket( AF_INET, SOCK_STREAM, 0 );
if(-1==sd ){
std::cerr << "error creating the socket" << std::endl;
return -1;
}
// to avoid error 'Address already in Use'
int optval = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
if( -1==bind( sd, result->ai_addr, result->ai_addrlen ) ){
printf("%m\n");
std::cerr << "error binding" << std::endl;
return -1;
}
while(true){
std::cout << "listen" << std::endl;
if( -1== listen(sd, MAX_BACKLOG ) ){
std::cerr << "listen didn't work" << std::endl;
return -1;
}
std::cout << "accept" << std::endl;
sockaddr peer;
socklen_t addr_size;
int pfd = accept( sd, &peer ,&addr_size );
if( pfd == -1 ){
std::cerr << "error calling accept()" << std::endl;
return -1;
}
epoll_event ev;
ev.data.fd = pfd;
ev.events = EPOLLIN;
std::cout << "adding to epoll list" << std::endl;
if( -1 == epoll_ctl( epfd, EPOLL_CTL_ADD, pfd, &ev ) ){
printf("epoll_ctl error %m\n");
return -1;
}
}
}
// end of server.cpp
// client.cpp
#include <iostream>
#include <cstring>
#include <cstdio>
extern "C"{
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
}
int main(){
const char* servername = "localhost";
const char* serverport = "59000";
struct addrinfo server_address;
memset( &server_address, 0, sizeof(struct addrinfo) );
server_address.ai_family = AF_INET;
server_address.ai_socktype = SOCK_STREAM;
server_address.ai_protocol = 0; // any protocol
server_address.ai_flags = 0;
struct addrinfo * result, * rp;
int res = getaddrinfo( servername, serverport, &server_address, &result );
if( -1 == res ){
std::cout << "I cannot getaddress " << servername << std::endl;
return -1;
}
int fd = socket( server_address.ai_family
, server_address.ai_socktype
, server_address.ai_protocol );
if( -1 == fd ){
printf("I cannot open a socket %m\n");
return -1;
}
for( rp = result; rp != NULL; rp = rp->ai_next ){
std::cout << "************" << std::endl;
if( -1 == connect( fd, rp->ai_addr, rp->ai_addrlen ) ){
close(fd);
}else{
std::cout << "connected" << std::endl;
break;
}
}
if( rp == NULL ){
std::cerr << "I couldn't connect server " << servername << std::endl;
}
while(true){
sleep(2);
pid_t me = getpid();
char buf[64];
bzero( buf,sizeof(buf));
sprintf( buf,"%ld",me );
write(fd,buf,sizeof(buf));
printf("%s\n",buf);
}
}
// end of client.cpp
A client disconnection is signalled by an EOF condition on the file descriptor. The system considers EOF to be a state in which the file descriptor is 'readable'. But, of course, the EOF condition cannot be read. This is the source of your looping. epoll is acting like the file descriptor for the disconnected client is always readable. You can detect that you have an EOF condition by checking when read returns 0 bytes read.
The only way to deal with an EOF condition is to close the file descriptor in some way. Depending on exactly how the flow of things go, this could be with shutdown(sockfd, SHUT_RD), shutdown(sockfd, SHUT_RDWR) or close(sockfd);.
Unless you know that you need the shutdown(2) call for whatever reason, I would recommend you use close. Of course, you should remember to tell epoll that the file descriptor is no longer of interest before you close. I'm not sure what will happen if you don't, but one possibility is that epoll will error. Another is that epoll will mysteriously begin reporting events for a new file descriptor that has the same numeric value before you add it to the list epoll should care about.
Socket cleanly closed by the other side will become readable and read(2) will return 0, you have to check for that. As coded now - level-triggered poll - epoll_wait(2) returns every time without waiting telling that you still haven't read that end-of-stream.
Alternatively, you can switch to edge-triggered poll (EPOLLET) and react to EPOLLRDHUP too.