self-written web server cannot be visited by other's computer - c

I wrote a web server using C language. I can visit the server at http://myhostname:protnum/index.html
But when I use my friend's computer to visit the same address, it said cannot visit the web page.
The file is webserv.c, server is launched with ./webserv 12345 (in a terminal)
Why can't my friend computer access the server?
The following is the webserv.c file:
int main(int argc, char const *argv[])
{
int sock, fd;
FILE *fpin;
char request[BUFSIZ];
if (argc == 1) {
fprintf(stderr, "usage: ws portnum\n");
exit(1);
}
sock = make_server_socket( atoi(argv[1]) );
if (sock == -1)
exit(2);
/*main loop here*/
while (1) {
/*take a call and buffer it*/
fd = accept(sock, NULL, NULL);
fpin = fdopen(fd, "r");
/*read request*/
fgets(request, BUFSIZ, fpin);
printf("Got a call: request = %s", request);
read_til_crnl(fpin);
/*do what client asks*/
process_rq(request, fd);
fclose(fpin);
}
return 0;
}
int make_server_socket_q(int portnum, int backlog)
{
struct sockaddr_in saddr;
struct hostent *hp;
char hostname[HOSTLEN];
int sock_id;
sock_id = socket(PF_INET, SOCK_STREAM, 0);
if (sock_id == -1)
return -1;
/*build address abd bind it to socket*/
bzero((void *)&saddr, sizeof(saddr));
gethostname(hostname, HOSTLEN);
hp = gethostbyname(hostname);
bcopy((void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum);
saddr.sin_family = AF_INET;
if (bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0)
return -1;
if (listen(sock_id, backlog) != 0)
return -1;
return sock_id;
}
void process_rq(char *request, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
/*create a new process and return if not the child*/
if (fork() != 0)
return;
strcpy(arg, "./");
if (sscanf(request, "%s%s", cmd, arg+2) !=2)
return;
if (strcmp(cmd, "GET") != 0)
cannot_do(fd);
else if (not_exist(arg))
do_404(arg, fd);
else if (isadir(arg))
do_ls(arg, fd);
else if (ends_in_cgi(arg))
do_exec(arg, fd);
else
do_cat(arg, fd);
}

One common error is to use the wrong address when creating the listening socket. If your listen from 127.0.0.1 only local connections will be accepted. You should listen on 0.0.0.0 to allow connection from any IP address.
To do this in my windows code I've used in the past
addr.sin_family = AF_INET;
addr.sin_port = ...;
addr.sin_addr.s_addr = 0;
but seems that INADDR_ANY is a better way to say 0.

Related

file server in C doesn't continually return files when requested

I'm new to network programming and recently finished reading through Beej's guide. I have a client/server program that I'm trying to get to continuously have the server return the contents of a file when the client requests it.
It works by the client sending the server a file path and the server reading it (if it exists) into a buffer then sending the buffer to the client which just prints the buffer.
It works, but it will only return one file then ignores any following requests. I have to shut down the client and reconnect again for it to work again. I can't figure out why. I've tried implementing select() and used aio_read() over the standard read() and I also forking a process for the send() function. Each of those those experiments had it working exactly the same pretty much.
Anyone have any tips? I'm at a loss where the problem could be.
Client
#define MAXDATASIZE 100 // max number of bytes at once
#define MAXMSG 25
#define MAXDATA 4096
#define SA struct sockaddr
// clean_str: make sure the string doesn't have junk spaces around it
void clean_str(char *s)
{
size_t len = strlen(s);
char tmp[MAXMSG] = {0};
strncpy(tmp, s, len-1);
memset(s, 0, len);
strncpy(s, tmp, len-1);
}
int main(int argc, char **argv)
{
int sockfd, numbytes;
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
char file_request[MAXMSG] = {0};
char file_buf[MAXDATA];
if (argc != 3) {
fprintf(stderr, "usage: client <hostname> <port>\n");
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
// load the struct
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(EXIT_FAILURE);
}
// loop trhough all results and connect to the first one we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0) {
close(sockfd);
perror("client: connect");
continue;
}
// if we make it here, we've got a connection
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
exit(EXIT_FAILURE);
}
inet_ntop(p->ai_family, (SA*)&p->ai_addr, s, sizeof s);
printf("client: connecting to %s\n", s);
freeaddrinfo(servinfo);
// stay connect until client exits
int n;
while (1) {
// make sure everything is cleared to minimize issues
memset(file_buf, 0, MAXDATA);
memset(file_request, 0, sizeof MAXMSG);
numbytes = 0;
// get client request from stdin
int b = read(STDIN_FILENO, file_request, MAXMSG);
if (b < 0) {
perror("client: read");
}
clean_str(file_request);
// send the request to the server
if ((numbytes = send(sockfd, file_request, strlen(file_request), 0)) < 0) {
perror("send");
exit(EXIT_FAILURE);
}
// now we wait for a response
while ((n = read(sockfd, file_buf, MAXDATA-1)) > 0)
printf("%s\n", file_buf);
if (n < 0) {
perror("read");
}
}
return 0;
}
Server
#define PORT 3490
#define MAXDATA 4096
#define FILENAME 256
#define SA struct sockaddr // for less messy casting
// get_file: open file, read contents info a buffer, return buffer
char *get_file(const char *path) {
int n, bytes;
static char buf[MAXDATA];
// try to open file
n = open(path, O_RDONLY);
if (n < 0) {
strcpy(buf, "problem opening file");
printf("%s\n", buf);
return buf;
}
// if exists, read it into buffer on
bytes = read(n, buf, sizeof buf-1);
if (bytes < 0) {
strcpy(buf, "problem reading file");
printf("%s\n", buf);
return buf;
}
close(n);
return buf;
}
int main()
{
int sockfd, filefd;
struct sockaddr_in servaddr;
struct sockaddr_storage client_addr;
socklen_t len;
int nbytes;
char file_request[FILENAME]; // buf to hold client's request string
// clear servaddr struct
memset(&servaddr, 0, sizeof servaddr);
servaddr.sin_family = AF_INET; // IPv4 for simplicity
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // use my IP
servaddr.sin_port = htons(PORT); // short, network by order
// create socket file descriptor
// #param3 is the protocol. 0 means TCP
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// bind the socket to the PORT
if (bind(sockfd, (SA*)&servaddr, sizeof servaddr) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
// this prevents the 'bind: address already in use' issue
int yes = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
perror("setsocket");
exit(EXIT_FAILURE);
}
if (listen(sockfd, 10) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("server running and waiting for connection...\n");
int open = 1; // keep track if there's an accepted() fd
char *open_file;
while (1) {
// clear the file_request buffer
memset(file_request, 0, FILENAME);
memset(&open_file, 0, sizeof open_file);
nbytes = 0;
if (open) {
// we're only going to connect to one client for now
len = sizeof client_addr;
filefd = accept(sockfd, (SA*)&client_addr, &len);
if (filefd < 0) {
perror("accept");
continue;
} else {
printf("connected to a client\n");
open = 0; // keep track that there's an open fd
}
}
// recieve data from a client
if ((nbytes = recv(filefd, file_request, sizeof file_request, 0)) <= 0) {
// got error or connection was closed by client
if (nbytes == 0) {
printf("file-server: client hung up\n");
close(filefd);
open = 1;
continue;
} else {
perror("recv");
close(filefd);
open = 1;
continue;
}
close(filefd);
} else {
// we got some data
// manage it and get file contents
open_file = get_file(file_request);
if (strcmp(open_file, "0") == 0) {
continue;
}
if (send(filefd, open_file, strlen(open_file), 0) < 0) {
perror("send");
continue;
}
}
}
close(sockfd);
return 0;
}

Connection Refused even after adding a new Firewall rule

I am trying to connect to my local UNIX server i made from another remote device. the Server is up and listening to the port i specified. i also added a new firewall rule to open that port but still my client cannot connect. it shows ERROR CONNECTION REFUSED
here is my server code
int main() {
int fd, i,svclient,rval,msg;
int clients[10], num_clients;
fd_set read_set,write_set;
char buf[100];
struct sockaddr_in addr;
if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(-1);
}
bzero((char *) &addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(4001);
//strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
//strcpy(addr.sun_path, NAME);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("bind error");
exit(-1);
}
printf("Bind complet...\n");
if (listen(fd, 20) == -1) {
perror("listen error");
exit(-1);
}
num_clients = 0;
int size = sizeof(fd);
while (1) {
int clientfd;
struct sockaddr_in client_addr;
int addrlen=sizeof(client_addr);
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
for (i = 0; i < num_clients; i++) { //at first this part will not excute
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,(struct sockaddr*)&client_addr,&addrlen)) == -1) {
perror("accept error");
continue;
}
/*printf("incoming message..................... !\n \n");*/
printf("%s:%d connected\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
msg = read(clients[i], buf, sizeof(buf));
if(msg > 0){
buf[msg] = 0;
int savedclnt = clients[i];
printf("%s \n \n", buf);
/*for(int p=0;p<num_clients;p++)
{
if( clients[p]!= savedclnt){
write(clients[p],buf,msg);
}
}*/
}
}
}
}
}
and my client
int main( )
{
struct uci_context *uci;
uci = uci_init();
int sockfd;
int ret;
struct sockaddr_in dest;
struct addrinfo hint, *res = NULL;
struct hostent *host;
char *hostip;
char *string;
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
{
puts("Unble to create socket");
exit(1);
}
hostip = ucix_get_option(uci, "pack_mon", "pack_monitoring", "address");
string = ucix_get_option(uci, "pack_mon", "pack_monitoring", "port");
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(string));
memset(&hint, '\0', sizeof hint);
hint.ai_family = PF_UNSPEC;
hint.ai_flags = AI_NUMERICHOST;
printf(" %s- %s\n", hostip, string );
if(isdigit(hostip[0])){
ret = getaddrinfo(hostip, NULL, &hint, &res);// this is more efficient than inet_addr
if (ret) {
exit(1);
}
}else if( (host = gethostbyname(hostip)) != 0){
memcpy((char*)&dest.sin_addr , (char*)host->h_addr , (sizeof dest.sin_addr)+1);
}else{
exit(1);
printf("cannot resolve ip address");
}
if ( connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) < 0 )
{
perror("ERROR Connecting" );
exit(1);
}else{
printf("Port number %s is open.....\n",string);
}
char *message;
message = "help";
write(sockfd,message,strlen(message));
close(sockfd);
freeaddrinfo(res);
return 0;
}
FIREWALL RULE
sudo iptables -I INPUT -p tcp --dport 4001 -j ACCEPT
Error is :
192.168.10.155- 4001
ERROR Connecting: Connection refused
and this logs are coming from this codes :
printf(" %s- %s\n", hostip, string );
perror("ERROR Connecting");
exit(1);
Your client has no code to specify the IP address it wants to connect to. All the code that could do that has been commented out.
Update: Now your bug is here:
strncpy((char*)&dest.sin_addr , (char*)host->h_addr , sizeof dest.sin_addr);
The strncpy function is only suitable for C-style strings. You need to use memcpy or something similar. This will only copy part of the IP address if any octet other than its last one (in network byte order) is zero.
Update: Now your bug is here:
printf("%d\n", connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) < 0);
perror("hmmmm" );
exit(1);
This calls connect, then calls printf and then calls perror. The problem is, the call to printf can modify errno even if it succeeds. Thus your call to perror can print a totally irrelevant error message.

