Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I downloaded a C++ program from Github which does Bandwidth estimation using Pair to pair algorithm. I've compiled and installed the program on my system but when I initialize the Bind() function to bind to port 53 for e.g, it says either Bind() failed, or an infinite "Waiting for Client". I'm not very experienced with socket programming and using BIND. Are there some rules in play?
Here is the program source if needed:
https://github.com/npbendre/Bandwidth-Estimation-using-Packet-Pair-Probing-Algorithm
/********************************************************
* Program: Bandwidth Estimation *
* *
* Summary: *
* This file contains the server code *
* *
*********************************************************/
#include "defs.h"
int quit = 0; /* CNT-C exit flag */
#define RESET 0
#define HEX 16
#define MSZ_BEGIN 5
#define SEQ_BEGIN 5
#define BSZ_BEGIN 9
#define MAX_WAIT 30
#define HEADER_SIZE 28
#define TO_Mbits(x) (x*8/1048576)
struct Client {
int cmd;
char sessionID[ID_LEN+1];
unsigned int msgSize;
unsigned int seq;
unsigned int burstSize;
unsigned int burstCnt;
unsigned int numRecv;
double bandwidth;
};
/* SIGINT handler */
void CNT_C_Code()
{
printf("CNT-C Interrupt, exiting...\n");
quit = 1;
}
/* SIGALRM handler */
void alarmHandler() { }
int main(int argc, char *argv[])
{
int sock; /* Socket */
struct sockaddr_in serverAddr; /* Local address */
struct sockaddr_in clientAddr; /* Client address */
struct sockaddr_in dupClAddr; /* Temporary address */
unsigned int addrLen; /* Client address length */
unsigned int serverPort; /* Local port */
struct sigaction sa_int; /* For SIGINT */
struct sigaction sa_alrm; /* For SIGALRM */
int cmd = RESET; /* Server commands */
char buffer[MIN_MSG_LEN]; /* Receive buffer */
char results[RESULT_LEN]; /* Results buffer */
char *msg = NULL; /* Client message */
struct Client cl; /* Current client */
int recvlen; /* Received message length */
int seq; /* Sequence number */
struct timespec ts1; /* time structure */
struct timespec ts2; /* time structure */
struct timespec *ts = NULL; /* Switch pointer */
int swtch = 0; /* Switch flag for timespecs */
int cnt = 0;
char temp[ID_LEN+1];
/* Invalid invocation of the program */
if (argc != 2) {
printf("Syntax: %s <port number>\n", argv[0]);
exit(1);
}
serverPort = atoi(argv[1]);
/* create datagram socket */
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
DieWithError("socket() failed");
/* build local address struct */
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(serverPort);
/* bind to local address */
printf("Server: binding to port %d\n", serverPort);
if (bind(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) {
close(sock);
DieWithError("bind() failed");
}
/* assign handler and initialize set to all signals */
sa_int.sa_handler = CNT_C_Code;
sa_alrm.sa_handler = alarmHandler;
if (sigfillset(&sa_int.sa_mask) < 0 || sigfillset(&sa_alrm.sa_mask) < 0) {
close(sock);
DieWithError("sigfillset() failed");
}
/* set the handler */
sa_int.sa_flags = sa_alrm.sa_flags = 0;
if (sigaction(SIGINT, &sa_int, 0) < 0 || sigaction(SIGALRM, &sa_alrm, 0) < 0) {
close(sock);
DieWithError("sigaction() failed");
}
addrLen = sizeof(struct sockaddr_in);
/* Run until CNT-C */
for ( ; !quit; ) {
if (cmd == RESET) {
/* reset session id */
memset(&cl, 0, sizeof(cl));
errno = swtch = cnt = 0;
ts = &ts1;
printf("Waiting for Client...\n");
/* get the first message from client */
recvfrom(sock, buffer, MIN_MSG_LEN, 0, (struct sockaddr *) &clientAddr, &addrLen);
if (errno == EINTR) continue;
memcpy(&dupClAddr, &clientAddr, addrLen);
cl.cmd = buffer[0]-'0'; /* get the client command */
}
/* Received START_MSG, send START_ACK */
if (cl.cmd == START_MSG) {
getFromMsg(cl.sessionID, buffer, SID_BEGIN, ID_LEN); /* retrieve session ID */
getFromMsg(temp, buffer, MSZ_BEGIN, ID_LEN); /* retrieve message size */
cl.msgSize = strtol(temp, NULL, HEX);
getFromMsg(temp, buffer, BSZ_BEGIN, ID_LEN); /* retrieve burst size */
cl.burstSize = strtol(temp, NULL, HEX);
if (!msg) msg = (char *) calloc(cl.msgSize+1, sizeof(char));
printf("\n");
printf("Serving client with session ID: %s\n", cl.sessionID);
/* set sending buffer to START_ACK */
cmd = START_ACK;
*msg = cmd + '0';
strcpy(msg+SID_BEGIN, cl.sessionID);
/* send START_ACK to client */
printf("=== Received START_MSG, sending START_ACK...\n");
sendto(sock, msg, strlen(msg)+1, 0, (struct sockaddr *) &clientAddr, addrLen);
}
if (cmd == START_ACK) {
int once = 1;
/* accept client bursts */
for ( ; ; ) {
alarm(MAX_WAIT); /* wait till client sends next message, used when client crashes */
recvlen = recvfrom(sock, msg, cl.msgSize+1, 0, (struct sockaddr*) &dupClAddr, &addrLen);
clock_gettime(CLOCK_REALTIME, ts); /* get time */
if (errno == EINTR) {
if (!quit) {
free(msg);
msg = NULL;
cl.cmd = cmd = RESET;
printf("Server Timeout: Resetting...\n\n");
}
break;
}
alarm(0);
if (recvlen != cl.msgSize+1) /* damaged packet */
continue;
/* wrong client */
if (memcmp(&dupClAddr, &clientAddr, addrLen) || strncmp(cl.sessionID, msg+SID_BEGIN, ID_LEN))
break;
if ((cl.cmd = *msg-'0') == START_MSG) /* client did not receive START_ACK */
break;
if (cl.cmd == DONE) { /* Client done bursting */
cmd = RESULTS_MSG;
break;
}
++cl.numRecv;
ts = ((swtch = 1-swtch)) ? &ts2 : &ts1; /* switch time pointer */
getFromMsg(temp, msg, SEQ_BEGIN, ID_LEN); /* get sequence number */
seq = strtol(temp, NULL, HEX);
/* change in sequence */
if (seq != cl.seq) {
once = 1;
cl.seq = seq;
ts = &ts1;
swtch = 0;
++cl.burstCnt;
printf("=== Receiving from burst #%d...\n", cl.seq);
}
else {
/* get time interval only if its from the same burst sequence */
if (once) {
once = 0;
continue;
}
/* calculate average bandwidth */
cl.bandwidth *= cnt++;
cl.bandwidth += ((double) cl.msgSize+1+HEADER_SIZE) /
((swtch) ? getTimeDiff(ts1, ts2) : getTimeDiff(ts2, ts1));
cl.bandwidth /= (double) cnt;
}
/* not retrieving data from message */
}
}
if (cmd == RESULTS_MSG) {
float totExpected = cl.burstSize * cl.burstCnt;
/* create results */
results[0] = cmd + '0';
sprintf(results+SID_BEGIN, "%s:%f:%.2f:%u:%u", cl.sessionID, TO_Mbits(cl.bandwidth),
((totExpected-cl.numRecv)/totExpected)*100, cl.numRecv, cl.burstCnt);
printf("\n");
printf("=== Received DONE, sending RESULTS_MSG...\n");
sendto(sock, results, RESULT_LEN, 0, (struct sockaddr *) &dupClAddr, addrLen);
alarm(MAX_WAIT);
recvfrom(sock, msg, cl.msgSize+1, 0, (struct sockaddr *) &dupClAddr, &addrLen);
if (errno == EINTR) {
if (!quit) {
free(msg);
msg = NULL;
cmd = RESET;
printf("Server Timeout: Resetting...\n\n");
}
continue;
}
alarm(0);
/* wrong client */
if (memcmp(&dupClAddr, &clientAddr, addrLen) || strncmp(cl.sessionID, msg+SID_BEGIN, ID_LEN))
continue;
if (*msg-'0' == DONE) /* client did not receive RESULTS_MSG */
continue;
if (*msg-'0' == DONE_ACK) { /* received DONE_ACK from client */
printf("=== Received DONE_ACK.\n\n");
free(msg);
msg = NULL;
cmd = RESET;
}
}
}
if (msg) free(msg);
close(sock);
return 0;
}
Try port number greater than 1024 - maybe OS is blocking binding on lower number ports for non-privileged applications.
Related
So I'm writing a very basic TCP server which just echoes back the messages sent from the client back to the client. I have a setup where the server is running in a while loop and waiting for either a new client to connect or for a message from one of the existing clients using the select() method.
My question is:
How do i, from the server side, close the master socket and basically shutting down the server. Im not asking for exact code but more for standard practice.
For some context:
In the long run I am imagining multiple clients connected to my server and I need to shutdown the server gracefully from the server side.
Even more context: The server code.
#define TRUE 1
#define FALSE 0
#define PORT 55555
int main(int argc, char* argv[])
{
int opt = TRUE;
int masterSocket, addrlen, newSocket, maxClients = 10, clientSockets[maxClients],
activity, valread, sd, maxSd;
struct sockaddr_in address;
char buffer[1025];
fd_set readfds;
const char *message = "ECHO DAMON v1.0 \r\n";
/* Initialize array with 0 so it's not read */
for(int i = 0; i < maxClients ; i++)
{
clientSockets[i] = 0;
}
/* Create master socket */
if((masterSocket = socket(AF_INET, SOCK_STREAM,0)) == 0)
{
perror("Error when trying to create master socket.\n");
exit(EXIT_FAILURE);
}
/* Set master socket to allow multiple connections */
if( setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
{
perror("Could not set sockopt");
exit(EXIT_FAILURE);
}
/* Socket type */
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if( bind(masterSocket, (struct sockaddr*)&address, sizeof(address)) < 0)
{
perror("Error, could not bind master socket. \n");
exit(EXIT_FAILURE);
}
printf("Listening on %d. \n", PORT);
if( listen(masterSocket, 3) < 0)
{
perror("Error, could not listen.\n");
exit(EXIT_FAILURE);
}
addrlen = sizeof(address);
puts("Waiting for connections...\n"); //just a printf variant
/* END INIT */
while(TRUE)
{
/* Clear socket set */
FD_ZERO(&readfds);
/* Add master socket to set */
FD_SET(masterSocket, &readfds);
/* Add child sockets to set, will be 0 first iteration */
for(int i = 0; i < maxClients ; i++)
{
sd = clientSockets[i]; // sd = socket descriptor
/* If valid socket descriptor */
if(sd > 0)
{
FD_SET(sd, &readfds);
}
/* Get highest fd number, needed for the select function (later) */
if(sd > maxSd)
{
maxSd = sd;
}
}//end for-loop
/* Wait for activity on any socket */
activity = select(maxSd +1, &readfds, NULL, NULL, NULL);
if((activity < 0) && (errno != EINTR))
{
printf("Error on select.\n"); //no need for exit.
}
/* If the bit for the file descriptor fd is set in the
file descriptor set pointed to by fdset */
/* If something happend in the master socket, its a new connection */
if(FD_ISSET(masterSocket, &readfds))
{
if((newSocket = accept(masterSocket, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0)
{
perror("Could not accept new socket.\n");
exit(EXIT_FAILURE);
}
/* Print info about connector */
printf("New connection, socket fd is %d, ip is: %s, port: %d\n", newSocket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
if( send(newSocket, message, strlen(message), 0) != strlen(message))
{
perror("Could not sent welcome message to new socket.\n");
}
puts("Welcome message sen successfully.\n");
/* Add new socket to array of clients */
for(int i = 0; i < maxClients; i++)
{
if(clientSockets[i] == 0)
{
clientSockets[i] = newSocket;
printf("Adding socket to list of client at index %d\n", i);
break;
}
}
}//end masterSocket if
/* Else something happend at client side */
for(int i = 0; i < maxClients; i++)
{
sd = clientSockets[i];
if(FD_ISSET(sd, &readfds))
{ /* Read socket, if it was closing, else read value */
if((valread = read( sd, buffer, 1024)) == 0)
{
getpeername( sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
printf("Host disconnected, ip %s, port %d.\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));
close(sd);
clientSockets[i] = 0;
}
}
else
{
buffer[valread] = '\0';
send(sd, buffer, strlen(buffer), 0);
}
}
}
return 0;
}
If you are planning to exit your application gracefully by adding a breakstatement in your app by something similar to this:
if (exit_condition)
{
break;
}
You can place this loop at the end of your main function:
/* close connections gracefully */
closesocket(masterSocket);
masterSocket = 0; /* optional, see comment below */
for(int i = 0; i < maxClients; i++)
{
if (clientSockets[i] != 0)
{
shutdown(clientSockets[i]);
closesocket(clientSockets[i]);
clientSockets[i] = 0; /* optional, except if you also have a SIGINT handler */
}
}
If you want to do the same to handle an exit with Ctrl-C, you will find details on how to setup a SIGINT handler to handle Ctr-C here:
Catch Ctrl-C in C. Place the above loop in your handler, then. Your sockets-related variables will have to be declared at global scope, since your sockets are only visible from main() in your current code.
Based on the examples at http://simplestcodings.blogspot.com/2010/08/secure-server-client-using-openssl-in-c.html I have written the following epoll server code to replace the regular tcp sockets with SSL. My code is slightly modified to do mutual authentication and verify the certs since I add
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,"/home/test/ssl/cert_files/cacert.pem",NULL);.
My OS is Ubuntu 12.04.
I have also generated server cert, client cert, server key, client key, and of course cacert.pem files from the IBM tutorial http://pic.dhe.ibm.com/infocenter/lnxinfo/v3r0m0/index.jsp?topic=%2Fliaat%2Fliaatseccreatecskeycert.htm
I ran the single server - single client code and it runs perfectly fine. Inspired by this I wanted to see how the epoll server code would behave but then I ran into issues. Here is the code for the server
Code that does not work
//(c) 2014 enthusiasticgeek for stackoverflow - epollserver.cc
//============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#include <iostream>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#define FAIL -1
#define MAXEVENTS 64
SSL_CTX* InitServerCTX(void)
{ const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
method = SSLv23_server_method(); /* create new server-method instance */
ctx = SSL_CTX_new(method); /* create new context from method */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,"/home/test/ssl/cert_files/cacert.pem",NULL);
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
static int
AibSocketNonBlocking (int sfd)
{
int flags, s;
flags = fcntl (sfd, F_GETFL, 0);
if (flags == -1)
{
perror ("fcntl");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl (sfd, F_SETFL, flags);
if (s == -1)
{
perror ("fcntl");
return -1;
}
return 0;
}
static int
AibCreateAndBind (char *port)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int s, sfd;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */
hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */
hints.ai_flags = AI_PASSIVE; /* All interfaces */
s = getaddrinfo (NULL, port, &hints, &result);
if (s != 0)
{
fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
s = bind (sfd, rp->ai_addr, rp->ai_addrlen);
if (s == 0)
{
/* We managed to bind successfully! */
break;
}
close (sfd);
}
if (rp == NULL)
{
fprintf (stderr, "Could not bind\n");
return -1;
}
freeaddrinfo (result);
return sfd;
}
int
main (int argc, char *argv[])
{
SSL_CTX *ctx;
SSL_library_init();
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "/home/test/ssl/cert_files/servercert.pem", "/home/test/ssl/cert_files/serverkey.pem"); /* load certs */
int sfd, s;
int efd;
struct epoll_event aibevent;
struct epoll_event *aibevents;
if (argc != 2) {
fprintf (stderr, "Usage: %s [port]\n", argv[0]);
exit (EXIT_FAILURE);
}
char portt[sizeof (unsigned int)];
snprintf(portt,sizeof portt + 1,"%u",atoi(argv[1]));
printf("sizeof %s argv[1] = %d\n",argv[1], sizeof(argv[1]));
printf("sizeof %s portt = %d\n",portt, sizeof(portt));
sfd = AibCreateAndBind (portt);//argv[1]);
if (sfd == -1) {
abort ();
}
s = AibSocketNonBlocking (sfd);
if (s == -1) {
abort ();
}
s = listen (sfd, SOMAXCONN);
if (s == -1) {
perror ("listen");
abort ();
}
efd = epoll_create1 (0);
if (efd == -1) {
perror ("epoll_create");
abort ();
}
aibevent.data.fd = sfd;
aibevent.events = EPOLLIN | EPOLLET;
s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &aibevent);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
// Buffer where events are returned
//events = static_cast<epoll_event*>(calloc (MAXEVENTS, sizeof event));
//aibevents = static_cast<epoll_event*>(malloc (MAXEVENTS * sizeof aibevent));
aibevents = new epoll_event[MAXEVENTS * sizeof aibevent];
// The event loop
while (true)
{
int n, i;
n = epoll_wait (efd, aibevents, MAXEVENTS, -1);
for (i = 0; i < n; i++)
{
if ((aibevents[i].events & EPOLLERR) ||
(aibevents[i].events & EPOLLHUP) ||
(!(aibevents[i].events & EPOLLIN)))
{
// An error has occured on this fd, or the socket is not
// ready for reading (why were we notified then?)
fprintf (stderr, "epoll error\n");
close (aibevents[i].data.fd);
continue;
} else if (sfd == aibevents[i].data.fd) {
// We have a notification on the listening socket, which
// means one or more incoming connections.
while (1)
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept (sfd, &in_addr, &in_len);
if (infd == -1)
{
if ((errno == EAGAIN) ||(errno == EWOULDBLOCK)) {
// We have processed all incoming
// connections.
break;
} else {
perror ("accept");
break;
}
}
s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV);
if (s == 0) {
printf("Accepted connection on descriptor %d "
"(host=%s, port=%s)\n", infd, hbuf, sbuf);
}
// Make the incoming socket non-blocking and add it to the
// list of fds to monitor.
s = AibSocketNonBlocking (infd);
if (s == -1) {
abort ();
}
aibevent.data.fd = infd;
aibevent.events = EPOLLIN | EPOLLET;
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &aibevent);
if (s == -1) {
perror ("epoll_ctl");
abort ();
}
}
continue;
} else {
// We have data on the fd waiting to be read. Read and
// display it. We must read whatever data is available
// completely, as we are running in edge-triggered mode
// and won't get a notification again for the same
// data.
int done = 0;
int sd;
SSL *ssl;
while (1)
{
ssize_t count;
char buf[1024];
char reply[1024];
printf("Performing exchange.\n");
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, aibevents[i].data.fd); /* set connection socket to SSL state */
printf("Performing exchange 1.\n");
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
if ( SSL_accept(ssl) == FAIL ) { /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
printf("Performing exchange Error 1.\n");
done = 1;
break;
} else {
ShowCerts(ssl); /* get any certificates */
count = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( count > 0 )
{
buf[count] = 0;
printf("Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); /* construct reply */
SSL_write(ssl, reply, strlen(reply)); /* send reply */
} else {
ERR_print_errors_fp(stderr);
printf("Performing exchange Error 2.\n");
done = 1;
break;
}
}
sd = SSL_get_fd(ssl); /* get socket connection */
/*
count = read (aibevents[i].data.fd, buf, sizeof buf);
if (count == -1)
{
// If errno == EAGAIN, that means we have read all
// data. So go back to the main loop.
if (errno != EAGAIN)
{
perror ("read");
done = 1;
}
break;
}
else if (count == 0)
{
// End of file. The remote has closed the
// connection.
done = 1;
break;
}
// Write the buffer to standard output
s = write (1, buf, count);
if (s == -1)
{
perror ("write");
abort ();
}
printf(" read correctly (n > 0) n==%d\n",s);
printf("msg: %s\n", buf);
write(aibevents[i].data.fd, buf,s);
memset(buf,'\0', s);
*/
}
if (done)
{
printf("Freeing data.\n");
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
//printf ("Closed connection on descriptor %d\n",
// aibevents[i].data.fd);
// Closing the descriptor will make epoll remove it
// from the set of descriptors which are monitored.
//close (aibevents[i].data.fd);
}
}
}
}
//free (aibevents);
delete[] aibevents;
close (sfd);
SSL_CTX_free(ctx); /* release context */
return EXIT_SUCCESS;
}
The single server- single client code are listed below for reference. Note that I am using the same client code to interact with the epoll server.
Single server code. Works
//SSL-Single Server code that works ! used as a reference for epoll server
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#define FAIL -1
int OpenListener(int port)
{ int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("can't bind port");
abort();
}
if ( listen(sd, 10) != 0 )
{
perror("Can't configure listening port");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void)
{ const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
method = SSLv23_server_method(); /* create new server-method instance */
ctx = SSL_CTX_new(method); /* create new context from method */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,"/home/test/ssl/cert_files/cacert.pem",NULL);
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{ char buf[1024];
char reply[1024];
int sd, bytes;
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
ShowCerts(ssl); /* get any certificates */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( bytes > 0 )
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); /* construct reply */
SSL_write(ssl, reply, strlen(reply)); /* send reply */
}
else
ERR_print_errors_fp(stderr);
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
char *portnum;
if ( count != 2 )
{
printf("Usage: %s <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
portnum = strings[1];
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "/home/test/ssl/cert_files/servercert.pem", "/home/test/ssl/cert_files/serverkey.pem"); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
while (1)
{ struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
close(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
}
Single client code Works
// Single Client Code that works!
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL -1
//Added the LoadCertificates how in the server-side makes.
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_load_verify_locations(ctx,"/home/test/ssl/cert_files/cacert.pem",NULL);
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
SSL_CTX* InitCTX(void)
{ const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = SSLv23_client_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); /* free the malloc'ed string */
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); /* free the malloc'ed string */
X509_free(cert); /* free the malloc'ed certificate copy */
}
else
printf("No certificates.\n");
}
int main(int count, char *strings[])
{
SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
if ( count != 3 )
{
printf("Usage: %s <host/ip> <portnum>\n", strings[0]);
exit(0);
}
char* hostname=strings[1];//"127.0.0.1";
char* portnum=strings[2];//"3334";
char CertFile[] = "/home/test/ssl/cert_files/clientcert.pem";
char KeyFile[] = "/home/test/ssl/cert_files/clientkey.pem";
SSL_library_init();
ctx = InitCTX();
LoadCertificates(ctx, CertFile, KeyFile);
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{ char *msg = "Hello???";
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
I did compilation using
g++ -g <src>.cc -o <src> -lssl -lcrypto for each of the three files (I had a Makefile)
The error that see is
./tcpserver_epoll 3334
sizeof 3334 argv[1] = 4
sizeof 3334 portt = 4
Accepted connection on descriptor 5 (host=127.0.0.1, port=44716)
Performing exchange.
Performing exchange 1.
Performing exchange Error 1.
Freeing data.
Performing exchange.
Performing exchange 1.
3072792824:error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol:s23_srvr.c:628:
Performing exchange Error 1.
Freeing data.
I am wondering if I have to create multiple instances of SSL_CTX *ctx;. I am a bit lost. Any help is appreciated. Thanks.
Update: Thanks to everyone for guidance. I found an example here that closely matches what I want
http://bendecplusplus.googlecode.com/svn/trunk/ssl_mycode/epoll_ssl/server.c
http://bendecplusplus.googlecode.com/svn/trunk/ssl_mycode/epoll_ssl/client.c
Your code is very hard to read (how about proper indentation?) but I think the main problem is, that you consider a return of -1 from SSL_accept to be a fatal error.
According to the man page (which is true in this case) -1 can happen on fatal error or "It can also occur of action is need to continue the operation for non-blocking BIOs. Call SSL_get_error() with the return value ret to find out the reason.". So you have to check the error and if its SSL_ERROR_WANT_READ you have to wait the socket is readable and on SSL_ERROR_WANT_WRITE until its writable.
I am wondering if I have to create multiple instances of SSL_CTX *ctx;
No, you can use a single SSL_CTX*.
The SSL_CTX* is reference counted. It will be destroyed after the last SSL_free.
Since you are using a common SSL_CTX*, you should tune each SSL session with the non-context API calls. For example, SSL_set_cipher_list rather than SSL_CTX_set_cipher_list or SSL_use_certificate rather than SSL_CTX_use_certificate (unless, of course, its common).
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)
I'm working on win ce 6 modbus tcp client server, application is developed for a client server communication and it is working fine. now req is my slave device should respond to the diff slave addresses polled by master/client. Can I just change slave id and establish connection or I need to close previous connection and again establish new one
below is the code, which is working fine for one node, if I polled with other node ID then it gives exception. what change it req to communicate with other node simultaneously. My device should be able to communicate with 32 diff nodes on modbus tcp. Shall I create individual threads for each node but how they will communicate on same port? before establishing connection with other node shall I close previous node?
startupServer(int slaveAddr, const TCHAR * const hostName)
{
int result;
int tcpOption;
struct sockaddr_in hostAddress;
if (isStarted())
return (FTALK_ILLEGAL_STATE_ERROR);
// Note: For TCP we allow 0 as slave address, -1 means ignore slave adr
if ((slaveAddr < -1) || (slaveAddr > 255))
return (FTALK_ILLEGAL_ARGUMENT_ERROR);
this->slaveAddr = slaveAddr;
//
// Special treatment for the Win32 platform, needs to load WinSock DLL
//
#ifdef _WINSOCKAPI_
WSADATA wsaData;
result = WSAStartup(0x0101, &wsaData);
if (result != 0)
return (FTALK_SOCKET_LIB_ERROR);
#endif
//
// Open socket
//
listenSocket = socket(PF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
shutdownServer();
return (FTALK_OPEN_ERR);
}
//
// Configure listen socket options (we ignore errors here)
//
#ifdef SO_REUSEADDR
tcpOption = 1; // Enable option
setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR,
(char *) &tcpOption, sizeof (tcpOption));
#endif
//
// Binding the listen socket to the port
//
hostAddress.sin_family = AF_INET;
if ((hostName == NULL) || (hostName[0] == '\0'))
hostAddress.sin_addr.s_addr = htonl(INADDR_ANY);
else
{
hostAddress.sin_addr.s_addr = inet_addr((char *) hostName);
#if !defined(__VXWORKS__) // We don't support host name resolving with VxWorks
if (hostAddress.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *hostInfo;
hostInfo = gethostbyname((char *) hostName);
if (hostInfo == NULL)
return (FTALK_TCPIP_CONNECT_ERR);
hostAddress.sin_addr = *(struct in_addr *) hostInfo->h_addr;
}
#endif
}
hostAddress.sin_port = htons(portNo);
result = bind(listenSocket, (struct sockaddr *) &hostAddress,
sizeof (hostAddress));
if (result == SOCKET_ERROR)
{
shutdownServer();
switch (socketErrno)
{
#ifdef _WINSOCKAPI_
case WSAEACCES:
return (FTALK_PORT_NO_ACCESS);
case WSAEADDRINUSE:
return (FTALK_PORT_ALREADY_BOUND);
case WSAEADDRNOTAVAIL:
default:
return (FTALK_PORT_NOT_AVAIL);
#else
case ENOTCONN: // Linux 7.2 reports this error no if no root privilege
case EACCES:
return (FTALK_PORT_NO_ACCESS);
case EADDRINUSE:
return (FTALK_PORT_ALREADY_BOUND);
case EADDRNOTAVAIL:
default:
return (FTALK_PORT_NOT_AVAIL);
#endif
}
}
//
// Start listening to incoming connections
//
result = listen(listenSocket,
((MAX_CONNECTIONS < SOMAXCONN) ? MAX_CONNECTIONS : SOMAXCONN));
if (result == SOCKET_ERROR)
{
shutdownServer();
return (FTALK_LISTEN_FAILED);
}
return (FTALK_SUCCESS);
}
serverLoop()
{
int iReturnCode = (FTALK_SUCCESS);
int result;
int sockIdx;
int recvResult;
int sendResult;
fd_set fdSet;
timeval timeVal;
SOCKET maxFileDes;
int replyCnt;
int tcpOption;
if (!isStarted())
return (FTALK_ILLEGAL_STATE_ERROR);
//
// Prepare file descriptor set for select call
//
FD_ZERO (&fdSet);
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127)
#endif
FD_SET (listenSocket, &fdSet);
#ifdef _MSC_VER
# pragma warning(pop)
#endif
maxFileDes = listenSocket;
for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127)
#endif
FD_SET (connectionSocketArr[sockIdx], &fdSet);
#ifdef _MSC_VER
# pragma warning(pop)
#endif
if (connectionSocketArr[sockIdx] > maxFileDes)
maxFileDes = connectionSocketArr[sockIdx];
}
//
// Block until accept request or received data or time-out
//
timeVal.tv_sec = (long) timeOut / 1000L;
timeVal.tv_usec = ((long) timeOut % 1000L) * 1000L;
if (timeOut == 0)
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, NULL);
else
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, &timeVal);
if (result == SOCKET_ERROR)
return (FTALK_FILEDES_EXCEEDED);
//
// Check for time-out
//
if (result == 0)
{
TRACELOG1("Slave poll time-out!\n");
dataTablePtr->timeOutHandler();
iReturnCode = (FTALK_REPLY_TIMEOUT_ERROR);
}
//
// Connection accept request
//
if (FD_ISSET (listenSocket, &fdSet))
{
// Search a free socket
for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] == INVALID_SOCKET)
{
struct sockaddr_in peerAddr;
SOCK_LEN_TYPE peerAddrLen = sizeof(peerAddr);
// Yes, socket is free, try to accept a connection on it
connectionSocketArr[sockIdx] = accept(listenSocket,
(struct sockaddr *) &peerAddr,
&peerAddrLen);
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
{
//
// Check id connection shall be accepted
//
if (!dataTablePtr->validateMasterIpAddr(inet_ntoa(peerAddr.sin_addr)))
{
shutdown(connectionSocketArr[sockIdx], SD_BOTH);
closesocket(connectionSocketArr[sockIdx]);
connectionSocketArr[sockIdx] = INVALID_SOCKET;
TRACELOG2("Connection rejected on slot %d\n", sockIdx);
}
//
// Set socket options (we ignore errors here, not critical)
//
#ifdef TCP_NODELAY
tcpOption = 1; // Enable option
setsockopt(connectionSocketArr[sockIdx],
IPPROTO_TCP, TCP_NODELAY,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_SNDBUF
tcpOption = MAX_MSG_SIZE;
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_SNDBUF,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_RCVBUF
tcpOption = MAX_MSG_SIZE;
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_RCVBUF,
(char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_LINGER
tcpOption = 0; // Disable option = discard unsent data when closing
setsockopt(connectionSocketArr[sockIdx],
SOL_SOCKET, SO_LINGER,
(char *) &tcpOption, sizeof (tcpOption));
#endif
TRACELOG2("Connection accepted on slot %d\n", sockIdx);
}
break; // Leave for loop
}
}
}
//
// Data received on socket
//
for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
{
if (FD_ISSET (connectionSocketArr[sockIdx], &fdSet))
{
recvResult = recv (connectionSocketArr[sockIdx],
(char *) bufferArr, sizeof (bufferArr), 0);
sendResult = 0;
replyCnt = 0;
//
// Process client message
//
if (recvResult >= PREFIX_LEN) // Process only minimum message sizes
{
short dataLen;
dataLen = (short) ((bufferArr[4] << 8) | (bufferArr[5] & 0xFF));
// Validate length before processing message
if ((dataLen + PREFIX_LEN) == recvResult)
{
replyCnt = processMessage(&bufferArr[PREFIX_LEN],
recvResult - PREFIX_LEN);
// The first two bytes (msg id) are returned untouched
bufferArr[2] = 0; // protocol identifier
bufferArr[3] = 0; // protocol identifier
bufferArr[4] = (char) ((replyCnt) >> 8);
bufferArr[5] = (char) ((replyCnt) & 0xFF);
sendResult = send(connectionSocketArr[sockIdx],
(char *) bufferArr,
replyCnt + PREFIX_LEN, 0);
}
}
//
// Check for disconnection and errors
//
if ((recvResult < PREFIX_LEN) ||
(sendResult != replyCnt + PREFIX_LEN))
{
//
// Free socket
//
shutdown(connectionSocketArr[sockIdx], SD_BOTH);
closesocket(connectionSocketArr[sockIdx]);
connectionSocketArr[sockIdx] = INVALID_SOCKET;
if (recvResult == 0)
TRACELOG2("Disconnected slot %d nicely by other peer.\n",
sockIdx);
else
TRACELOG2("Forced disconnection on slot %d!\n", sockIdx);
}
}
}
}
return iReturnCode;
}
Will the below code resolve my problem?
int ModbusTCPSlave::serverLoop()
{
int iReturnCode = (FTALK_SUCCESS);
int result;
int sockIdx;
int recvResult;
int sendResult;
fd_set fdSet;
timeval timeVal;
SOCKET maxFileDes;
int replyCnt;
int tcpOption;
//if (!isStarted())
// return (FTALK_ILLEGAL_STATE_ERROR);
//
// Prepare file descriptor set for select call
//
// FD_ZERO (&fdSet);
//#ifdef _MSC_VER
//# pragma warning(push)
//# pragma warning(disable: 4127)
//#endif
// FD_SET (listenSocket, &fdSet);
//#ifdef _MSC_VER
//# pragma warning(pop)
//#endif
// maxFileDes = listenSocket;
// for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
// {
// if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
//#ifdef _MSC_VER
//# pragma warning(push)
//# pragma warning(disable: 4127)
//#endif
// FD_SET (connectionSocketArr[sockIdx], &fdSet);
//#ifdef _MSC_VER
//# pragma warning(pop)
//#endif
// if (connectionSocketArr[sockIdx] > maxFileDes)
// maxFileDes = connectionSocketArr[sockIdx];
// }
//
// Block until accept request or received data or time-out
//
timeVal.tv_sec = (long) timeOut / 1000L;
timeVal.tv_usec = ((long) timeOut % 1000L) * 1000L;
if (timeOut == 0)
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, NULL);
else
result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, &timeVal);
// if (result == SOCKET_ERROR)
// return (FTALK_FILEDES_EXCEEDED);
//
// Check for time-out
//
// if (result == 0)
// {
// TRACELOG1("Slave poll time-out!\n");
// dataTablePtr->timeOutHandler();
//
// iReturnCode = (FTALK_REPLY_TIMEOUT_ERROR);
// }
//
// Connection accept request
//
// if (FD_ISSET (listenSocket, &fdSet))
{
// Search a free socket
// for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
{
// if (connectionSocketArr[sockIdx] == INVALID_SOCKET)
{
struct sockaddr_in peerAddr;
SOCK_LEN_TYPE peerAddrLen = sizeof(peerAddr);
// Yes, socket is free, try to accept a connection on it
connectionSocketArr[sockIdx] = accept(listenSocket,
(struct sockaddr *) &peerAddr,
&peerAddrLen);
// if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
// {
// //
// // Check id connection shall be accepted
// //
// if (!dataTablePtr->validateMasterIpAddr(inet_ntoa(peerAddr.sin_addr)))
// {
// shutdown(connectionSocketArr[sockIdx], SD_BOTH);
// closesocket(connectionSocketArr[sockIdx]);
// connectionSocketArr[sockIdx] = INVALID_SOCKET;
// TRACELOG2("Connection rejected on slot %d\n", sockIdx);
// }
//
// Set socket options (we ignore errors here, not critical)
//
//#ifdef TCP_NODELAY
// tcpOption = 1; // Enable option
// setsockopt(connectionSocketArr[sockIdx],
// IPPROTO_TCP, TCP_NODELAY,
// (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_SNDBUF
// tcpOption = MAX_MSG_SIZE;
// setsockopt(connectionSocketArr[sockIdx],
// SOL_SOCKET, SO_SNDBUF,
// (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_RCVBUF
// tcpOption = MAX_MSG_SIZE;
// setsockopt(connectionSocketArr[sockIdx],
// SOL_SOCKET, SO_RCVBUF,
// (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_LINGER
// tcpOption = 0; // Disable option = discard unsent data when closing
// setsockopt(connectionSocketArr[sockIdx],
// SOL_SOCKET, SO_LINGER,
// (char *) &tcpOption, sizeof (tcpOption));
//#endif
// TRACELOG2("Connection accepted on slot %d\n", sockIdx);
// }
// break; // Leave for loop
// }
// }
// }
//
// Data received on socket
//
// for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
// {
// if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
// {
// if (FD_ISSET (connectionSocketArr[sockIdx], &fdSet))
// {
recvResult = recv (connectionSocketArr[sockIdx],
(char *) bufferArr, sizeof (bufferArr), 0);
sendResult = 0;
replyCnt = 0;
//
// Process client message
//
if (recvResult >= PREFIX_LEN) // Process only minimum message sizes
{
short dataLen;
dataLen = (short) ((bufferArr[4] << 8) | (bufferArr[5] & 0xFF));
// Validate length before processing message
if ((dataLen + PREFIX_LEN) == recvResult)
{
replyCnt = processMessage(&bufferArr[PREFIX_LEN],
recvResult - PREFIX_LEN);
// The first two bytes (msg id) are returned untouched
bufferArr[2] = 0; // protocol identifier
bufferArr[3] = 0; // protocol identifier
bufferArr[4] = (char) ((replyCnt) >> 8);
bufferArr[5] = (char) ((replyCnt) & 0xFF);
sendResult = send(connectionSocketArr[sockIdx],
(char *) bufferArr,
replyCnt + PREFIX_LEN, 0);
}
}
//
// Check for disconnection and errors
//
if ((recvResult < PREFIX_LEN) ||
(sendResult != replyCnt + PREFIX_LEN))
{
//
// Free socket
//
shutdown(connectionSocketArr[sockIdx], SD_BOTH);
closesocket(connectionSocketArr[sockIdx]);
connectionSocketArr[sockIdx] = INVALID_SOCKET;
if (recvResult == 0)
TRACELOG2("Disconnected slot %d nicely by other peer.\n",
sockIdx);
else
TRACELOG2("Forced disconnection on slot %d!\n", sockIdx);
}
// }
// }
// }
return iReturnCode;
}
Thanks Valter, You are right, I got it. I've one more query in code there are two arrays regdata[30][65535]; and bitarray[30][2000] after reading data from a file I can decide the first dimension of array i.e. [30]..if data in file is for two slave id then i require regdata[2][65535] and bitarray[2][2000]..how I can manage this assignment at runtime? I tried using vector like struct{ regdata[65535]; bitarray[2000]; }regstack; after reading file I tried to push_back() regstack, but it gives heap error..how I can resize this array in runtime?
You can't have multiple sockets listening on the same port. But if the address is validated inside processMessage couln't you simply change that function to accept requests for different slave IDs.
Seems that flow not going in " for" loop containing accept in ipv6server.c and hence not able to accept and connect with the client. Whats the mistake ? This code is working fine for IPV4 but after IPV6 changes getting this problem
ipv6server.c
#include <stdio.h>
#include <stdlib.h> /* needed for os x */
#include <string.h> /* for memset */
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h> /* defines ERESTART, EINTR */
#include <sys/wait.h> /* defines WNOHANG, for wait() */
#include "port.h" /* defines default port */
#ifndef ERESTART
#define ERESTART EINTR
#endif
extern int errno;
void serve(int port); /* main server function */
void disconn(void);
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
int c, err = 0;
int port = SERVICE_PORT;
static char usage[] = "usage: %s [-d] [-p port]\n";
while ((c = getopt(argc, argv, "dp:")) != -1)
switch (c) {
case 'p':
port = atoi(optarg);
if (port < 1024 || port > 65535) {
fprintf(stderr, "invalid port number: %s\n", optarg);
err = 1;
}
break;
case '?':
err = 1;
break;
}
if (err || (optind < argc)) {
fprintf(stderr, usage, argv[0]);
exit(1);
}
serve(port);
}
/* serve: set up the service */
void
serve(int port)
{
int svc; /* listening socket providing service */
int rqst; /* socket accepting the request */
socklen_t alen; /* length of address structure */
struct sockaddr_in6 my_addr; /* address of this service */
struct sockaddr_in6 client_addr; /* client's address */
int sockoptval = 1;
char hostname[128]; /* host name, for debugging */
gethostname(hostname, 128);
/* get a tcp/ip socket */
/* AF_INET is the Internet address (protocol) family */
/* with SOCK_STREAM we ask for a sequenced, reliable, two-way */
/* conenction based on byte streams. With IP, this means that */
/* TCP will be used */
if ((svc = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
perror("cannot create socket");
exit(1);
}
/* we use setsockopt to set SO_REUSEADDR. This allows us */
/* to reuse the port immediately as soon as the service exits. */
/* Some operating systems will not allow immediate reuse */
/* on the chance that some packets may still be en route */
/* to the port. */
setsockopt(svc, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int));
/* set up our address */
/* htons converts a short integer into the network representation */
/* htonl converts a long integer into the network representation */
/* INADDR_ANY is the special IP address 0.0.0.0 which binds the */
/* transport endpoint to all IP addresses on the machine. */
memset((char*)&my_addr, 0, sizeof(my_addr)); /* 0 out the structure */
my_addr.sin6_family = AF_INET6; /* address family */
my_addr.sin6_port = htons(port);
my_addr.sin6_addr = in6addr_any;
client_addr.sin6_family = AF_INET6; /* address family */
client_addr.sin6_port = htons(port);
client_addr.sin6_addr = in6addr_any;
/* bind to the address to which the service will be offered */
if (bind(svc, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) {
perror("bind failed");
exit(1);
}
/* set up the socket for listening with a queue length of 5 */
if (listen(svc, 5) < 0) {
perror("listen failed");
exit(1);
}
printf("server started on %s, listening on port %d\n", hostname, port);
/* loop forever - wait for connection requests and perform the service */
alen = sizeof(client_addr); /* length of address */
for (;;) {
while ((rqst = accept(svc,
(struct sockaddr *)&client_addr, &alen)) < 0) {
/* we may break out of accept if the system call */
/* was interrupted. In this case, loop back and */
/* try again */
if ((errno != ECHILD) && (errno != ERESTART) && (errno != EINTR)) {
perror("accept failed");
exit(1);
}
}
printf("received a connection from: %s port %d\n",
inet_ntoa(client_addr.sin6_addr), ntohs(client_addr.sin6_port));
shutdown(rqst, 2); /* close the connection */
}
}
ipv6client.c
/*
echoc: a demo of TCP/IP sockets connect
usage: client [-h serverhost] [-p port]
*/
#include <stdio.h>
#include <stdlib.h> /* needed for os x*/
#include <string.h> /* for strlen */
#include <netdb.h> /* for gethostbyname() */
#include <sys/socket.h>
#include <netinet/in.h>
#include "port.h" /* defines default port */
int conn(char *host, int port);
void disconn(void);
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
int c, err = 0;
char *prompt = 0;
int port = SERVICE_PORT; /* default: whatever is in port.h */
char *host = "localhost"; /* default: this host */
static char usage[] =
"usage: %s [-d] [-h serverhost] [-p port]\n";
while ((c = getopt(argc, argv, "dh:p:")) != -1)
switch (c) {
case 'h': /* hostname */
host = optarg;
break;
case 'p': /* port number */
port = atoi(optarg);
if (port < 1024 || port > 65535) {
fprintf(stderr, "invalid port number: %s\n", optarg);
err = 1;
}
break;
case '?':
err = 1;
break;
}
if (err || (optind < argc)) { /* error or extra arguments? */
fprintf(stderr, usage, argv[0]);
exit(1);
}
printf("connecting to %s, port %d\n", host, port);
if (!conn(host, port)) /* connect */
exit(1); /* something went wrong */
disconn(); /* disconnect */
return 0;
}
int fd; /* fd is the file descriptor for the connected socket */
/* conn: connect to the service running on host:port */
/* return 0 on failure, non-zero on success */
int
conn(char *host, int port)
{
struct hostent *hp; /* host information */
unsigned int alen; /* address length when we get the port number */
struct sockaddr_in6 myaddr; /* our address */
struct sockaddr_in6 servaddr; /* server address */
printf("conn(host=\"%s\", port=\"%d\")\n", host, port);
/* get a tcp/ip socket */
/* We do this as we did it for the server */
/* request the Internet address protocol */
/* and a reliable 2-way byte stream */
if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
perror("cannot create socket");
return 0;
}
/* bind to an arbitrary return address */
/* because this is the client side, we don't care about the */
/* address since no application will connect here --- */
/* INADDR_ANY is the IP address and 0 is the socket */
/* htonl converts a long integer (e.g. address) to a network */
/* representation (agreed-upon byte ordering */
memset((char *)&myaddr, 0, sizeof(myaddr));
myaddr.sin6_family = AF_INET6;
myaddr.sin6_addr = in6addr_any;
myaddr.sin6_port = htons(0);
if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
perror("bind failed");
return 0;
}
/* this part is for debugging only - get the port # that the operating */
/* system allocated for us. */
alen = sizeof(myaddr);
if (getsockname(fd, (struct sockaddr *)&myaddr, &alen) < 0) {
perror("getsockname failed");
return 0;
}
printf("local port number = %d\n", ntohs(myaddr.sin6_port));
/* fill in the server's address and data */
/* htons() converts a short integer to a network representation */
memset((char*)&servaddr, 0, sizeof(servaddr));
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(port);
/* look up the address of the server given its name */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "could not obtain address of %s\n", host);
return 0;
}
/* put the host's address into the server address structure */
memcpy((void *)&servaddr.sin6_addr, hp->h_addr_list[0], hp->h_length);
/* connect to server */
if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("connect failed");
return 0;
}
return 1;
}
/* disconnect from the service */
void
disconn(void)
{
printf("disconn()\n");
shutdown(fd, 2); /* 2 means future sends & receives are disallowed */
}
hp = gethostbyname(host);
How do you know this returns an IPv6 address if you pass it "localhost" ? It probably returns the IPv4 address, and things go belly up if you try to copy that into servaddr.sin6_addr
Use getaddrinfo() and explicitly look for an AF_INET6 address (or better yet, make your program independent of the actual address types, see here), or use the global in6addr_loopback as the server address, for testing with localhost.
I can see a few issues of varying degrees:
Don't declare errno yourself - use the headers. It may be a macro instead of an int
(per #Boofhead) don't bind the client socket
Use getaddrinfo() instead of gethostbyname() in the client to get the server's address. gethostbyname() doesn't portably support IPv6
The last of the 3 is actually the real problem. I've tested your code on MacOS X and CentOS 5 and gethostbyname() only returns an IPv4 address.