This is a simple client server system. Server uses select to handle different client request. But the problem is: When I shut down the client, server will get segmentation fault. I don't know how to deal with the situation.
Thanks for your help.
Client Side:
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <queue>
#include <cstdlib>
#include <string.h>
#include <mutex>
#include <thread>
#include <pthread.h>
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <ctype.h>
#define SIZESTACKSPACE 1000000;
#define RCVBUFSIZE 32 /* Size of receive buffer */
using namespace std;
void *sendRequest(void *);
void *receiveRequest(void *);
//#define TEMPPORTNO "40868";
//#define IP1 "10.10.154.41";
//#define IP1 "192.168.37.166";
int sock; /* Socket descriptor */
bool running1 = true, running2 = true;
//queue1 is used for sending message
//queue2 is used by recieve to display message
queue<char*> queue1, queue2;
//create two mutex for two queue
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
//create conditional variables
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER;
pthread_attr_t attr;
int main(void){
pthread_t thread2, thread1;
int rc2, rc1;
size_t stacksize;
//initialize attributes
pthread_attr_init(&attr);
pthread_attr_getstacksize (&attr, &stacksize);
stacksize = sizeof(double)*1000+SIZESTACKSPACE;
pthread_attr_setstacksize(&attr, stacksize);
//create thread1, with function sendRequest()
rc1 = pthread_create(&thread1, &attr, sendRequest, NULL);
if(rc1){
cout<<" ERROR; return code from pthread_create() is " << rc1;
exit(-1);
}
//create thread2, with the function recieveRequest()
rc2 = pthread_create(&thread2, &attr, receiveRequest, NULL);
if(rc2){
cout<<" ERROR; return code from pthread_create() is " << rc2;
exit(-1);
}
int chunksize = 4; /* use BUFSIZ-1 for (probably) more efficient program */
char *s;
char *temp;
int buffersize;
int nPos;
char c;
char *str;
while(1){
buffersize = chunksize+1;
s = (char*) malloc(buffersize); /* get the first chunk */
if (s == NULL) {
printf("Can't allocate %d bytes\n", buffersize);
exit(1);
}
if((c=getchar()) != '\n' && c != EOF){
nPos = 1;
s[0] = c;
while((c=getchar()) != '\n' && c != EOF){
s[nPos] = c;
if(nPos>=buffersize){
buffersize += chunksize;
temp = (char*)realloc(s, buffersize);
if (temp == NULL) {
printf("Can't realloc %d bytes\n", chunksize);
free(s); /* clean up the previously allocated stuff */
s = NULL;
exit(1);
}
s=temp;
}
nPos++;
}
int k, j;
/*The last character is null*/
//nPos is the length of the string, aside of the null character
s[nPos] = '\0';
//to store the new string, allocation
str = (char*) malloc(nPos+6);
j = nPos;
//each byte contains a value of the number
k = 3;
while(k>=0){
str[k] = nPos % 10;
nPos = nPos/10;
k--;
}
str[4] = '\0';
k = 0;
while(k<=j){
str[k+5] = s[k];
k++;
}
str[k+5] = '\0';
free(s);
//add mutex here
pthread_mutex_lock(&mutex1);
queue1.push(str);
//signal sendRequest
if(!queue1.empty())
pthread_cond_signal(&cond1);
pthread_mutex_unlock(&mutex1);
//signal recvRequest
pthread_mutex_lock(&mutex2);
queue2.push(str);
if(!queue2.empty())
pthread_cond_signal(&cond2);
pthread_mutex_unlock(&mutex2);
}else if(c==EOF){
break;
}
}
//wait for thread 2
while(!queue2.empty());
//signal for threads
close(sock);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_exit(NULL);
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
pthread_cond_destroy(&cond1);
pthread_cond_destroy(&cond2);
return 0;
}
void *sendRequest(void *){
struct sockaddr_in echoServAddr; /* Echo server address */
unsigned short servPort; /* server port */
char* servIP; /* Server IP address (dotted quad) */
char* echoString; /* String to send to echo server */
unsigned int stringLen;
char* tempPort;
//fetch environment variables from the system
servIP = getenv ("SERVER_ADDRESS");
tempPort = getenv("SERVER_PORT");
// servIP = IP1;
// tempPort = TEMPPORTNO;
if(strlen(servIP) == 0 || strlen(tempPort) == 0){
perror("DOES NOT SET ENVIRONMENT VARIABLES FOR SERVER_ADDRESS OR SERVER_PORT\n");
exit(-1);
}
servPort = atoi(tempPort);
/* Create a reliable, stream socket using TCP */
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
printf("socket() failed\n");
exit(1);
}
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */
echoServAddr.sin_family = AF_INET; /* Internet address family */
echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */
echoServAddr.sin_port = htons(servPort); /* Server port */
/* Establish the connection to the echo server */
if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0){
perror("connect() failed\n");
exit(1);
}
pthread_mutex_lock(&mutex1);
while(true){
pthread_cond_wait(&cond1, &mutex1);
echoString = queue1.front();
queue1.pop();
pthread_mutex_unlock(&mutex2);
//send a request to the server
//determine the length that is going to be sent
stringLen = 5;
while(echoString[stringLen])
stringLen++;
/* Send the string to the server */
if (send(sock, echoString, stringLen, 0) != (int)stringLen){
perror("send() sent a different number of bytes than expected\n");
exit(1);
}
sleep(2);//sleep for 2 seconds
}
return (void *) 0;
}
void *receiveRequest(void *){
char* temp;
char *echoBuffer; /* Buffer for echo string */
unsigned int echoStringLen; /* Length of string to echo */
unsigned int bytesRcvd; /* Bytes read in single recv()]*/
char *partTemp;
unsigned int stringLen;
pthread_mutex_lock(&mutex2);
while(true){
//get the toppest value from the queue
pthread_cond_wait(&cond2, &mutex2);
temp = queue2.front();
pthread_mutex_unlock(&mutex2);
//get the length from the queue
//cp the first four bytes
int k = 0;
stringLen = 0;
while(k<4){
stringLen = stringLen * 10 + temp[k];
k++;
}
//wait for response
echoBuffer = (char *)malloc(5);
if((bytesRcvd = recv(sock, echoBuffer,5, 0)) <= 0){
printf("recv() failed or connection closed prematurely\n");
exit(1);
}
//totalBytesRcvd += bytesRcvd;
//get the length of the string recv
k=0;
echoStringLen = 0;
while(k<4){
echoStringLen = echoStringLen * 10 + echoBuffer[k];
k++;
}
echoBuffer = (char *)realloc(echoBuffer, echoStringLen + 1);
//recive the rest of the string, which is a length of echoStringLen+1
bytesRcvd = recv(sock, echoBuffer, echoStringLen+1, 0);
echoBuffer[echoStringLen] = '\0';
//escape the first 5 bytes for the printing
partTemp = temp;
k=0;
while(k<5){
partTemp ++;
k++;
}
printf("%s\nServer: %s \n", partTemp, echoBuffer);
free(echoBuffer);
//pop the toppest value from the queue
//this is useful for closing the threads
//it can ensure all allocations are freed
pthread_mutex_lock(&mutex2);
queue2.pop();
free(temp);
pthread_mutex_unlock(&mutex2);
}
return (void *) 0;
}
Server Side:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
#define PORT 0
#define MAXPENDING 5
int main(){
struct sockaddr_in serverAddress, clientAddress; // These stores the network settings
socklen_t serverAddressLength = sizeof(serverAddress), clientAddressLength = sizeof(clientAddress);
int serverSocketID, clientSocketID;
if ((serverSocketID = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket() failed");
exit(1);
}
// Specifying preference for IP address and port number lookup
memset(&serverAddress, 0, sizeof(serverAddress)); // Initialize memory for
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(PORT);
if (bind(serverSocketID, (struct sockaddr *) &serverAddress, serverAddressLength) != 0) {
perror("bind() failed");
close(serverSocketID);
exit(1);
}
// Server starts to listen
if (listen(serverSocketID, MAXPENDING) == -1) {
perror("listen() failed");
close(serverSocketID);
exit(1);
}
//The following code is to obtain IP address from ifaddr info from Linux
getsockname(serverSocketID, (struct sockaddr*) &serverAddress, &serverAddressLength);
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
if(ifa->ifa_name[0] == 'e' ){
printf("SERVER_ADDRESS %s\nSERVER_PORT %d\n", addressBuffer, (int) ntohs(serverAddress.sin_port));
break;
}
} else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
if(ifa->ifa_name[0] == 'e' ){
printf("SERVER_ADDRESS %s\nSERVER_PORT %d\n", addressBuffer, (int) ntohs(serverAddress.sin_port));
break;
}
}
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
// Select attributes
int largestFileDescriptorIndex = serverSocketID;
// We will add clients to the master list, select will use a worker copy of our master list
fd_set master, worker;
//initialize the file descriptor list
FD_ZERO(&master);
FD_ZERO(&worker);
FD_SET(serverSocketID, &master);
// Add keyboard to allow control over server
FD_SET(STDIN_FILENO, &master);
// Specify how long to block and wait for a client to do something
struct timeval fileDescriptorWaitTime;
// Wait for 1 second to check if there is data coming in
fileDescriptorWaitTime.tv_sec = 1;
fileDescriptorWaitTime.tv_usec = 0;
int running = 1, i;
while(running) { // This is the big red switch that makes the server run
worker = master; // Resets the select list
if (select(largestFileDescriptorIndex + 1, &worker, NULL, NULL, &fileDescriptorWaitTime) == -1) {
perror("select() failed");
close(serverSocketID);
exit(1);
}
// Loop through the state of all file descriptors
for (i = 0; i <= largestFileDescriptorIndex; i++) {
// Check if any file descriptor changed state
if (FD_ISSET(i, &worker)) {
// A new client is trying to connect
if (i == serverSocketID) {
// Client connect successfully
if ((clientSocketID = accept(serverSocketID,
(struct sockaddr*) &clientAddress, &clientAddressLength)) != -1) {
// Register client into master list
FD_SET(clientSocketID, &master);
if (clientSocketID > largestFileDescriptorIndex) {
// Update length of list to loop
largestFileDescriptorIndex = clientSocketID;
}
}
}
else if (i == STDIN_FILENO) { // Check keyboard input
fprintf(stdout, "Server is Shutting down\n");
getchar();
running = 0;
continue;
}else
{
char *echoBuffer; /* Buffer for echo string */
int recvMsgSize; /* Size of received message */
int j;
echoBuffer = (char *)malloc(5);
/* Receive message from client, get the first 5 bytes first to know the length of the string*/
if ((recvMsgSize = recv(clientSocketID, echoBuffer, 5, 0)) < 0){
perror("recv() failed");
close(clientSocketID);
FD_CLR(clientSocketID, &master);
}
int stringLen=0, k = 0;//the length of the string
/*convert the char * into an int*/
while(k<4){
stringLen = stringLen*10 + (int)echoBuffer[k];
k++;
}
char *str; // store the string
//string size + 4 bytes + '\0'+strlen(string)+'\0'
str = (char *)malloc(stringLen + 6);
//put the first 5 bytes into the echo string
k = 0;
while(k<5){
str[k] = echoBuffer[k];
k++;
}
free(echoBuffer);
//recieve string of a length of stringLen+1, which is char num + '\0'
echoBuffer = (char *)malloc(stringLen+1);
if ((recvMsgSize = recv(clientSocketID, echoBuffer, stringLen+1, 0)) < 0){
perror("recv() failed");
close(clientSocketID);
FD_CLR(clientSocketID, &master);
}
//set the last char to be null
echoBuffer[stringLen]='\0';
printf("%s\n", echoBuffer);
//deal with the data here
if(echoBuffer[0] <= 'z' && echoBuffer[0]>='a')
echoBuffer[0] = echoBuffer[0] + 'A'-'a';
//operations on data except the first one
for( j = 1; j<stringLen; j++)
{
if(echoBuffer[j]<='z' && echoBuffer[j]>='a' && echoBuffer[j-1] == ' ')
echoBuffer[j] = echoBuffer[j] + 'A'-'a';
else if(echoBuffer[j]<='Z' && echoBuffer[j]>='A' && echoBuffer[j-1] != ' ')
echoBuffer[j] = echoBuffer[j] + 'a'-'A';
}
//store the data into str
k= 0;
while(k<=stringLen){
str[k+5] = echoBuffer[k];
k++;
}
str[stringLen+5] = '\0';
free(echoBuffer);
recvMsgSize = stringLen+6;
/* Send received string */
/* Echo message back to client */
if (send(clientSocketID, str, recvMsgSize, 0) != recvMsgSize){
perror("send() failed");
close(clientSocketID);
FD_CLR(clientSocketID, &master);
}
free(str);
}//operations on the data finishes
}//if the client socket descriptor is in the list
}//loop through all the file descriptors
}//busy waiting
close(serverSocketID);
return 0;
}
When a client disconnects, the client's socket will select as ready-for-read, and then all subsequent attempts to recv() on that socket will return 0, to indicate EOF. Note that this situation is the only time recv() will return 0. It looks like your code is expecting recv() to return -1 in that scenario instead, so it isn't handling that case correctly.
Also it looks like you try to use echoBuffer after free()-ing it, which is undefined behavior and should be avoided. (In fact, why use malloc() and free() at all? Just declare echoBuffer on the stack with a large-enough size, and you won't have to worry about when to free() it)
Related
I'm implementing a server in C++ with non-blocking sockets.
Since I want to send messages between the client & server, I wrote 2 wrappers around send/recv syscalls. Mainly, I want to prepend 4Bytes (message length) to every message, so that the receiver knows how long to execute recv.
Moreover I have a client/server programs that each start a socket and listen on localhost.
Then the client sends a random message, which the server receives.
When I try,however, to send from the server to the client, both programs halt.
I have tested the wrappers many times and they read/deliver data, but whenever I try to receive on a previously sending connection, then comes the problem.
I know that there is a memory leak in the secure_recv but I need it to pass some custom tests, which are not very well written.
The issue lies in the select, which returns a positive number, but then I never go inside the if (FD_ISSET(fd, &readset)) statement.
What am I doing wrong and how can we fix it ? Thanks a lot !
EDIT
My problem was that the sockets were blocking(busy working) at the select function. I updated the code so that there is no select in the secure_* functions. It's a much better way to first check if the socket is available for send/recv on a client/server thread level via select and then calling the secure_* functions. Question is answered for now.
client.cpp
// Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "util.h"
#define PORT 8080
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
int numbytes;
size_t size = 0;
std::unique_ptr<char[]> buf = get_rand_data(size);
if ((numbytes = secure_send(sock, buf.get(), size, 0)) == -1)
{
std::cout << std::strerror(errno) << "\n";
exit(1);
}
std::cout << "Client sent : " << numbytes << "\n";
int64_t bytecount = -1;
while (1)
{
std::unique_ptr<char[]> buffer;
if ((bytecount = secure_recv(sock, buffer, 0)) <= 0)
{
if (bytecount == 0)
{
break;
}
}
std::cout << bytecount << "\n";
}
std::cout << "Client received : " << bytecount << "\n";
close(sock);
return 0;
}
server.cpp
// Server side C/C++ program to demonstrate Socket programming
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include "util.h"
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr *)&address,
sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
// set the socket to non-blocking mode
fcntl(new_socket, F_SETFL, O_NONBLOCK);
int64_t bytecount = -1;
while (1) {
std::unique_ptr<char[]> buffer;
if ((bytecount = secure_recv(new_socket, buffer, 0)) <= 0) {
if (bytecount == 0) {
break;
}
}
std::cout << bytecount << "\n";
}
int numbytes;
size_t size = 0;
std::unique_ptr<char[]> buf = get_rand_data(size);
if ((numbytes = secure_send(new_socket, buf.get(), size, 0)) == -1)
{
std::cout << std::strerror(errno) << "\n";
exit(1);
}
std::cout << "Client sent : " << numbytes << "\n";
close(new_socket);
return 0;
}
util.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#include <cstring>
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <algorithm>
#include <memory>
#include <poll.h>
#include <iomanip>
/**
* It takes as arguments one char[] array of 4 or bigger size and an integer.
* It converts the integer into a byte array.
*/
void convertIntToByteArray(char *dst, int sz)
{
auto tmp = dst;
tmp[0] = (sz >> 24) & 0xFF;
tmp[1] = (sz >> 16) & 0xFF;
tmp[2] = (sz >> 8) & 0xFF;
tmp[3] = sz & 0xFF;
}
/**
* It takes as an argument a ptr to an array of size 4 or bigger and
* converts the char array into an integer.
*/
int convertByteArrayToInt(char *b)
{
return (b[0] << 24) + ((b[1] & 0xFF) << 16) + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
}
/**
* It constructs the message to be sent.
* It takes as arguments a destination char ptr, the payload (data to be sent)
* and the payload size.
* It returns the expected message format at dst ptr;
*
* |<---msg size (4 bytes)--->|<---payload (msg size bytes)--->|
*
*
*/
void construct_message(char *dst, char *payload, size_t payload_size)
{
convertIntToByteArray(dst, payload_size);
memcpy(dst + 4, payload, payload_size);
}
/**
* It returns the actual size of msg.
* Not that msg might not contain all payload data.
* The function expects at least that the msg contains the first 4 bytes that
* indicate the actual size of the payload.
*/
int get_payload_size(char *msg, size_t bytes)
{
// TODO:
return convertByteArrayToInt(msg);
}
/**
* Sends to the connection defined by the fd, a message with a payload (data) of size len bytes.
* The fd should be non-blocking socket.
*/
/**
* Receives a message from the fd (non-blocking) and stores it in buf.
*/
int secure_recv(int fd, std::unique_ptr<char[]> &buf)
{
// TODO:
int valread = 0;
int len = 0;
int _len = 4;
bool once_received = false;
std::vector<char> ptr(4);
while (_len > 0)
{
int _valread = recv(fd, ptr.data() + valread, _len, MSG_DONTWAIT);
if (_valread == 0)
{
break;
}
if (_valread > 0)
{
_len -= _valread;
valread += _valread;
}
if (!once_received && valread == 4)
{
once_received = true;
len = convertByteArrayToInt(ptr.data());
_len = len;
ptr = std::vector<char>(len);
valread = 0;
}
}
buf = std::make_unique<char[]>(len);
memcpy(buf.get(), ptr.data(), len);
return len;
}
/**
* Sends to the connection defined by the fd, a message with a payload (data) of size len bytes.
* The fd should be non-blocking socket.
*/
int secure_send(int fd, char *data, size_t len)
{
// TODO:
char ptr[len + 4];
int valsent = 0;
int _len = 4;
bool once_sent = false;
construct_message(ptr, data, len);
while (_len > 0)
{
int _valsent = send(fd, ptr + valsent, _len, MSG_DONTWAIT);
if (_valsent == 0)
{
break;
}
if (_valsent > 0)
{
_len -= _valsent;
valsent += _valsent;
}
if (!once_sent && valsent == 4)
{
once_sent = true;
_len = len;
}
}
return len;
}
Compilation via
g++ -O3 -std=c++17 -Wall -g -I../ client.cpp -o client -lpthread
Let's start with the write loop:
while (1)
{
// std::cerr << "first iteration send\n";
FD_ZERO(&writeset);
FD_SET(fd, &writeset);
if (select(fd + 1, NULL, &writeset, NULL, NULL) > 0)
{
if (FD_ISSET(fd, &writeset))
{
valsent = send(fd, ptr + valsent, _len, 0);
Oops. This loses valsent, which tracks how many bytes you've sent so far. So on your third loop, ptr + valsent will only add the number of bytes received the second time. You need to track the total number of bytes sent so far somewhere.
if (valsent <= 0)
{
break;
}
_len -= valsent;
And what if _len becomes zero? You'll still call select and even send. You probably want that while (1) to be while (_len > 0).
}
}
}
return len;
Now, onto the read loop:
if (select(fd + 1, &readset, NULL, NULL, NULL) > 0)
{
if (FD_ISSET(fd, &readset))
{
if (first_iteration)
{
recv(fd, ptr, 4, 0);
You ignore the return value of recv here. What if it's not 4?
len = convertByteArrayToInt(ptr);
buf = std::make_unique<char[]>(len);
_len = len;
first_iteration = false;
}
valread = recv(fd, buf.get() + valread, _len, 0);
if (valread <= 0)
{
break;
}
_len -= valread;
You don't leave the loop if _len is zero. You'll call select again, waiting for data that may never come.
}
}
I am currently writing a program in linux where:
client sends a "password" to the server
server waits for n people to send the password and records the address of the sender
after n messages are received, a start message is sent back to the senders.
The problem is when I try to send "start" back to the clients I get an Illegal seek error (ERROR in sendto: Illegal seek). And only the first client receives the start message (clientaddrs[0])
Note: after 30 minutes of testing, the error has now become: Address family not supported by protocol.(absolutely nothing in the code has changed)
Here is my code (I've pasted a minimal reproduceable example)
To reproduce the problem:
run the server code with arguments: 8080
enter 2 at the prompt
run the client code on two different with argument: 127.0.0.1 8080
choose two different passcodes and enter them when prompted by the client
Server code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <errno.h>
#include <ifaddrs.h>
#define TO_CLI_BUF_SIZE 32
#define FROM_CLI_BUF_SIZE 8
void printAddr(){ // for printing my ip
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
} else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
}
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
}
int main(int argc, char ** argv){
//seed rand
srand(time(NULL));
int sockfd; // socket
int port; // my port to listen on
struct sockaddr_in serveraddr; // server's address
struct sockaddr_in clientaddrs[4];
socklen_t clientLens[4];
int currentAddrMax = 0;
struct hostent * hostp; //host info
char * hostaddrp; // host adddr string
char toClientBuf[TO_CLI_BUF_SIZE];
char fromClientBuf[FROM_CLI_BUF_SIZE];
if(argc != 2){
perror("usage: file <port>");
exit(1);
}
port = atoi(argv[1]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0){
perror("ERROR: opening socket.");
exit(1);
}
//int option = 1;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&option, sizeof(int));
//internet stuff
bzero((char*) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("ERROR on bind");
exit(1);
}
int playerKeys[4];
int playerJoined[4];
printf("(you can enter 1,2,3 or 4)\n");
printf("Enter amount of players: \n");
int amountPlayers = 0;
scanf("%d",&amountPlayers);
// hacky way to clear screen
printf("\033[H\033[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
bool keyExists = true;
while (keyExists == true ){
playerKeys[i] = rand()%10000;
keyExists = false;
for(int j = 0; j < i; j++){
if(playerKeys[i] == playerKeys[j]){
keyExists = true;
}
}
}
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
fflush(stdin);
}
for(int i = 0; i < amountPlayers;i++){
bzero(fromClientBuf, FROM_CLI_BUF_SIZE);
int n = recvfrom(sockfd, fromClientBuf,FROM_CLI_BUF_SIZE, 0, (struct sockaddr*) &clientaddrs[currentAddrMax], &(clientLens[currentAddrMax]));
//TODO store senders
if(n>0){
int key = (fromClientBuf[0]-'0')*1000; //TODO change the way keys are extracted.
key += (fromClientBuf[1]-'0')*100;
key += (fromClientBuf[2]-'0')*10;
key += (fromClientBuf[3]-'0');
for (int i = 0; i < 4; i++){
if(playerKeys[i] == key && playerJoined[i] == 0){
playerJoined[i] = 1;
currentAddrMax++;
}
}
printf("\033[H\033[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
}
// decode key
}
}
//TODO finished waiting for all senders. send them start signal
//MAY BE USEFULL:n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddr, clientlen);
strcpy(toClientBuf, "start");
for(int j = 0; j < currentAddrMax; j++){
int n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddrs[j], (clientLens[j]));
if(n < 0) {
perror("ERROR in sendto");
printf("%d\n",j);
exit(1);
}
}
// wait for connections
//main loop
//set some options
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 100;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&read_timeout,sizeof(read_timeout)) < 0) {
perror("Error with options");
}
printf("start loop\n");
return 0;
}
client code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define FROM_SER_BUF_SIZE 32
#define TO_SER_BUF_SIZE 8
int main(int argc, char **argv){
int sockfd, portno, n;
socklen_t serverlen;
struct sockaddr_in serveraddr;
struct hostent *server;
char *hostname;
char toServerBuf[TO_SER_BUF_SIZE];
char fromServerBuf[FROM_SER_BUF_SIZE];
if (argc != 3) {
perror("usage: filename <hostname> <port>\n");
exit(0);
}
hostname = argv[1];
portno = atoi(argv[2]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("ERROR: opening sockets\n");
exit(0);
}
// get host
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
exit(0);
}
// build server's internet address
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
bzero(toServerBuf, TO_SER_BUF_SIZE);
int key = 0;
printf("Please enter your key: ");
scanf("%d",&key);
if(key > 9999 || key < 0){
printf("INVALID KEY\n");
exit(1);
}
toServerBuf[0] = key/1000 + '0';
toServerBuf[1] = (key%1000)/100 + '0';
toServerBuf[2] = (key%100)/10 +'0';
toServerBuf[3] = key%10 + '0';
serverlen = sizeof(serveraddr);
n = sendto(sockfd, toServerBuf, strlen(toServerBuf), 0, ( struct sockaddr *) &serveraddr, serverlen);
if (n < 0){
perror("ERROR: sendto\n");
exit(0);
}
//TODO wait for server to get send start signal
bzero(fromServerBuf, FROM_SER_BUF_SIZE);
n = recvfrom(sockfd, fromServerBuf, FROM_SER_BUF_SIZE, 0,( struct sockaddr *) &serveraddr, &serverlen);
printf("wow we got here");
return 0;
}
Turns out it was a problem with g++. No idea why g++ doesn't work however, once I switched to clang all my errors went away. However, clang++ does not work either.
Edit: There was a problem with the clientaddr length found with valgrind. Turns out in the array for client legnths (for my example), we must first initialize it with the line
clientLens[currentAddrMax] = sizeof(clientaddrs[currentAddrMax]);
As Bodo mentions, we must always initialize the client length otherwise there will be unspecified behaviour.
I am writing a tftp server in C and testing it with the tftp command on the terminal. However, for most of the times, I receive something liske the following when I try to send RRQ:
tftp> get a.txt
sent RRQ <file=a.txt, mode=netascii>
received ERROR <code=4, msg=>
Error code 1024:
other barly happend case includes:
tftp> get a.txt
sent RRQ <file=a.txt, mode=netascii>
received DATA <block=20334, 512 bytes>
discarded 4 packets
and this one: this might look correct but it barely happened. The text file I use to test it is of 857 bytes.
sent ACK <block=1>
received DATA <block=1, 512 bytes>
sent ACK <block=1>
received DATA <block=2, 345 bytes>
Received 857 bytes in 0.0 seconds
and here is part of my code
Here buffer is a char array of size 512 , to simplify the code, I have removed some of the error handling code
Thanks to whoever may help
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <cerrno>
//define opcode for later use
enum opcode {
RRQ = 1,
WRQ,
DATA,
ACK,
ERROR
};
void handle_write(struct sockaddr_in * sock_info, char* filename, int BUF_LEN){
//printf("Received write request + %d\n", WRQ);
return;
}
void handle_error(unsigned short int * opcode_ptr, char* buffer, socklen_t sockaddr_len, int server_socket, struct sockaddr_in * sock_info, const char* errormsg){
ssize_t n;
*opcode_ptr = htons(ERROR);
*(opcode_ptr + 1) = htons(1);
*(buffer + 4) = 0;
//memcpy(buffer+4, errormsg, strlen(errormsg));
intr_send:
n = sendto(server_socket, buffer, 5, 0,
(struct sockaddr *)sock_info, sockaddr_len);
if(n < 0) {
if(errno == EINTR) goto intr_send;
perror(errormsg);
exit(-1);
}
return;
}
int main() {
int BUF_LEN = 516;
ssize_t n;
char buffer[BUF_LEN];
socklen_t sockaddr_len;
int server_socket;
struct sigaction act;
unsigned short int opcode;
unsigned short int * opcode_ptr;
struct sockaddr_in sock_info;
memset(&sock_info, 0, sockaddr_len);
//----------setup the server----------------//
sock_info.sin_addr.s_addr = htonl(INADDR_ANY);
sock_info.sin_port = htons(5743);
sock_info.sin_family = PF_INET;
if((server_socket = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(-1);
}
sockaddr_len = sizeof(sock_info);
if(bind(server_socket, (struct sockaddr *)&sock_info, sockaddr_len) < 0) {
perror("bind");
exit(-1);
}
getsockname(server_socket, (struct sockaddr *)&sock_info, &sockaddr_len);
printf("UDP server listening on port: %d\n", ntohs(sock_info.sin_port));
//----------setup the server----------------//
while(1){
intr_recv:
n = recvfrom(server_socket, buffer, BUF_LEN, 0, (struct sockaddr *)&sock_info, &sockaddr_len);
if(n < 0) {
if(errno == EINTR) goto intr_recv;
perror("recvfrom()");
exit(-1);
}
opcode_ptr = (unsigned short int *)buffer;
opcode = ntohs(*opcode_ptr); //the opcode will be either RRQ or WRQ according to the test
if(opcode != RRQ && opcode != WRQ) {
/* Illegal TFTP Operation */
handle_error(opcode_ptr, buffer, sockaddr_len, server_socket, &sock_info, "invalid command");
}
else {
if(fork() == 0) {
/* Child - handle the request */
FILE* fd;
char* filename;
filename = strdup(buffer+2); //this is the filename to be read (i.e. a.txt)
printf("request received\n");
char data[512];
//----------------------------------handle read request-------------------------------------//
if(opcode == RRQ){
int blocknumber = 0;
int i = 0; //counter for loop
fd = fopen(filename, "r");
free(filename);
//uint8_t data[512];
ssize_t datalen, n;
int done = 0; //this is a boolean indicator that indicates whether the packet transfering process is done or not.
while(!done){
datalen = fread(data, 1, 512, fd);
blocknumber++;
if(datalen < 512){
done = 1; //according to rfc 1350, the last packet will have a data length less than 512 bytes.
}
//for(i = 5; i > 0; i--){
*opcode_ptr = htons(DATA);
opcode = ntohs(*opcode_ptr);
*(opcode_ptr + 1) = htons(blocknumber);
memcpy(buffer + 4, data, datalen);
buffer[datalen + 4] = '\0';
//*(buffer + 4) = 0;
//printf("%d %s\n", datalen, buffer+2);
n = sendto(server_socket, buffer, 4 + datalen, 0, (struct sockaddr *)&sock_info, sockaddr_len);
if(n < 0){
perror("sendto() failed");
exit(-1);
}
//printf("done %d\n", done);
//char buffer[512];
n = recvfrom(server_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&sock_info, &sockaddr_len);
opcode_ptr = (unsigned short int *)buffer;
opcode = ntohs(*opcode_ptr);
if(n >= 0 && n < 4){
//handle_error(opcode_ptr, buffer, sockaddr_len, server_socket, &sock_info, "invalid request size");
}
if(n > 4){
break;
}
//}
//if(i != 0){
// printf("Transfer timeout!\n");
// exit(1);
//}
//printf("opcode is %d\n", opcode);
if(opcode == ERROR){
printf("Error received\n");
exit(1);
}
if(opcode != ACK){
printf("Invalid message received\n");
exit(1);
}
}
}
//----------------------------------handle read request-------------------------------------//
//----------------------------------handle write request------------------------------------//
//----------------------------------handle write request------------------------------------//
close(server_socket);
break;
}
else {
/* Parent - continue to wait */
}
}
}
return EXIT_SUCCESS;
}
I read your code but not tested by executing.
Compareing with RFC 1350, what I found are
The data field is upto 512 bytes, so 512-byte buffer is not enough because there are no room for the header (Opcode and Block #). You need at least 4 more bytes.
You write the data from buffer + 2 via memcpy(). This should destroy the block number. It seems buffer + 4 should be used instead.
buffer[datalen + 2] = '\0'; shouldn't be needed. I think you should remove it because it will destroy the data or cause buffer overrun.
You should close the file opened after handling read request.
I'm new to socket programming, but I have followed ibm example at https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzab6/poll.htm and changed some code to make it better but when I connect to the server via telnet ip port, and make it echo messages it works for the first client that connects but on second it does not echo the messages?
#include <netinet/in.h> // sockaddr_in struct
#include <arpa/inet.h> //inet_addr()
#include <sys/socket.h>
#include <errno.h> //errors
#include <stdio.h> //perror()
#include <cstdlib> //EXIT_FAILURE
#include <sys/ioctl.h> //FIONBIO
#include <unistd.h> //close file descriptor
#include <fcntl.h> //make non blocking
#include <poll.h> //poll stuff
#include <string.h> //memset
int main()
{
int s = -1;
int rc;
int optval = 1;
int timeout;
bool end_server = false; //because we need to log if EWOULDBLOCK is true...
struct pollfd fds[200]; //initialize pollfd struct
int nfds = 1; // nfds_t really set to 1 else it will be 199 once we pass it to poll....
int current_size = 0;
int new_s = -1;
int close_conn;
char *buff;
int len;
bool compress_array;
s = socket(AF_INET, SOCK_STREAM, 0);
//make socket description reusable with SO_REUSEADDR
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval), sizeof(optval));
if(rc < 0){
perror("setsockopt()");
close(s);
exit(EXIT_FAILURE);
}
//make socket non-blocking
//rc = ioctl(s, FIONBIO, reinterpret_cast<char*>(&optval));
//if(rc < 0)
//{
// perror("ioctl()");
// close(s);
// exit(EXIT_FAILURE);
//}
fcntl(s, F_SETFL, O_NONBLOCK);
struct sockaddr_in saddr;
//initialize sockaddr_in struct
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
rc = bind(s, reinterpret_cast<struct sockaddr *>(&saddr), sizeof(saddr));
if(rc < 0){
perror("bind()");
exit(EXIT_FAILURE);
}
rc = listen(s, 32);
if(rc < 0){
perror("listen() failed");
close(s);
exit(EXIT_FAILURE);
}
//initialize fds struct
memset(&fds, 0, sizeof(fds));
fds[0].fd = s;
fds[0].events = POLLIN; //check if data to read
//initialize timeout value to 3 mins based on millisecs
//timeout = (3 * 60 * 1000); // because function will be like sleep() that uses millisecs
timeout = 10000;
do{
//call poll() and wait 3 mins to complete because of timeout
printf("Waiting on poll()...\n");
rc = poll(fds, nfds, timeout);
if(rc < 0){
perror("poll() failed");
exit(EXIT_FAILURE);
}
//check if 3 minutes timeout expired
if(rc == 0){
printf("poll() timed out ending program...\n");
exit(EXIT_FAILURE);
}
current_size = nfds;
for(int i = 0; i < current_size; i++)
{
//loop thru fds and check if revents returns POLLIN, means the fd have data to read...
if(fds[i].revents == 0)
continue;
//if revents is not POLLIN then exit program and log
if(fds[i].revents != POLLIN){
printf("revents != POLLIN, revents = %d\n", fds[i].revents);
//end_server = true;
//break;
//perror("revents unknown");
//exit(EXIT_FAILURE);
close(fds[i].fd);
fds[i].fd = -1;
break;
}
if(fds[i].fd == s){
printf("Listening socket available\n");
do{
//accept each new incoming connections
new_s = accept(s, NULL, NULL);
if(new_s < 0){
if(errno != EWOULDBLOCK){
perror("accept() failed because of socket would block");
end_server = true;
}
//printf("something else wrong with accept()\n");
break;
}
//add new incoming connection
printf("new incoming connection - nfds: %d\n", new_s);
fds[nfds].fd = new_s;
fds[nfds].events = POLLIN;
nfds++;
//continue;
//loop back up and accept another connection
} while(new_s != -1);
}
// file descriptor is readable because its now new_s instead of s
else {
printf("descriptor %d is readable\n", fds[i].fd);
close_conn = false;
//receive all data on this connection till we go back and poll again
do {
rc = recv(fds[i].fd, reinterpret_cast<void*>(&buff), sizeof(buff), 0);
if(rc < 0){
if(errno != EWOULDBLOCK){
perror("recv() failed");
close_conn = true;
}
break;
}
//check if conn was closed by client
if(rc == 0){
printf("connection closed");
close_conn = true;
break;
}
//data was received
len = rc;
printf("%d bytes received", len);
//process stuff or echo data back to client
rc = send(fds[i].fd, reinterpret_cast<void*>(&buff), sizeof(buff), 0);
if(rc < 0){
perror("send() failed");
close_conn = true;
break;
}
memset(&buff, 0, sizeof(buff));
} while (true);
if(close_conn){
close(fds[i].fd);
fds[i].fd = -1;
compress_array = true;
}
}
}
if(compress_array){
compress_array = false;
int i = 0;
for(i = 0; i < nfds; i++){
if(fds[i].fd == -1){
for(int j = i; j < nfds; j++){
fds[j].fd = fds[j+1].fd;
}
i--;
nfds--;
}
}
}
} while (end_server == false);
//clean all sockets that are open
for(int i = 0; i < nfds; i++){
if(fds[i].fd > 0){ // if already -1 don't need to close socket
close(fds[i].fd);
fds[i].fd = -1;
}
}
return 0;
}
Ahh it was because I looped with while(true) so it kept looping trying to recv data instead of going back up and add new connection to the list.
I can't seem to get working 2 watchers per socket.. Code below doesn't acually works at all, but if i mix up these calls(for ex. call init/set/start for 1 watcher and then for other), i get only 1 watcher working.. Is there something I'm missing badly here...?
I don't think it has anything to do with loops and setup... I have 1 accept loop(default loop) and 1 loop for accepted connections. I tried both, running code below directly after accepting connection on accept loop and via ev_async_send(...) then executing this code from other io loop. Results were same.
Also setting both events on 1 watcher works fine too.
Thank you
ev_init (pSockWatcher->_wW, &CNetServer::send_cb);
ev_init (pSockWatcher->_wR, &CNetServer::recv_cb);
ev_io_set (pSockWatcher->_wW, pSockWatcher->_sd, EV_WRITE );
ev_io_set (pSockWatcher->_wR, pSockWatcher->_sd, EV_READ );
ev_io_start (loop, pSockWatcher->_wR);
ev_io_start (loop, pSockWatcher->_wW);
Well, here is an example with two I/O watchers on one socket fd, which seems to work fine for me. I am using the ev_io_init() function, however, not the ev_init() and ev_set().
#include <ev.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
static struct ev_loop *loop;
static ev_timer timeout_watcher;
static ev_io in_watcher, out_watcher;
static ev_idle idle_watcher;
static int sock_fd;
// socket input watcher
static void in_cb(EV_P_ ev_io *watcher, int revents) {
int r, t;
char buf[1024];
for (t = 0; (r = read(sock_fd, buf, sizeof(buf))) > 0;) {
t += r;
write(STDOUT_FILENO, buf, r); // copy input to stdout
if (buf[r-1] == '\n') break; // operate line-at-a-time
}
fprintf(stderr, "in: count = %d\n", t);
if (r == 0) {
fputs("in: connection closed\n", stderr);
ev_io_stop(loop, &in_watcher); // stop the socket watcher
ev_break(loop, EVBREAK_ALL); // exit the loop
} else if (r < 0) {
perror("read");
}
}
// socket output watcher
static void out_cb(EV_P_ ev_io *watcher, int revents) {
int r, t, lim;
char buf[1024];
ev_io_stop(loop, &out_watcher);
for (t = 0; t < sizeof(buf); t++) {
buf[t] = 'a' + (rand() % 26);
}
for (t = 0, lim = rand() % 10000 + 1000;
(r = write(sock_fd, buf, (lim - t > sizeof(buf)) ? sizeof(buf) : lim - t)) > 0;) {
t += r;
if (t >= lim) break;
}
if (r < 0) {
perror("write");
}
fprintf(stderr, "out: finished sending, count = %d\n", t);
}
static void timeout_cb(EV_P_ ev_timer *watcher, int revents) {
fprintf(stderr, "timeout: now = %f\n", ev_now(loop));
// send a bunch of stuff on the socket when able
ev_io_start (loop, &out_watcher);
}
static void idle_cb(EV_P_ ev_idle *watcher, int revents) {
static long idle_count = 0;
fprintf(stderr, "idle: count = %ld\n", ++idle_count);
sleep(1); // simulate doing stuff
}
int main() {
extern int errno;
int master_fd;
int sock_opt = 1;
int conn_port = 7000;
struct sockaddr_in addr;
socklen_t addrlen;
// **** the following is needed to set up a socket to receive data ****
master_fd = socket(AF_INET, SOCK_STREAM, 0);
if (master_fd == -1) {
perror("socket");
return errno;
}
if (setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &sock_opt, sizeof(sock_opt)) == -1) {
perror("setsockopt");
return errno;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(conn_port);
addrlen = sizeof(addr);
if (bind(master_fd, (struct sockaddr *) &addr, addrlen) != 0) {
perror("bind");
return errno;
}
if (listen(master_fd, 3) != 0) {
perror("listen");
return errno;
}
fprintf(stderr, "awaiting a connection on port %d\n", conn_port);
sock_fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen);
if (sock_fd == -1) {
perror("accept");
return errno;
}
fputs("in: connection established\n", stderr);
// **** end of socket setup code ****
// define a loop
loop = ev_default_loop(0);
// define a repeating timer
ev_timer_init (&timeout_watcher, timeout_cb, 5.0, 5.0);
ev_timer_start (loop, &timeout_watcher);
// define an idle process
ev_idle_init(&idle_watcher, idle_cb);
ev_idle_start (loop, &idle_watcher);
// define the socket data receiver
ev_io_init(&in_watcher, in_cb, sock_fd, EV_READ);
ev_io_start (loop, &in_watcher);
// define the socket data write complete watcher
ev_io_init(&out_watcher, out_cb, sock_fd, EV_WRITE);
// run the loop
ev_run(loop, 0);
// clean up
close(sock_fd);
close(master_fd);
return 0;
}