Recently I have been struggling with servers for the first time:
The case is: I have a software (Motive from Optitrack) that provides a streaming of data via multicasting.
I have tried to modify a previous program, it can be build but I have some issues to solve:
1º- I want a .bat file to automatically open the server with the argc parameters, I have made one, but it doesn't work. The code is at the end.
2º When executing the .exe file, the console bips for a moment and closes. I suppose that it is because it returns -1, but maybe there is some error in the code.
I'm on Windows 10, using Code::Blocks and Gcc compiler.
The server code is:
#include <sys/types.h> /* for type definitions */
#include <winsock2.h> /* for win socket API calls */
#include <ws2tcpip.h> /* for win socket structs */
#include <stdio.h> /* for printf() and fprintf() */
#include <stdlib.h> /* for atoi() */
#include <string.h> /* for strlen() */
#define MAX_LEN 1024 /* maximum receive string size */
#define MIN_PORT 1051 /* minimum port allowed */
#define MAX_PORT 1051 /* maximum port allowed */
#define NUM_RIG_OBJ 2
int main(int argc, char *argv[]) {
int sock; /* socket descriptor */
int flag_on = 1; /* socket option flag */
struct sockaddr_in mc_addr; /* socket address structure */
char recv_str[MAX_LEN+1]; /* buffer to receive string */
int recv_len, ID ; /* length of string received */
struct ip_mreq mc_req; /* multicast request structure */
char* mc_addr_str ; /* multicast IP address */
unsigned short mc_port; /* multicast port */
struct sockaddr_in from_addr; /* packet source */
unsigned int from_len; /* source addr length */
WSADATA wsaData; /* Windows socket DLL structure */
float xa,ya,za,qx,qy,qz,qw;
/* validate number of arguments */
if (argc != 3) {
fprintf(stderr,
"Usage: %s <Multicast IP> <Multicast Port>\n",
argv[0]);
exit(1);
}
mc_addr_str = argv[1]; /* arg 1: multicast ip address */
mc_port = atoi(argv[2]); /* arg 2: multicast port number */
/* validate the port range */
if ((mc_port < MIN_PORT) || (mc_port > MAX_PORT)) {
fprintf(stderr, "Invalid port number argument %d.\n",
mc_port);
fprintf(stderr, "Valid range is between %d and %d.\n",
MIN_PORT, MAX_PORT);
exit(1);
}
/* Load Winsock 2.0 DLL */
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
fprintf(stderr, "WSAStartup() failed");
exit(1);
}
/* create socket to join multicast group on */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("socket() failed");
exit(1);
}
/* set reuse port to on to allow multiple binds per host */
if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&flag_on,
sizeof(flag_on))) < 0) {
perror("setsockopt() failed");
exit(1);
}
/* construct a multicast address structure */
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
mc_addr.sin_port = htons(mc_port);
/* bind to multicast address to socket */
if ((bind(sock, (struct sockaddr *) &mc_addr,
sizeof(mc_addr))) < 0) {
perror("bind() failed");
exit(1);
}
/* construct an IGMP join request structure */
mc_req.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
mc_req.imr_interface.s_addr = htonl(INADDR_ANY);
/* send an ADD MEMBERSHIP message via setsockopt */
if ((setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char*) &mc_req, sizeof(mc_req))) < 0) {
perror("setsockopt() failed");
exit(1);
}
int md=recv_str[0];
char *ptr = recv_str;
float x[NUM_RIG_OBJ]= {0};
float y[NUM_RIG_OBJ]= {0};
float z[NUM_RIG_OBJ]= {0};
float q1[NUM_RIG_OBJ]= {0};
float q2[NUM_RIG_OBJ]= {0};
float q3[NUM_RIG_OBJ]= {0};
float q4[NUM_RIG_OBJ]= {0};
for (;;) { /* loop forever */
/* clear the receive buffers & structs */
memset(recv_str, 0, sizeof(recv_str));
from_len = sizeof(from_addr);
memset(&from_addr, 0, from_len);
/* block waiting to receive a packet */
if ((recv_len = recvfrom(sock, recv_str, MAX_LEN, 0,
(struct sockaddr*)&from_addr, &from_len)) < 0) {
perror("recvfrom() failed");
exit(1);
}
/* output received string */
printf("Received %d bytes from %s: ", recv_len,
inet_ntoa(from_addr.sin_addr));
printf("%s", recv_str);
// rigid body pos/ori
if (md==7){
for (int i=0;i<NUM_RIG_OBJ;i++){
memcpy(&ID, ptr, 4); ptr += 4;
memcpy(&xa, ptr, 4); ptr += 4;
memcpy(&ya, ptr, 4); ptr += 4;
memcpy(&za, ptr, 4); ptr += 4;
memcpy(&qx, ptr, 4); ptr += 4;
memcpy(&qy, ptr, 4); ptr += 4;
memcpy(&qz, ptr, 4); ptr += 4;
memcpy(&qw, ptr, 4); ptr += 4;
printf("ID : %d\n", ID);
printf("pos: [%3.4f,%3.4f,%3.4f]\n", xa,ya,za);
printf("ori: [%3.4f,%3.4f,%3.4f,%3.4f]\n", qx,qy,qz,qw);
x[i]=xa;
y[i]=ya;
z[i]=za;
q1[i]=qx;
q2[i]=qy;
q3[i]=qz;
q4[i]=qw;
ptr = recv_str;
memcpy(ptr, &i, 4); ptr += 4;
memcpy(ptr, &x[i], 4); ptr += 4;
memcpy(ptr, &y[i], 4); ptr += 4;
memcpy(ptr, &z[i], 4); ptr += 4;
memcpy(ptr, &q1[i], 4); ptr += 4;
memcpy(ptr, &q2[i], 4); ptr += 4;
memcpy(ptr, &q3[i], 4); ptr += 4;
memcpy(ptr, &q4[i], 4); ptr += 4;
}
}
/* send a DROP MEMBERSHIP message via setsockopt */
if ((setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(void*) &mc_req, sizeof(mc_req))) < 0) {
perror("setsockopt() failed");
exit(1);
}
closesocket(sock);
WSACleanup(); /* Cleanup Winsock */
}
return 0;
}
and the .bat file is
#echo off
echo Opening server...
cd C:\Users\Optitrack\Desktop\Conrado\mcreceive
start mcreceive.exe 259.44.99.107 1051
Any piece of advice would be much appreciated.
thank you
I wrote a very small C code to open an UDP multicast socket, it works well on
a 32 bits platform but when I recompile my code and try it on a linux 64
bits platform it doesn't work. The program is pending undefinitely on the
recvfrom() function. I checked if the udp frames were actually received on the
specified network interface with tcpdump, but everything works fine. Does
anybody have an idea on what is wrong with my code ?
Here is the first code (before your comments) :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <math.h>
#include <errno.h>
static char* server = "231.180.0.1";
static char* network = "66.46.40.10";
static int port = 50001;
static struct sockaddr_in socketAddr;
static unsigned int socketDesc;
long toLong (unsigned char* msg, int offset);
int main (void) {
struct ip_mreq mreq;
int bindDesc, socketOptDesc;
int reuse = 1;
unsigned int socketLength = sizeof(socketAddr);
// Allocation
memset((char *) &socketAddr, 0, sizeof(socketAddr));
memset(&mreq, 0, sizeof(struct ip_mreq));
/*
* Create a datagram socket on which to receive.
*/
printf("# Init socket (server=%s network=%s port=%d)\n", server, network, port);
socketDesc = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socketDesc < 0) {
perror("socket() failed");
} else {
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
socketOptDesc = setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse));
if (socketOptDesc < 0) {
perror("setsockopt() failed");
} else {
/*
* Bind to the proper port number with the IP address
* specified as INADDR_ANY.
*/
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = htons(port);
socketAddr.sin_addr.s_addr = INADDR_ANY;
bindDesc = bind(socketDesc, (struct sockaddr*) &socketAddr, sizeof(socketAddr));
if (bindDesc < 0) {
perror("bind() failed");
} else {
/*
* Join the multicast group on the local interface.
* Note that this IP_ADD_MEMBERSHIP option must be
* called for each local interface over which the multicast
* datagrams are to be received.
*/
mreq.imr_multiaddr.s_addr = inet_addr(server);
mreq.imr_interface.s_addr = inet_addr(network);
socketOptDesc = setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
if (socketOptDesc < 0) {
perror("setsockopt() failed");
} else {
printf("# Socket created successfully !\n");
}
}
}
}
/*
* Acquisition Loop
*/
printf("# Starting reception loop...\n");
long lastFrameNumber = -1;
int nbDots = 0;
while (1) {
long frameNumber = -1;
unsigned char buffer[65536];
// Frame Acquisition
int ret = recvfrom(socketDesc, buffer, 65536, 0, (struct sockaddr *) &socketAddr, &socketLength);
if (ret < 0) {
perror("recvfrom() failed");
}
// Reading frame number
frameNumber = toLong(buffer, 28);
if (frameNumber < 0) {
// Context Frame
} else if (frameNumber == 0) {
printf("Invalid frame (frameNumber=0)\n");
} else {
if (frameNumber > 1 && frameNumber != (lastFrameNumber + 1)) {
printf("%ld frame(s) lost from frame %ld\n", frameNumber - lastFrameNumber - 1, lastFrameNumber + 1);
}
}
lastFrameNumber = frameNumber;
if (frameNumber == 1) {
if (nbDots > 50) {
printf(".\n");
nbDots = 0;
} else {
printf(".");
fflush(stdout);
}
nbDots++;
}
}
return EXIT_SUCCESS;
}
/* ======================================================================
* Read 4 bytes from the specified offset and convert it to a long value.
*
* #input msg
* Byte array representing the message.
* #input offset
* Byte offset.
* #return
* Long value representing the frame number.
* ====================================================================*/
long toLong (unsigned char* msg, int offset) {
long value;
int byte0; // bits 31..24
int byte1; // bits 23..16
int byte2; // bits 15..8
int byte3; // bits 7..0
byte0 = (0x000000FF & ((int) msg[offset + 0]));
byte1 = (0x000000FF & ((int) msg[offset + 1]));
byte2 = (0x000000FF & ((int) msg[offset + 2]));
byte3 = (0x000000FF & ((int) msg[offset + 3]));
value = ((long) (byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3)) & 0xFFFFFFFFL;
return value;
}
EDIT :
I updated my code with your comments but it doesn't work either :( Also, I forgot to say that the network is using VLAN. The network interface is eth.40 at 66.46.40.100 but it work on a 32 bit platform so it might not be the problem.
Here is the new code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <math.h>
#include <errno.h>
static char* server = "231.180.0.1";
static char* network = "66.46.40.100";
static uint16_t port = 50001;
long toLong (unsigned char* msg, int offset);
int main (void) {
struct sockaddr_in socketAddr;
struct ip_mreq mreq;
int bindDesc, socketDesc, socketOptDesc;
socklen_t reuse = 1;
socklen_t socketLength = sizeof(socketAddr);
// Allocation
memset((char *) &socketAddr, 0, sizeof(socketAddr));
memset(&mreq, 0, sizeof(struct ip_mreq));
/*
* Create a datagram socket on which to receive.
*/
printf("# Init socket (server=%s network=%s port=%d)\n", server, network, port);
socketDesc = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socketDesc < 0) {
perror("socket() failed");
} else {
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
socketOptDesc = setsockopt(socketDesc, SOL_SOCKET, SO_REUSEADDR, (void *) &reuse, sizeof(reuse));
if (socketOptDesc < 0) {
perror("setsockopt() failed");
} else {
/*
* Bind to the proper port number with the IP address
* specified as INADDR_ANY.
*/
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = htons(port);
socketAddr.sin_addr.s_addr = INADDR_ANY;
bindDesc = bind(socketDesc, (struct sockaddr*) &socketAddr, sizeof(socketAddr));
if (bindDesc < 0) {
perror("bind() failed");
} else {
/*
* Join the multicast group on the local interface.
* Note that this IP_ADD_MEMBERSHIP option must be
* called for each local interface over which the multicast
* datagrams are to be received.
*/
mreq.imr_multiaddr.s_addr = inet_addr(server);
mreq.imr_interface.s_addr = inet_addr(network);
socketOptDesc = setsockopt(socketDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq));
if (socketOptDesc < 0) {
perror("setsockopt() failed");
} else {
printf("# Socket created successfully !\n");
}
}
}
}
/*
* Acquisition Loop
*/
printf("# Starting reception loop...\n");
long lastFrameNumber = -1;
int nbDots = 0;
while (1) {
unsigned char buffer[65536];
// Frame Acquisition
ssize_t ret = recvfrom(socketDesc, buffer, 65536, 0, (struct sockaddr *) &socketAddr, &socketLength);
if (ret < 0) {
perror("recvfrom() failed");
} else {
printf("# Receiving frame\n");
}
}
return EXIT_SUCCESS;
}
One obvious 64-bit problem that you have in the code is that the last argument to recvfrom should be a pointer to a socklen_t and not unsigned int that you have in your code. socklen_t will most likely be a 64 bit variable on 64 bit machines, while unsigned int will most likely be 32 bits.
Another problem is socketDesc being unsigned. File descriptors are always signed so that you can actually detect errors from functions that return them. Your checks for errors from all the functions will not work, so it's possible that your code failed much earlier and you didn't notice.
Another possible problem is your toLong function, long is quite often 64 bits on a 64 bits platform while you're treating it as a 32 bit value.
Try building with warnings, the compiler should be quite helpful. This is definitely something that your compiler will warn you about. If that doesn't help, double check the manual pages for all the functions you call and check that the types are right.
I have to build a quiz application.
Details about the application:
1. Each client has to register to server before participating in Quiz. The server will ask
username from each user and generate temporary id for each user.
2. After Registration process clients, who are successfully connected to server will get
Question from server.
3. The client will reply with answer.
4. Server will receive answer from different clients with time stamps, and it will calculate
time difference of each client which is called ∆t.
Define such as:
∆t = (Time Question sent - Time answer received) - RTT
Where RTT is Round Trip Time
Server will select client, whose ∆t is minimum to all and reply with whatever score client will gain remains will not gain any score.
After sending Question server will wait Answer for a particular time periods called (T). If client did not reply within ‘T’ time period Server will skip that Question and goes to next Question.
Pseudocode of main loop in my server code
A. A main while loop which runs once for each question.
B. Inside this first I am accepting login for 10 seconds.
Here I am assigning user Id and all other initialization stuff.
C. Then using `select` to check which are available for writing.
To available connections I am checking `RTT` and then sending question to each user.
D. Then I am waiting for some time to get answers.
Here I am using `select` to determine where the answer is available to read.
E. Then I am repeating steps C. and D.
Problem:
When I connect only to a single client my code works fine for any number of question.
But when I test this code on multiple client with the same client code:
Login for everyone is OK.
Sending First Question to everyone works fine.
Then while waiting for the answer I only received answer from one client. Each client shows that answer is been sent. For second client the second select function doesn't return with readable data availability.
Why for multi-client my code is not working. (According to me the error is somewhere in getting answer).
My Code:
The structure of the packets send can be understand easily from the variable names.
Server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#define PORT "3490" //the port user will be connecting to
#define BACKLOG 10 //how many pending connection queue will hold
#define maxUser 10
#define LOGIN_OK "OK"
#define LOGIN_WrongPassword "NP"
#define LOGIN_WrongUsername "NU"
#define MAX_USERS 10
#define MAX_ANSWER_TIME 10
#define LOGIN_WAIT 10
#define TOTAL_QUES "3"
int users[MAX_USERS][3] = {}; //index is userID, 0 is no user
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
//get sockaddr, IPv4 or IPv6
int timer;
void alarm_handler(int s) {
timer = 0;
}
wrongRecv(ssize_t recvd, ssize_t expctd)
{
if(recvd != expctd)
{
printf("Recvd(%zd) bytes not equal to expected(%zd) bytes\n",recvd,expctd);
//getchar();
}
}
//void nextQues(char* quesMsg, char* ques, char* optA, char* optB, char* optC, char* optD)
int nextQues(char* quesMsg, int QID)
{
char ques[40], optA[10], optB[10], optC[10], optD[10], quesId[5];
sprintf(quesId,"%d",QID);
strncpy(ques, "This is the question?",22);
strncpy(optA, "OptionA", 7); strncpy(optB, "OptionB", 7); strncpy(optC, "OptionC", 7); strncpy(optD, "OptionD", 7);
strncpy(quesMsg,quesId,5);
strncpy(quesMsg + 05,ques,40);
strncpy(quesMsg + 45,optA,10);
strncpy(quesMsg + 55,optB,10);
strncpy(quesMsg + 65,optC,10);
strncpy(quesMsg + 75,optD,10);
return 0;
}
//void answerCheck(char* ques, char* optA, char* optB, char* optC, char* optD, char* usrResponse, int rtt, int timeTaken)
void answerCheck(int fd, char usrResponse[6], int rtt, int timeTaken)
{
int responseTime, i;
char actualAnswer[1];
char quesId[5];
printf("fd(%d) quesid(%s) response(%c) rtt(%d) timeTaken(%d)\n", fd, usrResponse, usrResponse[5], rtt, timeTaken );
strncpy(quesId, usrResponse, 5);
actualAnswer[0] = 'B';//we have quesId we can find actual answer on basis of it
if(actualAnswer[0] == usrResponse[5])
{
//printf("%s\n","+++++" );
responseTime = timeTaken - rtt;
//printf("Response Time(%d)\n",responseTime);
//save it with user id
//finding userid
for(i = 0; i < MAX_USERS; i++) {
if(users[i][1] == fd) {
users[i][2] = responseTime;//saving it
//printf("%d\n",i );
}
}
}
}
int compareAnswer() {
int i, min = 2 * MAX_ANSWER_TIME, userIndex;
for(i = 0; i < MAX_USERS; i++) {
if(users[i][2] < min) {
min = users[i][2];
userIndex = i;
}
}
//Increasing Score
users[userIndex][0]++;
//returning fd
return users[userIndex][1];
}
void users_deleteFd(int fd) {
int i;
for (i = 0; i < MAX_USERS; ++i)
{
if(users[i][1] == fd) {
users[i][1] =0;
return;
}
}
}
int rtt_check(int new_fd)
{
ssize_t send_ret, recv_ret;
char rtt_check[1];
time_t rtt1, rtt2;
rtt1 = time(NULL);
send_ret = send(new_fd, "r", 1, 0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret, 1);
//printf("%s\n","Between two phase of rttCheck" );
recv_ret = recv(new_fd, rtt_check, 1,0);
rtt2 = time(NULL);
if(recv_ret == 0)
{
return -2;
}
wrongRecv(recv_ret,1);
//printf("diff(%d)\n",(int) difftime(rtt2,rtt1));
return (int) difftime(rtt2,rtt1);
}
int login(char user[], char pass[])
{
//for user
static int Id = 0; //when have function getUserID, make it not static and also remove Id++;
if(!strcmp(user,"abhishek") && !strcmp(pass,"abhishek")) {
//Id = getUserID(user);
return ++Id;
}else if(!strcmp(user,"abhishek")){
return 0; //wrong password
}
return -1; //wrong username
}
int totalQues;
int login_setup(int new_fd)
{
//login inititalizations
char login_det[16];
char username[9],password[9], login_statMsg[7], totalQuesMsg[5] = TOTAL_QUES;
totalQues = atoi(totalQuesMsg);
//for user
int userId;
//for wrongRecv
ssize_t send_ret,recv_ret;
//getting username and password
recv_ret = recv(new_fd,login_det,16,0);
if(recv_ret == 0)
{
return -2;
}
wrongRecv(recv_ret,16);
//extracting username nad password
strncpy(username,login_det,8);
strncpy(password,login_det+8,8);
username[8]='\0'; password[8]='\0';
//printf("username(%s) and password(%s)\n",username,password);
if( (userId = login(username,password)) > 0) {
//printf("%d\n",userId);
//sending status
strncpy(login_statMsg, LOGIN_OK, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
//TODO error checking then handling if error
//users[userId][0] = 0; //score
users[userId][1] = new_fd; //file descriptor associated with this user
//users[userId][2] = 0; //answer time
return 1;
}
else if(userId == -1) { //wrong username
strncpy(login_statMsg, LOGIN_WrongUsername, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
return 0;
}
else{
strncpy(login_statMsg, LOGIN_WrongPassword, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
return 0;
}
//TODO erorr handling of above two case
//TODO make login a loop
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int listen_fd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;//connection's address info
socklen_t sin_size;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;//IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if((rv = getaddrinfo(NULL,PORT, &hints, &servinfo)) != 0){ //getting which IPv server supports
fprintf(stderr, "getaddrinfo: %s\n",gai_strerror(rv));
return 1;
}
//loop through all the result and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next){
if((listen_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server : socket");
continue;
}
if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("set sockopt");
exit(1);
}
if(bind(listen_fd, p->ai_addr, p->ai_addrlen) == -1){
close(listen_fd);
perror("server: bind");
continue;
}
break;
}
if(p == NULL) {
fprintf(stderr, "server:failed to bind\n");
return 2;
}
freeaddrinfo(servinfo);//all done with this structure
if(listen(listen_fd, BACKLOG) == -1){
perror("listen");
exit(1);
}
//printf("listen_fd(%d)\n",listen_fd );
// sa.sa_handler = sigchld_handler; // reap all dead processes
// sigemptyset(&sa.sa_mask);
// sa.sa_flags = SA_RESTART;
// if(sigaction(SIGCHLD, &sa, NULL) == -1){
// perror("sigaction");
// exit(1);
// }
printf("server waiting for connections.....\n");
fd_set master; //master file descriptor list
fd_set read_fds; //temp file descriptor list for select()
int fdmax;
FD_ZERO(&master); //clear the master and temp sets
FD_ZERO(&read_fds);
FD_SET(listen_fd, &master);
//keep track of the bigge file descriptor
fdmax = listen_fd; // so far it is this one
ssize_t recv_ret, send_ret;
//for login
int loginStatus;
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
//sa.sa_flags = SA_RESTART;
if(sigaction(SIGALRM, &sa, NULL) == -1){
perror("sigaction");
exit(1);
}
//login while
alarm(LOGIN_WAIT);//accepting login only for 10 seconds
timer = 1;
printf("\n-----------------------------Waiting for users to login for %d seconds.-----------------------------\n",LOGIN_WAIT);
while(timer) {
sin_size = sizeof their_addr;
new_fd = accept(listen_fd, (struct sockaddr *)&their_addr, &sin_size);
if(new_fd == -1){
//perror("accept");
break;// this break is very important , as we are using alarm(Signals) and accept is a blocking function
//If accept is in blocked sate and our signal comes then accept will exit returning error. So
//if error then we have to break else next satements will run on falsy values.
//In reality we dont need this as I alredy set the SA_RESTART flag in sigaction which means
//after returning from the signal handler restart the activity on which you are previously
//instead of starting execution from next line.
}else {
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("server : got connection from %s\n", s);
//LOGIN //need to call login function via thread because this
//may stop the function if user doesnot respond
loginStatus = login_setup(new_fd);
//adding to select checkup
if(loginStatus) {
printf("User Loginned Succesfully\n");
}
}
}
printf("-----------------------------Login Closed. Now starting the QUIZ.-----------------------------\n");
//for randome seek
srand(time(NULL));
//for main loop counter
int i, win_fd;
//for questions
int QID = 0;
int maxQues_Len = 40, maxOpt_len = 10, maxQuesId_len = 5;//including '\0' this time
char quesMsg[80], answer[6];//score doesnot include \0
//char ques[40], optA[10], optB[10], optC[10], optD[10];
//for time calculation of each answer
ssize_t time_ques, time_ans;
//getting all avialable participants
fdmax = 0;
FD_ZERO(&master);
for(i = 0; i < MAX_USERS; i++) {
if( (new_fd = users[i][1]) != 0){
FD_SET(new_fd, &master);
if(new_fd > fdmax)
fdmax = new_fd;
//printf("%d\n",new_fd);
}
}
int current_rtt;
//while for main quiz
while(totalQues--) {
//checking who are ready for witing
if(select(fdmax+1, NULL, &master, NULL, NULL) == -1){//here select will return withh all the descriptors which are
//ready to write , all others have to miss this question
perror("select");
exit(1);
}
//setting which question to send
QID++;
//for sending questions to all
for(i = 0; i <= fdmax; i++) {
if(FD_ISSET(i, &master)) {
//rtt check
current_rtt = rtt_check(i);
if(current_rtt == -2) {//connection closed
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}
//setting question
//nextQues(quesMsg, ques, optA, optB, optC, optD);
nextQues(quesMsg, QID);
printf("Sending Question QID(%s) fd(%d)\n",quesMsg,i);
//send a question
time_ques = time(NULL);
send_ret = send(i, quesMsg, maxQues_Len + 4 * maxOpt_len + maxQuesId_len, 0);
if(send_ret == 0) {//connection closed
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}
wrongRecv(send_ret, maxQues_Len + 4 * maxOpt_len + maxQuesId_len);
}
}
//ASSUMING Question is send ot all the users at same time
//receiving and waiting for answers
alarm(MAX_ANSWER_TIME);
timer = 1;
FD_ZERO(&read_fds);
read_fds = master;
// unsigned int qq = read_fds.fd_count;
// for (int ii = 0; ii < qq; ++ii)
// {
// printf("%d\n",read_fds.fd_array[i] );
// }
while(timer) {
//printf("HURRAY\n");
if(select(fdmax+1, &read_fds, NULL, NULL, NULL) <=0){
perror("select");
//exit(4);
break;//break is important. Explained above
}
for(i = 0; i <= fdmax; i++) {
//printf("Recving answer I(%d)\n",i);
if(FD_ISSET(i, &read_fds)) {
//receiving answer
//TODO if we get answer to wrong ques
printf("Recving answer I(%d) fdmax (%d)\n",i,fdmax);
recv_ret = recv(i,answer,6,0);
time_ans = time(NULL);
wrongRecv(recv_ret,6);
printf("%s\n",answer );
if(recv_ret == 0)//connection closed
{
FD_CLR(i, &read_fds);
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}else if(recv_ret > 0){
if(QID == atoi(answer)) { //we have received the answer to this question so remove the user from wait answer loop
FD_CLR(i, &read_fds);
//printf("%s i(%d)\n","#######",i );
answerCheck(i ,answer, current_rtt, (int) difftime(time_ans,time_ques));
//printf("Answer(%c)\n",answer[0]);
}
else{//we have recvd something unexpectable so ignore for NOW
}
}
//time_t cccc = time(NULL);
//printf("%s I(%d)\n",ctime(&cccc),i);
}
}
}
//comparing answers
win_fd = compareAnswer();
//sending score
}
return 0;
}
Client.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT "3490" //the port client will be connecting to
#define MAXDATASIZE 100 // max number of bytes we can get at once
//get sockaddr ,IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if(sa->sa_family ==AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
wrongRecv(ssize_t recvd, ssize_t expctd)
{
if(recvd != expctd)
{
printf("Recvd(%zd) bytes not equal to expected(%zd) bytes\n",recvd,expctd);
getchar();
}
}
void rtt_check(int sockfd)
{
ssize_t send_ret, recv_ret;
char rtt_check[1];
recv_ret = recv(sockfd, rtt_check, 1,0);
wrongRecv(recv_ret,1);
sleep(1);//to check
send_ret = send(sockfd, "r", 1, 0);
wrongRecv(send_ret, 1);
return;
}
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if(argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(rv));
return 1;
}
//lopp through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("client: socket");
continue;
}
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if(p ==NULL) {
fprintf(stderr,"client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
printf("client : connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
char login_det[17] = "abhishekabhishek";
char login_retMsg[7], login_stat[3], totalQuesMsg[5];
int totalQues;
//sending login details
ssize_t send_ret,recv_ret;
send_ret = send(sockfd, login_det,16,0);
wrongRecv(send_ret,16);
//receiving login status
recv_ret = recv(sockfd,login_retMsg,7,0);
wrongRecv(recv_ret,7);
strncpy(login_stat, login_retMsg, 2);
login_stat[2] = '\0';
printf("Login Status(%s)\n",login_stat);
strncpy(totalQuesMsg, login_retMsg + 2, 5);
totalQues = atoi(totalQuesMsg);
printf("totalQues(%d)\n",totalQues);
if(!strcmp(login_stat,"OK")) { //login ok
char quesId[5];
int maxQues_Len = 40, maxOpt_len = 10, maxQuesId_len = 5;//including '\0' this time
char quesMsg[80], scoreMsg[1];//score doesnot include \0
char ques[40], optA[10], optB[10], optC[10], optD[10];
char answer[6];
while(totalQues--) {
//checking rtt
rtt_check(sockfd);
//receving question
recv_ret = recv(sockfd, quesMsg, maxQues_Len + 4 * maxOpt_len + maxQuesId_len ,0);
wrongRecv(recv_ret, maxQues_Len + 4 * maxOpt_len + maxQuesId_len);
strncpy(quesId,quesMsg,5);
strncpy(ques, quesMsg + 05, 40);
strncpy(optA, quesMsg + 45, 10);
strncpy(optB, quesMsg + 55, 10);
strncpy(optC, quesMsg + 65, 10);
strncpy(optD, quesMsg + 75, 10);
printf("QUESID(%s) Question(%s), A(%s) , B(%s) , C(%s) , D(%s)\n", quesId, ques, optA, optB, optC, optD);
//choose answer
scoreMsg[0] = 'B';
strncpy(answer,quesId, 5);
answer[5] = scoreMsg[0];
sleep(5);
//sending answer
send_ret = send(sockfd, answer,6,0);
wrongRecv(send_ret,6);
printf("%s\n","Answer Message Sent" );
// if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
// perror("recv");
// exit(1);
// }
// buf[numbytes] = '\0';
// printf("client: received '%s'\n",buf);
}
}
//TODO wrong login
close(sockfd);
return 0;
}
The problem is that the call to select in the answer getting loop is modifying read_fds to hold just the file descriptor of the first client(s) to respond. Since you don't reset read_fds before calling select again, it will not recognize the other clients' response.
The program is supposed to accept telnet connections on port 8888 and then send and messages from each telnet client using poll(), send() and recv() but it doesn't quite work 100%. It seems certain connections can always send messages to anyone and the program works fine but there is always at least one client who cannot send messages. All clients can always receive (poll doesn't register incoming data).
This code runs on it's own so if you put it in a file and compile it with gcc -o app filename.c then you can telnet to localhost on port 8888 and see it not working. This code was written for Fedora but shouldn't have anything non-Linux specific in it. Any help would really be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define PORT 8888
#define MAX_CONN 10
#define SECOND 1000
#define TIMEOUT (30 * SECOND)
static int listen_socket();
int main(int argc, char **argv)
{
struct pollfd **my_fds; //array of pollfd structures for poll()
struct pollfd *curr, *new_conn; //so I can loop through
int num_fds; //count of how many are being used
int i, j; //for loops
char buff[255], buff2[255]; //for sending and recieving text
struct sockaddr_in my_addr, their_addr; // my address information
socklen_t sin_size;
int buff_sz; //size of data recieved
printf("App Started\n");
//allocate space for 10
my_fds = (struct pollfd**) malloc(sizeof(struct pollfd*) * MAX_CONN);
//set all the pointers to NULL
for (i = 0; i < MAX_CONN; i++)
*(my_fds + i) = NULL;
//I call listen_socket() which creates a socket to listen to
//this is anchored into my_fds array at element 0.
curr = (struct pollfd*) malloc (sizeof(struct pollfd));
curr->fd = listen_socket();
curr->events = POLLIN;
curr->revents = 0;
*my_fds = curr;
printf("Listening socket fd locked always at position zero in array: %d\n", curr->fd);
//num_fds, the count of items in the array is set to 1
//because the listen socket is already present
num_fds = 1;
//This is the main loop.
//While (true)
// set all struct pollfd items revents to 0
// call poll
// loop through, see if there is data to read
// read the data
// loop through all sockets (except the listen_socket()) and send the data.
while (1)
{
//reset all event flag
for (i = 1; i < num_fds; i++)
{
curr = *(my_fds + i);
curr->events = POLLIN | POLLPRI;
printf("%i: fd %i\n", i, curr->fd);
curr->revents = 0;
send(curr->fd, "Enter some text:\n", 18, 0);
}
//put all this into poll and wait for something magical to happen
printf("calling poll (%d sockets)\n", num_fds);
if (poll(*my_fds, num_fds, TIMEOUT) == -1)
{
perror("poll");
exit(0);
}
printf("poll returned!\n");
//First item is the accepting socket....check it independently of the rest!
curr = *my_fds;
if (curr->revents != 0)
{
printf("We have a new connection.\nAccept goes here...\n");
//Accept the connection
sin_size = sizeof their_addr;
new_conn = (struct pollfd*) malloc(sizeof(struct pollfd));
new_conn->fd = accept(curr->fd, (struct sockaddr *)&their_addr, &sin_size);
new_conn->events = POLLIN;
new_conn->revents = 0;
printf("Connection from %s\n", inet_ntoa(their_addr.sin_addr));
sprintf(buff, "Your %i\n", num_fds);
send(new_conn->fd, buff, 7, 0);
//Add it to the poll call
*(my_fds + num_fds) = new_conn;
num_fds++;
}
else
{
//skip first one, we know that's the accepting socket (handled above).
for (i = 1; i < num_fds; i++)
{
curr = *(my_fds + i);
if (curr->revents != 0)
{
buff_sz = recv(curr->fd, &buff, 254, 0);
buff[buff_sz] = '\0';
printf("Recieved: %s", buff);
//send the message to everyone else
for (j = 1; j < num_fds; j++)
{
printf("i = %i, j = %i\n", i, j);
if (j != i)
{
new_conn = *(my_fds + j);
sprintf(buff2, "%i sent you %i: %s", i, j, buff);
send(new_conn->fd, buff2, strlen(buff2) + 1, 0);
}
}
}
}
}
}
printf("App Ended\n");
}
static int listen_socket()
{
struct sockaddr_in a;
int s;
int yes;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return -1;
}
yes = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &yes, sizeof(yes)) < 0) {
perror("setsockopt");
close(s);
return -1;
}
memset(&a, 0, sizeof(a));
a.sin_port = htons(PORT);
a.sin_family = AF_INET;
if (bind(s, (struct sockaddr *) &a, sizeof(a)) < 0) {
perror("bind");
close(s);
return -1;
}
printf("Accepting connections on port %d\n", PORT);
listen(s, 10);
return s;
}
Isn't poll supposed to take an array of structures? From the man page:
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
And you're passing an array of pointers to structures:
struct pollfd **my_fds;
I think you need to have
struct pollfd *my_fds = calloc(sizeof(pollfd), MAX_CONN);
Then your code has a chance to work.