Im trying to make a basic non blocking chat client, but i cant really understand select() and FD_ISSET(). im trying to listen to the socket with the code below, but it wont work, it doesn't print anything, why not?
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
int main(int argc, char const* argv[])
{
fd_set readfs;
char sendline[100];
char str[100];
char *some_addr;
int listen_fd, comm_fd;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
//Socket error
if (listen_fd == -1) {
printf("Error on getting socket, Exiting!\n");
return 1;
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port=htons(22000);
bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listen_fd, 10);
comm_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL);
FD_ZERO(&readfs);
FD_SET(comm_fd, &readfs);
while (1)
{
select(listen_fd,&readfs, NULL, NULL, NULL);
if(FD_ISSET(listen_fd,&readfs))
{
bzero(str,100);
read(listen_fd,str,100);
printf("%s", str);
/* write(listen_fd, "read!", strlen(str)+1); */
}
}
return 0;
}
EDIT:
My code trying to Connect to the server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
int main(int argc,char **argv)
{
int sockfd,n;
char sendline[100];
char recvline[100];
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof servaddr);
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(22000);
inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
while(1)
{
bzero( sendline, 100);
bzero( recvline, 100);
fgets(sendline,100,stdin); /*stdin = 0 , for standard input */
write(sockfd,sendline,strlen(sendline)+1);
read(sockfd,recvline,100);
printf("%s\n",recvline);
}
return 0;
}
Four major problems, here:
Your select() call and the read/write loop should be using comm_fd, not listen_fd. If you call select() on listen_fd it'll return when there is an accept()able connection available, but you want to wait on the connected socket you already have for input, so use comm_fd.
The first argument to select() should be the highest file descriptor in the sets plus one. Since you only have one file descriptor, here, it should be comm_fd + 1.
You should move your FD_ZERO and FD_SET macros inside the while loop, and execute them prior to every select() call, because select() is going to modify those fd sets you pass to it.
You don't check the return from your system calls for errors. You should.
Other points:
bzero() has been removed from POSIX for quite some time, now, you should be using the standard memset() instead.
You shouldn't pass INADDR_ANY though htons(), just use it as it is.
It's only a comment in your program, but while STDIN_FILENO may be 0, stdin is a FILE pointer, and is not 0.
but i cant really understand select() and FD_ISSET()
An fd_set is like a bit array. Each bit in the array represents a socket or file descriptor.
FD_ISSET() is a macro or function that tells you whether a given socket descriptor (4, for example) is set in the bit array (fd_set). FD_SET() allows you to set a bit yourself, and FD_CLR() lets you clear a bit.
The bits don't just get set magically, you use select() to ask the OS kernel to set or clear each bit in the fd_set accordingly, then you check each bit with FD_ISSET() and act accordingly. Before calling select() you must setup the sets to tell the kernel which descriptors you are interested in polling by setting the bits in the fd_set using FD_SET() or if you have lots of sockets/bits to set, using a master fd_set and copying the whole thing to your read, write or error set. I usually did the latter for efficiency. These are integers typically from 0 to N (first 3 are usually not sockets so you normally poll 3 .. N). After select returns, you must check the bits. If a bit is set in readfds it is ready for reading.
select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
The supported statuses are "ready for read", "ready for write", and "error condition"
If you don't set a particular bit in the set, the kernel won't report its status to you.
As well, if you don't set the nfds param (max descriptor value) high enough, any descriptors above the max will be ignored. The descriptors do not have to be contiguous, just within the range of nfds.
All of this logic assumes successful return values on the system calls. If a system call returns an error status, you don't even regard the data structures for that call and must recover or process appropriately.
The primary problem that jumps out at me in your code is your select call's first argument. It isn't going to check comm_fd is comm_fd is lower than listen_fd.
I recommend you keep an int value of max_desc and each each time you accept a new socket, set max_desc = MAX(max_desc, new_fd+1), as well, you'll need to adjust it downward when closing out sockets. I always prefer to keep a separate fd_set just to track the descriptors my process has open (never pass it to select() just use it for bookkeeping).
Related
So, I can use FD_ZERO, FD_SET and FD_ISSET to mess around with the file descriptor set. The select() function may now be used to observe if a certain file object becomes readable (hope my wording is right here). What I want to do is: Read in a sequence of characters from stdin and then send this sequence to a server. I leave out some error messages of the socket stuff here, however, because my question is about FD_ISSET.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet.in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
static char buffer[256];
int sock_fd, length, port;
struct sockaddr_in server_addr;
fd_set input_fdset
sock_fd = socket(PF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
connect(sock_fd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));
while (1)
{
FD_ZERO(&input_fdset);
FD_SET(STDIN_FILENO, &input_fdset);
FD_SET(sock_fd, &input_fdset);
if (select(sock_fd+1, &input_fdset, NULL, NULL, NULL) == -1)
perror("some error");
if (FD_ISSET(STDIN_FILENO, &input_fdset))
{
if(fgets(buffer, 256, stdin) == NULL)
{
printf("connect: Closing socket.");
break;
}
length = strlen(buffer);
send(sock_fd, buffer, length, 0);
}
else
{
length = recv(sock_fd, buffer, 256, 0);
if(length == 0)
{
printf( "Connection closed by remote host.");
break;
}
write(STDOUT_FILENO, buffer, length);
}
}
close(sock_fd);
return(0);
}
You can probably guess I came across this code.
I have troubles to understand the following: Within the while-loop, I add both the socket object and stdin to the file descriptor set. In the next step select checks whether there is anything to read from either of these two objects. Then the if(FD_ISSET(STDIN_FILENO, &input_fdset)) should ALWAYS be executed, right? I mean the condition is always fulfilled, because we set the STDIN_FILENO using FD_SET(STDIN_FILENO, &input_fdset). Now, I am within this if-statement. fgets reads in my terminal input line by line. When I end this input with ENTER, a newline-character is appended to my buffer and reading from stdin stops. But only until the while-loop is executed again, right? Because in that case fgets gets called again as well. Btw I think it is kind of weird to have fgets only within this if-statement. Shouldn't it be called before? How can fgets become NULL? Only if EOF is reached without reading in any character. This won't happen though, will it? I cannot reach EOF when I am reading from stdin. I think. And now the last question: When would I execute the else-block? For this to happen, the previous if-statement would need to fail. But as I said, if(FD_ISSET(STDIN_FILENO, &input_fdset) will never fail.
Ofc I realize I am missunderstanding this code.
Can somebody help me figure this out?
From the documentation for select (man select):
"On return, select() replaces the given descriptor sets with subsets consisting of those descriptors that are ready for the requested operation."
The purpose of FD_ISSET is to determine which sets are ready to be read. The set is changed by the select call, by design.
I'm learning socket programming in C. I have gotten my server to create a socket that was successful, but when I try to bind my socket to a port nothing happens. No error occurs and it is not successful. It's as if the bind() function is not even executing at all.
I've checked out the documentation on the bind() function here but there's no mention of why it won't execute at all. I've also tried searching through this site with no avail.
I also tried following this tutorial from start to finish but the error (or lack thereof) still occurs.
Here is my full code leading up to the problem:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "include.h"
int main() {
// Descriptors. Used to check the status of functions such as socket, listen, bind etc.
// If a descriptor is equal to 0, then everything is okay. Else, if they are equal to -1, something went wrong.
int socketDescriptor, newSocketDescriptor = 1;
// The process ID of a child process (the client) when a new one is spawned (the client connects).
pid_t childPID;
// A string to hold the commands being sent a received.
char* commandBuffer = calloc(BUFFER_SIZE, sizeof(char));
// A structure to hold information on the server address.
struct sockaddr_in serverAddress;
memset(&serverAddress, '\0', sizeof(serverAddress));
// Fill in the server address information.
// Set the address family to AF_INET, which specifies we will be using IPv4.
// htons() takes the given int and converts it to the appropriate format. Used for port numbers.
// inet_addr() takes the given string and converts it to the appropriate format. Used for IP addresses.
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(PORT);
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
// A structure to hold information a client when a new one connects to this server.
struct sockaddr_in clientAddress;
memset(&clientAddress, '\0', sizeof(clientAddress));
// socklen_t defines the length of a socket structure. Need this for the accept() function.
socklen_t addressSize;
// Creating the socket.
// AF_NET specifies that we will be using IPv4 addressing.
// SOCK_STREAM specifies that we will be using TCP to communicate.
socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (socketDescriptor < 0) {
perror("ERROR CREATING SOCKET");
exit(1);
}
else
printf("Socket created successfully.\n");
// Binding to the specified port. 0 if everything is fine, -1 if there was an error.
if (bind(socketDescriptor, (struct sockaddr*) & serverAddress, sizeof(struct sockaddr_in)) < 0) {
perror("ERROR BINDNING");
exit(1);
}
else
printf("Socket bound to %s:%s.\n", serverAddress.sin_addr.s_addr, serverAddress.sin_port);
The last if statement at the bottom is where the code fails. It should either print and error or print "Socket bound to 127.0.0.1:80" but neither happens. See an example here.
I'm lost for what to do.
A server socket won't show up in a netstat listing unless you call listen after binding the socket.
Also, you're using the %s format specifier in your printf after the bind call on serverAddress.sin_addr.s_addr and serverAddress.sin_port. These are not strings but integers. Using the wrong format specifier invokes undefined behavior and is likely causing your program to crash. Using the correct format specifier such as %d or %x will fix this.
if (bind(socketDescriptor, (struct sockaddr*)&serverAddress, sizeof(struct sockaddr_in)) < 0) {
perror("ERROR BINDNING");
exit(1);
}
else
// use %x to print instead
printf("Socket bound to %x:%x.\n", serverAddress.sin_addr.s_addr, serverAddress.sin_port);
if (listen(socketDescriptor, 3) < 0) {
perror("listen failed");
} else {
printf("socket is listening\n");
}
In a simple program where I'm trying to send command-line inputs from client to server, I keep getting a "Broken Pipe" for the server side. I send a string to the server and the server returns the string as lower-case to the client.
Server:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include <ctype.h>
#include <unistd.h>
int main()
{
char str[100];
int listen_fd, comm_fd;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
bzero( &servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(37892);
bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listen_fd, 10);
comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
while(1){
bzero( str, 100);
read(comm_fd,str,100);
for(int i = 0; i < strlen(str); i++){
str[i] = tolower(str[i]);
}
printf("Echoing back - %s",str);
write(comm_fd, str, strlen(str)+1);
}
}
Client
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>
#include<ctype.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int sockfd,n;
char sendline[100];
char recvline[100];
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof servaddr);
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(37892);
inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(argc==1) printf("\nNo arguments");
if (1){
{
bzero( sendline, 100);
bzero( recvline, 100);
strcpy(sendline, argv[1]);
write(sockfd,sendline,strlen(sendline)+1);
read(sockfd,recvline,100);
printf("%s",recvline);
}
}
}
The problem I found was that when the client's side is done sending the string, the command line input does not work like fgets() where the loop will wait for another user input. If I change the if(1) in the client's side to a while(1), it will obviously run an infinite loop as no new inputs are being added.
The dilemma is, how would I be able to keep the server's side running to continuously return the string to the client while processing single requests from the command line on the client's side?
Your program has two problems:
1) read() works differently than you think:
Normally read() will read up to a certain number of bytes from some file or stream (e.g. socket).
Because read() does not distinguish between different types of bytes (e.g. letters, the end-of-line marker or even the NUL byte) read() will not work like fgets() (reading line-wise).
read() is also allowed to "split" the data: If you do a write(..."Hello\n"...) on the client the server may receive "Hel" the first time you call read() and the next time it receives "lo\n".
And of course read() can concatenate data: Call write(..."Hello\n"...) and write(..."World\n"...) on the client and one single read() call may receive "Hello\nWorld\n".
And of course both effects may appear at the same time and you have to call read() three times receiving "Hel", "lo\nWo" and "rld\n".
TTYs (= the console (keyboard) and serial ports) have a special feature (which may be switched off) that makes the read() call behave like fgets(). However only TTYs have such a feature!
In the case of sockets read() will always wait for at least one byte to be received and return the (positive) number of bytes received as long as the connection is alive. As soon as read() returns zero or a negative value the connection has been dropped.
You have to use a while loop that processes data until the connection has been dropped.
You'll have to check the data received by read() if it contains the NUL byte to detect the "end" of the data - if "your" data is terminated by a NUL byte.
2) As soon as the client drops the connection the handle returned by accept() is useless.
You should close that handle to save memory and file descriptors (there is a limit on how many file descriptors you can have open at one time).
Then you have to call accept() again to wait for the client to establish a new connection.
Your client sends one request and reads one response.
It then exits without closing the socket.
Your server runs in a loop reading requests and sending responses.
Your server ignores end of stream.
Little or none of this code is error-checked.
Hello my server program is :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
int sfd, cfd;
int ch='k';
struct sockaddr_in saddr, caddr;
sfd= socket(AF_INET, SOCK_STREAM, 0);
saddr.sin_family=AF_INET; /* Set Address Family to Internet */
saddr.sin_addr.s_addr=htonl(INADDR_ANY); /* Any Internet address */
saddr.sin_port=htons(29008); /* Set server port to 29008 */
/* select any arbitrary Port >1024 */
bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
listen(sfd, 1);
while(1) {
printf("Server waiting...");
cfd=accept(sfd, (struct sockaddr *)NULL, NULL);
if(read(cfd, &ch, 1)<0) perror("read");
ch++;
if(write(cfd, &ch, 1)<0) perror("write");
close(cfd);
}
}
so i got a simple server program and i also got a client program. The problem i have is that when i run both at the same machine on different terminals the client output works fine. The server although doesnt print the waiting line and also it stacks making me unable to use terminal. Whats the problem ?
As the comment points out, you need send a new line or fflush(stdout) to make the server print what you want, or disable buffering entirely before operating on stdout: setbuf(stdout, NULL), which is not necessary. By default, stdout is line buffered, stderr is none buffered. And note that Microsoft runtime libraries do not support line buffering, so if you implement this program with Winsock, it will print immediately.
Before bind(), you can set the SO_REUSEADDR option so that when your program exits uncleanly(in the code above, you didn't close the listening socket sfd explicitly, though the OS will clean up upon termination, but it's a good practice to do so), the port which may remain in TIME_WAIT state can be re-used immediately.
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))
If you want to use the current terminal after you run the program, add a & after your command, so the program will run in the background.
For a programming project in school we have to design a basic client/server setup using tcp protocol and then udp protocol. I already got the TCP working using read() and write() from the C Socket library. I now need to create a "reliable UDP" system. For instance:
"When the server receives the length message it will wait up to 500 milliseconds for that number of bytes to be sent. If it receives the correct number of bytes, it will respond with a string containing the characters "ACK" (a common abbreviation for an acknowledgement). If it does not receive the correct number of bytes by the end of the timeout period, the server will give up and silently exit."
I have the sendto() and recvfrom() functions set up, but I am not sure how to do the timeout feature so that it only waits 500ms for the second msg to be sent. I also have to do it later on the client side if it doesn't receive the "ACK" and resend the length msg + msg a few times. How can do I do the timeout?
Add the following function in your program and use it instead of using the recvfrom function directly.
the following functiong has the same input parameter of recvfrom function + a timeout input parameter in the last
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
int timeout_recvfrom (int sock, char *buf, int *length, struct sockaddr_in *connection, int timeoutinseconds)
{
fd_set socks;
struct timeval t;
FD_ZERO(&socks);
FD_SET(sock, &socks);
t.tv_sec = timeoutinseconds;
if (select(sock + 1, &socks, NULL, NULL, &t) &&
recvfrom(sock, buf, *length, 0, (struct sockaddr *)connection, length)!=-1)
{
return 1;
}
else
return 0;
}
An alternate without using select or poll is using socket options SO_RCVTIMEO and SO_SNDTIMEO
tv.tv_sec = 10; /* seconds */
tv.tv_usec = 0;
if(setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)
printf("Cannot Set SO_SNDTIMEO for socket\n");
if(setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
printf("Cannot Set SO_RCVTIMEO for socket\n");
SO_RCVTIMEO and SO_SNDTIMEO socket options
If you have a single socket to either read or write to/from, then this is a better option. Where as if you are using multiple sockets and wish to carry on with the one which completely sent / received data, then perhaps select would be more appropriate.