I'm playing around with epoll on Linux for the first time and see some strange behavior. Specificall, when I connect with a client to a socket, I see two events emitted by epoll_wait on the server side. When I call accept on the second attempt I get a "temporary unavailable" error.
Here's the simple client code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
const char* msg = "friendly ping";
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <socket>\n", argv[0]);
exit(-1);
}
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(-1);
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("Error connecting");
exit(-1);
}
write(sock, msg, strlen(msg));
close(sock);
return 0;
}
The server code is the following:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
const int kMaxEvents = 100;
const char *kSocketFile = "dummy_socket";
void MakeNonBlocking(int fd) {
int flags, s;
flags = fcntl (fd, F_GETFL, 0);
if (flags == -1) {
perror ("fcntl");
exit(-1);
}
flags |= O_NONBLOCK;
s = fcntl (fd, F_SETFL, flags);
if (s == -1) {
perror ("fcntl");
exit(-1);
}
}
void AcceptConnections(int sock, int epoll_fd) {
struct epoll_event event;
event.data.fd = sock;
event.events = EPOLLIN;
int insock = accept(sock, NULL, NULL);
if (insock < 0) {
perror("Error accepting connection");
exit(-1);
}
MakeNonBlocking(insock);
int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event);
if (s < 0) {
perror("Epoll error adding accepted connection");
exit(-1);
}
printf("Connection processed.\n");
}
int main(void) {
int sock, efd, n;
struct sockaddr_un addr;
struct epoll_event event;
struct epoll_event *events;
char buf[1024];
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("Error creating socket.");
exit(-1);
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, kSocketFile, sizeof(addr.sun_path) - 1);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("Error binding name to socket");
exit(-1);
}
if (listen(sock, SOMAXCONN) < 0) {
perror("Error listening on socket");
exit(-1);
}
MakeNonBlocking(sock);
if ((efd = epoll_create1(0)) < 0) {
perror("Epoll initialization error");
exit(-1);
}
event.data.fd = sock;
event.events = EPOLLIN;
if (epoll_ctl(efd, EPOLL_CTL_ADD, sock, &event) < 0) {
perror("Epoll error adding socket");
exit(-1);
}
events = (struct epoll_event*) calloc(kMaxEvents, sizeof(event));
if (!events) {
perror("Error allocating event buffers");
exit(-1);
}
while(1) {
printf("Calling epoll_wait\n");
if ((n = epoll_wait(efd, events, kMaxEvents, -1)) == -1) {
perror("epoll_wait failure");
exit(-1);
}
for (int i = 0; i < n; ++i) {
printf("Checking event for fd = %d\n", events[i].data.fd);
if (sock == events[i].data.fd) {
AcceptConnections(sock, efd);
continue;
}
int count = read(events[i].data.fd, buf, 100);
if (count == 0) {
close(events[i].data.fd);
}
write(1, buf, count);
}
}
free(events);
close(efd);
close(sock);
unlink(kSocketFile);
return 0;
}
When I run the server and connect to the client I get:
Calling epoll_wait
Checking event for fd = 3
Connection processed.
Calling epoll_wait
Checking event for fd = 3
Error accepting connection: Resource temporarily unavailable
In other words, I see two events on the listening socket. Any ideas?
To fix, change AcceptConnections to:
void AcceptConnections(int sock, int epoll_fd) {
struct epoll_event event;
event.events = EPOLLIN;
int insock = accept(sock, NULL, NULL);
if (insock < 0) {
perror("Error accepting connection");
exit(-1);
}
// This is the important change.
event.data.fd = insock;
MakeNonBlocking(insock);
int s = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, insock, &event);
if (s < 0) {
perror("Epoll error adding accepted connection");
exit(-1);
}
printf("Connection processed.\n");
}
epoll is sometimes funny...
The issue is with the way you register the event in AcceptConnections.
epoll accepts two different fd values, one in event.data.fd (user opaque data value) and the other in the beginning of the epoll_ctl function call (the fd controlling the event).
The event.data.fd can be anything, and it's the actual data you read in your event loop...
... in your original code, you set it to the listening socket instead of the client socket.
So, when the client socket has data ready to be read, an event is raised with event.data.fd pointing at the listening socket instead of the client socket.
Since you don't clear the event (for the client socket), it's repeatedly raised with the data you set (the listening socket fd).
Good Luck!
Related
I am trying to connect to the local openvpn client over its managment socket and query its state/status.
I have implemented the follow program with libevent support.
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <fcntl.h>
#include <string.h>
char message[1000], server_reply[2000];
struct event *ev, *ev1, *ev2;
int fd_set_blocking(int fd, int blocking)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
return 0;
}
if (blocking) {
flags &= ~O_NONBLOCK;
} else {
flags |= O_NONBLOCK;
}
return fcntl(fd, F_SETFL, flags) != -1;
}
void cb_func_hold_release(evutil_socket_t fd, short what, void *arg)
{
int sock = *(int *)arg;
struct timeval one_seconds = {1,0};
printf("IN CB CONNECT\n");
sprintf (message, "hold release\n");
if (send (sock, message, strlen (message), 0) < 0) {
printf("send failed\n");
}
event_add(ev1, NULL);
}
void cb_func_write(evutil_socket_t fd, short what, void *arg)
{
int sock = *(int *)arg;
printf("IN CB CHECKSTATUS\n");
sprintf (message, "status\n");
if (send (sock, message, strlen (message), 0) < 0) {
printf("send failed\n");
}
/*
int reclen = recv (sock, server_reply, 2000, 0);
if (reclen < 0) {
printf("recving failed");
} else {
if (reclen > 1) {
server_reply[reclen-1] = '\0';
}
printf("SERVER REPLT< %s\n", server_reply);
}
if (strstr(message, "CONNECTED")) {
printf("VPN CONNECTED");
}
*/
event_add(ev2, NULL);
if (event_pending(ev2,EV_READ|EV_PERSIST, NULL)) {
printf("ADDED READ EVENT\n");
}
}
void cb_func_read(evutil_socket_t fd, short what, void *arg)
{
int sock = *(int *)arg;
printf("IN CB READ\n");
int reclen = recv (sock, server_reply, 2000, 0);
if (reclen < 0) {
printf("recving failed");
} else {
if (reclen > 1) {
server_reply[reclen-1] = '\0';
}
printf("SERVER REPLY %s\n", server_reply);
}
if (strstr(message, "CONNECTED")) {
printf("VPN CONNECTED");
}
}
int main(void)
{
int flags;
int mgmretry = 0;
int sock = 0;
struct sockaddr_in server;
struct timeval five_seconds = {1,0};
struct event_base *base = event_base_new();
ev = event_new(base, sock, EV_TIMEOUT, cb_func_hold_release, &sock);
ev1 = event_new(base, sock, EV_WRITE, cb_func_write, &sock);
ev2 = event_new(base, sock, EV_READ, cb_func_read, &sock);
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("COUILD NOT CREATE SOCKET\n");
exit(0);
}
fd_set_blocking(sock, 1);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons (12345);
if (connect (sock, (struct sockaddr *)&server, sizeof (server)) < 0) {
printf("connecting failed\n");
exit(0);
}
event_add(ev, &five_seconds);
printf("DISPATCHING\n");
event_base_dispatch(base);
}
The issue is the the read callback "cb_func_read" is not being triggered. My expectation is that after I send the "status" message to the vpn from cb_func_write(), there is some data sent back which should trigger the read callback but that's not happening.
If I recv() directly after send() in the write callback(the commented portion) I can print the response sent from openvpn so the communication between these two are working.
The event will not trigger unless you armed it using event_add().
You have only done this for the ev event, but not for ev1 and ev2.
silly ,mistake...events ev,ev1,ev2 with event_new() come AFTER creating a socket+making it nonblock... not before
I am using raw sockets for the first time, so excuse me if my question exhibits serious lack of knowledge in this field.
My goal is to make a client-server program using raw sockets where client can send a string to server and server can send a reply. My codes are as follows:
/* server.c */
#include "../cn.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char const *argv[]) {
int sfd = socket(AF_INET, SOCK_RAW, 253);
if (sfd < 0) {
perror("Could not create socket");
exit(0);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("Could not bind");
}
int nsfd = accept(sfd, NULL, NULL);
if (nsfd < 0) {
perror("Could not accept");
exit(0);
}
char buffer[20];
int sz;
while (1) {
if ((sz = read(nsfd, buffer, 20)) < 0) {
perror("Could not read");
} else {
buffer[sz] = '\0';
strcat(buffer, " form server");
if (write(nsfd, buffer, strlen(buffer)) < 0) {
perror("Could not send");
}
}
}
return 0;
}
Client code:
#include "../cn.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char const *argv[]) {
int sfd = socket(AF_INET, SOCK_RAW, 253);
if (sfd < 0) {
perror("Could not create socket");
exit(0);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(sfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("Could not connect");
exit(0);
}
char buffer[20];
int sz;
while (1) {
scanf("%[^\n]s", buffer);
while (getchar() != '\n')
;
if (write(sfd, buffer, strlen(buffer)) < 0) {
perror("Could not write");
} else
if ((sz = read(sfd,buffer, 20)) < 0) {
perror("Could not read");
} else {
buffer[sz] = '\0';
printf("Reading: %s\n", buffer);
}
}
return 0;
}
Whenever I am trying to run the programs , it is showing error at the very time of socket creation itself:
Could not create socket: Operation not permitted
My questions are:
What am I missing in this case? Why does is this operation not permitted?
Though it is a broad question but, how to use raw sockets with custom protocol ID?
Is there any good material available for use of raw sockets with custom protocol?
I am trying to make a server.c file that supports 3 sockets, which are represented by 3 respective client classes: client1, client2, client3.
In my server.c file, I currently have this code which I found on the internet.
If I wanted to make it have 3 sockets. I want to use the select() command to see the write activities of the 3 clients. My question is how can I use this to support 3 sockets.
Can I bind the 3 clients to 3 sockets that the server can listen to? If so, how can the server listen to these 3 sockets respectively? With an array possibly?
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#define socket1 "sock1"
#define socket2 "sock2"
#define socket3 "sock3"
int main(int argc, char *argv[]) {
//struct sockaddr_un addr;
struct sockaddr_un addr1;
struct sockaddr_un addr2;
struct sockaddr_un addr3;
char buf[100];
int socket1;
int socket2;
int socket3;
//int fd;
int cl,rc;
if (argc > 1) socket_path=argv[1];
if ( (socket1 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr1, 0, sizeof(addr1));
addr1.sun_family = AF_UNIX;
strncpy(addr1.sun_path, socket_path, sizeof(addr1.sun_path)-1);
unlink(socket_path1);
if ( (socket2 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr2, 0, sizeof(addr2));
addr1.sun_family = AF_UNIX;
strncpy(addr2.sun_path, socket_path, sizeof(addr2.sun_path)-1);
unlink(socket_path2);
if ( (socket3 = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr3, 0, sizeof(addr3));
addr3.sun_family = AF_UNIX;
strncpy(addr3.sun_path, socket_path, sizeof(addr3.sun_path)-1);
unlink(socket_path3);
if (bind(socket1, (struct sockaddr*)&addr1, sizeof(addr1)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket2, (struct sockaddr*)&addr2, sizeof(addr2)) == -1) {
perror("bind error");
exit(-1);
}
if (bind(socket3, (struct sockaddr*)&addr3, sizeof(addr3)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(socket1, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket2, 5) == -1) {
perror("listen error");
exit(-1);
}
if (listen(socket3, 5) == -1) {
perror("listen error");
exit(-1);
}
while (1) {
if ( (cl = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
while ( (rc=read(cl,buf,sizeof(buf))) > 0) {
printf("read %u bytes: %.*s\n", rc, rc, buf);
}
if (rc == -1) {
perror("read");
exit(-1);
}
else if (rc == 0) {
printf("EOF\n");
close(cl);
}
}
return 0;
}
If you want three listening sockets in the same process, you have to make them unique. In the AF_INET family you do that by bind(2)-ing different ports, in the AF_UNIX family you do that with different paths.
Also your line:
char *socket_path = "\0hidden";
has at least two problems:
Type of the string literal on the right side of the assignment is const char[8], which decays to const char* pointer type, but not char* type. Make the left hand side const char*. Plus, compile with higher warning level, like -Wall -pedantic to get help from your compiler.
Zero byte at the beginning of the string makes strncpy(3) not copy anything, since it copies at most n characters from the string pointed to by src, including the terminating null byte ('\0').
Create a function that take UNIX path as an argument and creates, binds, and marks socket as listening, and returns created socket descriptor. Call it three times - you have three listening UNIX sockets. Setup select(2) on them for reading - that'll tell you when client connections arrive. At that point call accept(2) on the active socket to get connected client socket, which is separate from the listening socket itself.
Ok, since select, is and has always been my favorite Unix syscall, I decided to do a little something, which is, in my humble opinion, what you were looking for.
I shamelessly took server's and client's code from here:
https://troydhanson.github.io/misc/Unix_domain_sockets.html
I did of course some little modifications, to make it fit your needs, lets see:
server.c:
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
char *socket_path = "/tmp/socket";
int main() {
int fd, i;
int clients[10], num_clients;
fd_set read_set;
char buf[100];
struct sockaddr_un addr;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
unlink(socket_path);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
if (listen(fd, 5) == -1) {
perror("listen error");
exit(-1);
}
num_clients = 0;
while (1) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
for (i = 0; i < num_clients; i++) {
FD_SET(clients[i], &read_set);
}
select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);
if (FD_ISSET(fd, &read_set)) {
if ( (clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
printf("we got a connection!\n");
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
read(clients[i], buf, sizeof(buf));
printf("client %d says: %s\n", i, buf);
}
}
}
}
client.c:
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char *socket_path = "/tmp/socket";
int main(int argc, char *argv[]) {
struct sockaddr_un addr;
char buf[100];
int fd,rc;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("connect error");
exit(-1);
}
while( (rc=read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
printf("writing\n");
*index(buf, '\n') = 0;
if (write(fd, buf, rc) != rc) {
if (rc > 0) fprintf(stderr,"partial write");
else {
perror("write error");
exit(-1);
}
}
}
return 0;
}
Ok, this works very easily, you just fire the server in one terminal, and then you open a couple other terminals and fire a couple clients.
Running it on my pc I get:
exe#atreides:~/tmp$ ./server
we got a connection!
client 0 says: Hello!
we got a connection!
client 1 says: Hey man!
Another terminal at the same time:
exe#atreides:~/tmp$ ./client
Hey man!
writing
And in another:
exe#atreides:~/tmp$ ./client
Hello!
writing
The magic behind all this is to properly use socket and select.
First you need a server socket, the one that will accept connections.
Once you bind to a server socket, let it be a Unix socket or a network socket, you can get sockets to your clients by accepting connections on that socket. Each client gets a new socket number.
Then, you add these sockets, the server socket and the clients socket to an fd_set and pass it to select. Select will listen on all sockets at the same time and will leave in the set those who have received data.
Now you iterate the set to see what sockets are hot and, you're there!
One more thing, which I guess was confusing you, all clients connect to the same server socket address (file). Yes it is like if many processes opened the same file, at the same time... But this isn't an ordinary file, it is a Unix socket. :)
Have fun and good luck!!!
I had to implement a application health check mechanism , I used non blocking socket with select although I achieved success
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
int connect_tout(char * hostname1, int port, int timeoutval)
{
char *hostname = hostname1; /* pointer to name of server */
struct sockaddr_in saddr; /* socket address */
int s, i;
fd_set fd_r, fd_w;
struct timeval timeout;
int flags;
timeout.tv_sec = timeoutval;
timeout.tv_usec = 0;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(hostname1);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/* set the socket fd to non-blocking mode */
fcntl(s, F_SETFL, (flags = fcntl(s, F_GETFL)) | O_NONBLOCK);
connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
FD_ZERO(&fd_r);
FD_ZERO(&fd_w);
FD_SET(s, &fd_r);
FD_SET(s, &fd_w);
/* timeout durring connect() ?? */
select(s+1, &fd_r, &fd_w, NULL, &timeout);
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
printf("Conect TIMEOUT \n");
close(s);
return errno;
}
i = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if(i)
{
printf("Conect failed errno:%d\n",errno);
perror("connect:");
close(s);
return errno;
}
else
{
printf("Connect passed and OK \n");
close(s);
return 1;
}
close(s);
return 1;
}
int main (int argc, char *argv[])
{
int ret;
if (argc < 3)
{
printf("Usage: %s [host] [port] [timout]\n", argv[0]);
exit(1);
}
char *hostname = argv[1]; /* pointer to name of server <IP address>*/
connect_tout(hostname, atoi(argv[2]), atoi(argv[3]));
return 0;
}
BUT my problem comes when my code running machine goes under very high fd usage . NOTE: Opening many fds at a time at my system is common behaviour . then this piece fails each time
if(FD_ISSET(s, &fd_w))
{
printf("ALIVE\n");
}
else
{
close(s);
return errno;
printf("Conect TIMEOUT\n");
}
As I said in such environment it fails by saying TIMEOUT , I want to know why select is failing by not determining ready discripters so soon and that is each time. Is FD_ISSET() may also fall under doubt ?
P S : This runs well when system is under normal number of fds . Sorry for bad program I just pasted here my sample working code. I will check for errors later
For non-blocking connect() usage, you do not call it again after getting writeable notification. Instead, you should check the error status of the socket with getsockopt() with the SO_ERROR option.
You are not checking the return values of any of your calls, and this makes it impossible for your code to actually determine any failures correctly. Note that you do not check the case if the passed in timeout itself is 0, which would cause select() to return immediately with the instantaneous status of the socket. Note that checking for readable notification of the connecting socket is not documented by the socket API.
int s = socket(PF_INET, SOCK_STREAM, 0);
assert(!(s < 0));
int r = fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
assert(r == 0);
r = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (r < 0) {
if (errno == EINPROGRESS) {
FD_ZERO(&fd_w);
FD_SET(s, &fd_w);
r = select(s+1, NULL, &fd_w, NULL, NULL);
if (r < 0) {
perror("select");
abort();
}
assert(r == 1);
assert(FD_ISSET(s, &fd_w));
int erropt = -1;
socklen_t errlen = sizeof(erropt);
r = getsockopt(s, SOL_SOCKET, SO_ERROR, &erropt, &errlen);
assert(r == 0);
if (erropt != 0) {
errno = erropt;
perror("connect[after select]");
abort();
}
/* connect succeeded asynchronously */
} else {
perror("connect[direct call]");
abort();
}
} else {
/* connect succeeded synchronously */
}
I'm new to network programming. I have to write a simple client/server program in C. The server will listen for a connection and the client will connect to the server, send a message, and receive an echo back from the client. We have to update this using select() to handle connections to multiple clients at the same time from the server process. I tried to implement select() on the client side like instructed,but I think I'm having an infinite loop on the client side in if(FD_ISSET(clientSockfd, &readfds)) part.
//client1.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <resolv.h>
#include <arpa/inet.h>
const int BUF_SIZE = 512;
int main(int argc, char *argv[]) {
char buf[BUF_SIZE], buf2[BUF_SIZE];
char *msg;
struct sockaddr_in serverInfo;
int clientSockfd, errorCheck, readVal, numfd;
struct hostent *hostName;
fd_set readfds;
//make sure user entered correct arguments when starting client
if(argc != 3)
{
printf("error: must enter 'programName portNumber hostname'\n");
exit(errno);
}
//create socket and error check socket() call
clientSockfd=socket(AF_INET, SOCK_STREAM, 0);
if (clientSockfd == -1)
{
perror("error creating socket");
exit(errno);
}
//assign sockaddr_in info for RemoteAddr
bzero(&serverInfo, sizeof(serverInfo));
serverInfo.sin_family=AF_INET;
hostName=gethostbyname(argv[2]);
if(hostName == NULL)
{
herror("Error when calling gethostbyname()");
exit(errno);
}
memcpy((unsigned char *) &serverInfo.sin_addr, (unsigned char *)hostName->h_addr, hostName->h_length); //copy IP address to be used
serverInfo.sin_port=htons(atoi(argv[1])); //port number to be used, given in command line, must be converted to network byte order
//connect to server side
if(connect(clientSockfd, (struct sockaddr *)&serverInfo, sizeof(serverInfo)) == -1)
{
perror("error when connecting to server");
exit(errno);
}
while(1)
{
FD_ZERO(&readfds); //zero out set
FD_SET(fileno(stdin), &readfds);
FD_SET(clientSockfd, &readfds);
int maxfd = fileno(stdin);
if(maxfd < clientSockfd) maxfd = clientSockfd;
numfd = select(maxfd, &readfds, 0, 0, 0); //call select()
if(numfd > 0)
{
if(FD_ISSET(clientSockfd, &readfds))
{
//make sure buf is empty so it doesnt print extra chars
bzero(buf2, BUF_SIZE);
read(clientSockfd, buf2, BUF_SIZE);
}
if(FD_ISSET(fileno(stdin), &readfds))
{
bzero(buf, BUF_SIZE);
fgets(buf, BUF_SIZE-1, stdin);
printf("echo from server: %s\n", buf);
errorCheck = write(clientSockfd, buf, strlen(buf)+1);
if(errorCheck == -1)
{
perror("error writing");
}
}
}
else if(numfd == 0)
{
perror("Error using select()\n");
exit(0);
}
else
printf("no data\n");
}
//close connection to server
errorCheck = close(clientSockfd);
if(errorCheck == -1)
{
perror("Error closing connection.");
exit(errno);
}
return 0;
}
here is the server..
//server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
const int ASSIGNED_PORT = 17000;
const int BUF_SIZE = 512;
int main() {
int serverfd, clientfd;
struct sockaddr_in serverSock; //NOTE: a pointer to sockaddr_in can be cast to a pointer to
// a struct sockaddr - useful for connect()
char buf[BUF_SIZE];
int errorCheck, msgLength;
//create socket with error checking (-1 ret on error)
serverfd = socket(AF_INET, SOCK_STREAM, 0);
if(serverfd < 0 )
{
perror("socket failed.");
exit(errno);
}
//assign sockaddr_in info for server
bzero(&serverSock, sizeof(serverSock)); //set to all 0's
serverSock.sin_family = AF_INET;
serverSock.sin_addr.s_addr = htonl(INADDR_ANY);
serverSock.sin_port = htons(ASSIGNED_PORT);
//bind a name to the socket with error checking (0 ret on success)
errorCheck = bind(serverfd, (struct sockaddr*)&serverSock, sizeof(serverSock));
if(errorCheck < 0 )
{
perror("bind failed.");
exit(errno);
}
//listen for connections with error checking (0 ret on success)
errorCheck = listen(serverfd, 10);
if(errorCheck < 0 )
{
perror("listen failed.");
exit(errno);
}
printf("Listening for connections. Enter CNTRL-c to kill server.\n");
//create infinite loop to accept, read, write, and close connections with error hecking
while(1)
{
//accept the connection from the client
clientfd = accept(serverfd, 0, 0);
if(clientfd == -1)
{
perror("error accepting connection.");
exit(errno);
}
//read data from the client
bzero(buf, BUF_SIZE);
msgLength = read(clientfd, buf, BUF_SIZE-1);
if(msgLength == -1)
{
perror("error reading from client");
close(clientfd);
close(serverfd);
exit(errno);
}
if(buf[0] '\0')
{
printf("connection closing");
exit(0);
}
//print what the client sent
printf("Message from client: %s\n", buf);
//echo back what the client sent
errorCheck = write(clientfd, buf, strlen(buf)+1);
if(errorCheck == -1 )
{
perror("error writing to client");
exit(errno);
}
//close the connection
errorCheck = close(clientfd);
if(errorCheck == -1)
{
perror("error closing connection");
exit(errno);
}
}
errorCheck = close(serverfd);
if(errorCheck==-1)
{
perror("error closing server, exiting program now");
sleep(6);
exit(errno);
}
return 0;
}
I think the problem (or at least, a problem) in your client-side code is that you are passing the magic number 32 to select(). That's incorrect. What you should be passing is the maximum of all of the socket numbers, plus one. For example, something like this:
int maxfd = fileno(stdin);
if (maxfd < clientSockFD) maxfd = clientSockFD;
// further maximizations for other sockets would go here, if you had any other sockets...
numfd = select(maxfd+1, &readfds, 0, 0, 0);
You need to use select() on the server side to handle multiple clients, not on the client side.