How to assign hostname and portnumber that client connected from?

I have this ftserver.c program which is implementing a file transfer server which listens for a client and then responds to the clients request over a data connection. Right now it works but I have the hostname and port number for the data connection hardcoded. The portnumber has been provided by the client and the server should be able to get the hostname from the client's control connection.
References:http://beej.us/guide/bgnet/output/html/multipage/getaddrinfoman.html
How can I assign the hostname and portnumber dynamically? Thank you.
void error(const char *msg)
{
perror(msg);
exit(1);
}
void startup(int portNumber);
void setupData(char* portNum);
int sockfd, newsockfd, datasock, portno;
char buffer[256]; socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr, port_addr;
struct addrinfo hints, *servinfo, *p;
char ipstr[1000];
struct in_addr ipAddr;
struct sockaddr_in *s;
int main(int argc, char *argv[]){
int n; char* dataport; char * token; char filename[100];
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
portno = atoi(argv[1]);
startup(portno);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
token = strtok(buffer, " ");
//if client requested a list, setup data connection and send it
if (strcmp(token, "-l") == 0){
token = strtok(NULL, " ");
printf("the token is %s\n", token);
//dataport = atoi(token);
dataport = token;
setupData(dataport);
//sendList(dataport);
}
//if client requested a file, setup data connection and send it
else if (strcmp(token, "-g") == 0){
token = strtok(NULL, " ");
//filename = *token;
token = strtok(NULL, " ");
//dataport = atoi(token);
printf("the data port is %d\n", dataport);
//setupData(dataport);
//sendFile(filename, dataport);
}
else {
n = write(newsockfd,"not a valid command",19);
if (n < 0) error("ERROR writing to socket");
}
//close sockets for connection P
close(datasock);
close(newsockfd);
close(sockfd);
return 0;
}
void startup(int portNumber)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portNumber);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,256);
}
void setupData(char* portNum){
int rv;
const char* name = "localhost";
char s[1000];
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(name, "30024", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((datasock = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("socket");
continue;
}
if (connect(datasock, p->ai_addr, p->ai_addrlen) == -1) {
perror("connect");
close(sockfd);
continue;
}
break; // if we get here, we must have connected successfully
}
if (p == NULL) {
// looped off the end of the list with no connection
fprintf(stderr, "failed to connect\n");
exit(2);
}
printf("data connection setup successful\n");
}
I have resolved this. By using a simplified version of setting up the data conection, not using getaddrinfo() and using a hostname_to_ip conversion function. Reference: http://www.linuxhowtos.org/data/6/client.c
int setupData(char* hostname, char* portNum){
int sock_fd; char ip[100];
struct sockaddr_in srv_addr;
memset(&srv_addr, 0, sizeof(srv_addr)); /* zero-fill srv_addr structure*/
/* create a client socket */
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
srv_addr.sin_family = AF_INET; /* internet address family */
/* convert command line argument to numeric IP */
hostname_to_ip(hostname, ip);
printf("%s resolved to %s" , hostname , ip);
if ( inet_pton(AF_INET, ip, &(srv_addr.sin_addr)) < 1 )
{
printf("Invalid IP address\n");
exit(EXIT_FAILURE);
}
srv_addr.sin_port = htons(atoi(portNum));
if( connect(sock_fd, (struct sockaddr*) &srv_addr, sizeof(srv_addr)) < 0 )
{
perror("connect error");
exit(EXIT_FAILURE);
}
return sockfd;
}

Unable to connect 2 machines using ipv6 (TCP server client )

I was trying to do a simple tcp server client using ipv6. It works on the same machine for ipv6 and ipv4 but when on different machines ipv6 fails to connect.
Server Code
int main(int argc, char* argv[])
{
int sockfd,new_fd,rv,yes=1;
struct addrinfo hints,*servinfo,*p;
struct sockaddr_storage their_addr;
socklen_t addr_size;
SOCKET listenSocket,clientSocket;
WSADATA w;
if (WSAStartup(0x0101, &w) != 0)
{
fprintf(stderr, "Could not open Windows connection.\n");
exit(0);
}
//ip=argv[1];
//port=argv[2];
memset(&hints,0,sizeof(hints));
hints.ai_family=AF_INET6;
hints.ai_socktype=SOCK_STREAM;
hints.ai_flags=AI_NUMERICHOST;
if((rv = getaddrinfo("fe80::c0a8:0160","5002",&hints,&servinfo)) != 0)
{
perror("\nGetaddrinfo failed\n");
return 1;
}
//Creating socket
listenSocket = socket(servinfo->ai_family,servinfo->ai_socktype,servinfo->ai_protocol);
if(listenSocket == INVALID_SOCKET)
{
printf("\nSocket failed with error \n");
WSACleanup();
}
//setting non blocking mode
u_long iMode = 1;
rv = ioctlsocket(listenSocket,FIONBIO,&iMode);
if(rv == SOCKET_ERROR)
{
printf("\nioctl failed\n");
WSACleanup();
}
rv = bind(listenSocket,servinfo->ai_addr,(int)servinfo->ai_addrlen);
if(rv == SOCKET_ERROR)
{
perror("\nBind: \n");
}
freeaddrinfo(servinfo);
rv = listen(listenSocket,SOMAXCONN);
if(rv == SOCKET_ERROR)
{
perror("listen");
return 1;
}
// now accept an incoming connection:
char recvbuf[DEFAULT_BUFLEN];
int buflen = DEFAULT_BUFLEN;
SOCKET AcceptSocket;
while (1)
{
AcceptSocket = SOCKET_ERROR;
while (AcceptSocket == SOCKET_ERROR)
{
AcceptSocket = accept(listenSocket, NULL, NULL);
}
printf("Server: Client Connected!\n");
listenSocket = AcceptSocket;
rv = recv(listenSocket,recvbuf,buflen,0);
break;
}
printf("Received %d bytes from client \n",rv);
closesocket(listenSocket);
closesocket(AcceptSocket);
return 0;
}
Client Code
int main(int argc,char* argv[])
{
struct addrinfo hints,*servinfo,*p;
int rv;
SOCKET connectSocket;
WSADATA w;
if (WSAStartup(0x0101, &w) != 0)
{
fprintf(stderr, "Could not open Windows connection.\n");
exit(0);
}
//resetting memory
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
//getting values
if((rv = getaddrinfo("fe80::c0a8:160","5002",&hints,&servinfo)) != 0)
{
perror("Getaddrinfo failed");
return 1;
}
//Creating socket
connectSocket = socket(servinfo->ai_family,servinfo->ai_socktype,servinfo->ai_protocol);
if(connectSocket == INVALID_SOCKET)
{
perror("Socket create : ");
}
rv = connect(connectSocket,servinfo->ai_addr,(int)servinfo->ai_addrlen);
if(rv == SOCKET_ERROR)
{
perror("Socket Connect : ");
}
//free memory
freeaddrinfo(servinfo);
// Send and receive data.
int bytesSent;
char sendbuf[200] = "Client: Sending some test string to server...";
char recvbuf[200] = "";
bytesSent = send(connectSocket, sendbuf, strlen(sendbuf), 0);
printf("Client: send() - Bytes Sent: %ld\n", bytesSent);
closesocket(connectSocket);
return 0;
}
The aim is just to print how many bytes transferred.
It appears that you're using a link local address. Are you sure for that? Also, I'd suggest you check your firewall settings first.
EDIT:
Try to include the zone ID. When you issue the ipconfig in command prompt, you should be able to get addresses like fe80::c0a8:0160%21 where %21 is the zone ID. It's important when you use link local addresses according to this answer.

Using select() for non-blocking sockets

I am trying to use the select function to have non-blocking i/o between a server and 1 client (no more) where the communication flows nicely (can send at any time and the other will receive without waiting to send). I found a tutorial with some code and tried to adapt it to mine. This is what I have -
Server
#define PORT "4950"
#define STDIN 0
struct sockaddr name;
void set_nonblock(int socket) {
int flags;
flags = fcntl(socket,F_GETFL,0);
assert(flags != -1);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET)
return &(((struct sockaddr_in*)sa)->sin_addr);
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int agrc, char** argv) {
int status, sock, adrlen, new_sd;
struct addrinfo hints;
struct addrinfo *servinfo; //will point to the results
//store the connecting address and size
struct sockaddr_storage their_addr;
socklen_t their_addr_size;
fd_set read_flags,write_flags; // the flag sets to be used
struct timeval waitd; // the max wait time for an event
int sel; // holds return value for select();
//socket infoS
memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; //tcp
hints.ai_flags = AI_PASSIVE; //use local-host address
//get server info, put into servinfo
if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
//make socket
sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (sock < 0) {
printf("\nserver socket failure %m", errno);
exit(1);
}
//allow reuse of port
int yes=1;
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
//unlink and bind
unlink("127.0.0.1");
if(bind (sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
printf("\nBind error %m", errno);
exit(1);
}
freeaddrinfo(servinfo);
//listen
if(listen(sock, 5) < 0) {
printf("\nListen error %m", errno);
exit(1);
}
their_addr_size = sizeof(their_addr);
//accept
new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size);
if( new_sd < 0) {
printf("\nAccept error %m", errno);
exit(1);
}
set_nonblock(new_sd);
cout<<"\nSuccessful Connection!";
char* in = new char[255];
char* out = new char[255];
int numSent;
int numRead;
while(1) {
waitd.tv_sec = 10;
FD_ZERO(&read_flags);
FD_ZERO(&write_flags);
FD_SET(new_sd, &read_flags);
if(strlen(out) != 0)
FD_SET(new_sd, &write_flags);
sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
if(sel < 0)
continue;
//socket ready for reading
if(FD_ISSET(new_sd, &read_flags)) {
FD_CLR(new_sd, &read_flags);
memset(&in, 0, sizeof(in));
if(recv(new_sd, in, sizeof(in), 0) <= 0) {
close(new_sd);
break;
}
else
cout<<"\n"<<in;
} //end if ready for read
//socket ready for writing
if(FD_ISSET(new_sd, &write_flags)) {
FD_CLR(new_sd, &write_flags);
send(new_sd, out, strlen(out), 0);
memset(&out, 0, sizeof(out));
}
} //end while
cout<<"\n\nExiting normally\n";
return 0;
}
Client (basically the same just minus an accept call) -
#define PORT "4950"
struct sockaddr name;
void set_nonblock(int socket) {
int flags;
flags = fcntl(socket,F_GETFL,0);
assert(flags != -1);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET)
return &(((struct sockaddr_in*)sa)->sin_addr);
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int agrc, char** argv) {
int status, sock, adrlen;
struct addrinfo hints;
struct addrinfo *servinfo; //will point to the results
fd_set read_flags,write_flags; // the flag sets to be used
struct timeval waitd; // the max wait time for an event
int sel; // holds return value for select();
memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; //tcp
hints.ai_flags = AI_PASSIVE; //use local-host address
//get server info, put into servinfo
if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
//make socket
sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (sock < 0) {
printf("\nserver socket failure %m", errno);
exit(1);
}
if(connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
printf("\nclient connection failure %m", errno);
exit(1);
}
cout<<"\nSuccessful connection!";
set_nonblock(sock);
char* out = new char[255];
char* in = new char[255];
int numRead;
int numSent;
while(1) {
waitd.tv_sec = 10;
FD_ZERO(&read_flags);
FD_ZERO(&write_flags);
FD_SET(sock, &read_flags);
if(strlen(out) != 0)
FD_SET(sock, &write_flags);
sel = select(sock+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
if(sel < 0)
continue;
//socket ready for reading
if(FD_ISSET(sock, &read_flags)) {
FD_CLR(sock, &read_flags);
memset(&in, 0, sizeof(in));
if(recv(sock, in, sizeof(in), 0) <= 0) {
close(sock);
break;
}
else
cout<<"\n"<<in;
} //end if ready for read
//socket ready for writing
if(FD_ISSET(sock, &write_flags)) {
FD_CLR(sock, &write_flags);
send(sock, out, strlen(out), 0);
memset(&out, 0, sizeof(out));
}
} //end while
cout<<"\n\nExiting normally\n";
return 0;
}
The problem is that when I run them, nothing happens. I can type into one and hit enter and nothing shows up on the other screen (and vice versa). Thats not a whole of information for me to debug and this is my first real attempt at using select so I thought maybe I am just unaware of something simple. If anything can be spotted as wrong or weird please point it out, any help is appreciated.
The problem is that when I run them, nothing happens.
The real problem is that people have been pasting stuff from Beej for years without understanding it. That's why I don't really like that guide; it gives large blocks of code without really explaining them in detail.
You're not reading anything and not sending anything; no fgets, scanf, cin, etc. Here's what I would do:
FD_SET(sock, &read_flags);
FD_SET(STDIN_FILENO, &read_flags);
/* .. snip .. */
if(FD_ISSET(STDIN_FILENO, &read_flags)) {
fgets(out, len, stdin);
}
This will monitor stdin and read from it when input is available; then, when the socket is writeable (FD_ISSET(sock, &write_flags)), it will send the buffer.
I have the program working correctly now.
server -
#define PORT "4950"
#define STDIN 0
struct sockaddr name;
void set_nonblock(int socket) {
int flags;
flags = fcntl(socket,F_GETFL,0);
assert(flags != -1);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET)
return &(((struct sockaddr_in*)sa)->sin_addr);
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int agrc, char** argv) {
int status, sock, adrlen, new_sd;
struct addrinfo hints;
struct addrinfo *servinfo; //will point to the results
//store the connecting address and size
struct sockaddr_storage their_addr;
socklen_t their_addr_size;
fd_set read_flags,write_flags; // the flag sets to be used
struct timeval waitd = {10, 0}; // the max wait time for an event
int sel; // holds return value for select();
//socket infoS
memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; //tcp
hints.ai_flags = AI_PASSIVE; //use local-host address
//get server info, put into servinfo
if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
//make socket
sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (sock < 0) {
printf("\nserver socket failure %m", errno);
exit(1);
}
//allow reuse of port
int yes=1;
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
//unlink and bind
unlink("127.0.0.1");
if(bind (sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
printf("\nBind error %m", errno);
exit(1);
}
freeaddrinfo(servinfo);
//listen
if(listen(sock, 5) < 0) {
printf("\nListen error %m", errno);
exit(1);
}
their_addr_size = sizeof(their_addr);
//accept
new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size);
if( new_sd < 0) {
printf("\nAccept error %m", errno);
exit(1);
}
//set non blocking
set_nonblock(new_sd);
cout<<"\nSuccessful Connection!\n";
char in[255];
char out[255];
memset(&in, 0, 255);
memset(&out, 0, 255);
int numSent;
int numRead;
while(1) {
FD_ZERO(&read_flags);
FD_ZERO(&write_flags);
FD_SET(new_sd, &read_flags);
FD_SET(new_sd, &write_flags);
FD_SET(STDIN_FILENO, &read_flags);
FD_SET(STDIN_FILENO, &write_flags);
sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
//if an error with select
if(sel < 0)
continue;
//socket ready for reading
if(FD_ISSET(new_sd, &read_flags)) {
//clear set
FD_CLR(new_sd, &read_flags);
memset(&in, 0, 255);
numRead = recv(new_sd, in, 255, 0);
if(numRead <= 0) {
printf("\nClosing socket");
close(new_sd);
break;
}
else if(in[0] != '\0')
cout<<"\nClient: "<<in;
} //end if ready for read
//if stdin is ready to be read
if(FD_ISSET(STDIN_FILENO, &read_flags))
fgets(out, 255, stdin);
//socket ready for writing
if(FD_ISSET(new_sd, &write_flags)) {
//printf("\nSocket ready for write");
FD_CLR(new_sd, &write_flags);
send(new_sd, out, 255, 0);
memset(&out, 0, 255);
} //end if
} //end while
cout<<"\n\nExiting normally\n";
return 0;
}
The client is basically the same...only difference really is the lack of listen and accept.
I just want to add that above example may not work as expected on Linux. On Linux waitd may be modified by select. So for Linux, waitd should be rewritten before select.
See the "timeout" section in the man page for select.

Resources