I'm trying to write a very basic chat client in C which communicates with another machine using sockets, and I'm having some issues with understanding select. Here's a quick snippet of the relevant code.
while(1)
{
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(STDIN, &writefds);
FD_SET(connectFD, &readfds);
select(connectFD+1, &readfds, &writefds, NULL, NULL);
if(FD_ISSET(connectFD, &readfds) != 0)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
int lastBit;
lastBit = recv(connectFD, buf, sizeof(buf), 0);
if (lastBit > 0 && lastBit < 1024)
{
buf[lastBit] = '\0';
}
else
{
close(connectFD);
break;
}
printf("%s\n", buf);
}
else if (FD_ISSET(STDIN, &writefds))
{
char msg[1024];
memset(msg, 0, sizeof(msg));
read(STDIN, msg, sizeof(msg));
}
}
}
What I'm looking to do is have incoming messages processed as soon as they arrive, and only have outgoing messages sent after I hit ENTER, but what I have now only processes incoming data after I hit ENTER instead of immediately. I assume it's because read is a blocking call and select is returning when there's ANY data in the buffer, not just when there's a newline (which is when read returns), but I don't know how to process this otherwise. Any advice or tips for leading me down the right path?
Thank you guys so much!
FD_SET(STDIN, &writefds);
You want to read from STDIN so you should add STDIN to the &readfds and not &writefds. Writing to STDIN is almost always possible so your code effectively got the information that writing is possible to STDIN but then attempts to read from STDIN and hangs there until the read actually gets possible.
Related
I'm working on a client program that will operate as a basic instant messenger. I'm using pthread to to open up a thread dedicated to waiting for a message to be received and the the message to be read. Is using pthread_cond_wait the correct way to go about waiting for read(sockfd, buffer, 256) to be above 0?
void *threadRead() {
while (1) {
bzero(buffer,256);
pthread_cond_wait(&buffer_lock, read(sockfd, buffer, 255) > 0);
n = read(sockfd, buffer, 255);
printf("%s\n",buffer);
}
}
You see I just need to wait until read() comes back with a value above 0 to continue and I can't find the right system to do that. If anyone could link something that would put me on the right track or give me a hint that would be great.
No. pthread_cond_wait() is for waiting on a condition that will be changed by one of your other threads.
If you just want to wait for read() to return something, just call read(). Unless you have specifically marked the socket as non-blocking, it will block the calling thread until there is something to return.
If read() ever returns 0 then it indicates end of file: it means that the socket has been closed on the remote side, so there will never be any more to read.
You should use select() instead, like this
int running;
running = 1;
while (running != 0) /* Just in case you want to end the loop, you can */
{
fd_set rdset;
struct timeval timeout;
timeout.tv_sec = NUMBER_OF_SECONDS_TO_WAIT;
timeout.tv_usec = YOU_CAN_HAVE_MICRO_SECONDS_PRECISION;
FD_ZERO(&rdset);
FD_SET(fd, &rdset);
if (select(fd + 1, &rdset, NULL, NULL, &timeout) == 1)
{
ssize_t length;
char buffer[100];
length = read(fd, buffer, sizeof(buffer));
/* use buffer now */
}
else
{
/* Timed out and still nothing to read */
/* do something meanwhile and retry if */
/* you want to. */
}
running = use_a_function_to_check_this();
}
you can use it in a different thread, but you need to be careful.
Non-blocking IO is difficult, it doesn't matter how you implement it is hard.
One more thing, this
n = read(sockfd, buffer, 255);
printf("%s\n",buffer);
is likely undefined behavior, since apparently buffer is
char buffer[256];
you could
n = read(sockfd, buffer, 255 /* or sizeof(buffer) - 1 */);
buffer[n] = '\0';
printf("%s\n",buffer);
ensuring that buffer is nul terminated.
I am currently trying to write a program in C which will read from two named pipes and print any data to stdout as it becomes available.
for example: If I open two terminals and ./execute pipe1 pipe2 in one of the terminals (with pipe1 and pipe2 being valid named pipes) and then type echo "Data here." > pipe1 then the name of the pipe (here it is pipe1), the size, and the data should print to stdout-- Here it would look like pipe1 [25]: Data here.
I know I need to open the pipes with the O_RDONLY and O_NONBLOCK flags. I have looked at many examples (quite a few on this forum) of people using select() and I still don't understand what the different parameters being passed to select() are doing. If anyone can provide guidance here it would be hugely helpful. Below is the code I have so far.
int pipeRouter(char[] fifo1, char[] fifo2){
fileDescriptor1 = open(fifo1, O_RDONLY, O_NONBLOCK);
fileDescriptor2 = open(fifo2, O_RDONLY, O_NONBLOCK);
if(fileDescriptor1 < 0){
printf("%s does not exist", fifo1);
}
if(fileDescriptor2 < 0){
printf("%s does not exist", fifo2);
}
}
The select lets you wait for an i/o event instead of waisting CPU cycles on read.
So, in your example, the main loop can look like:
for (;;)
{
int res;
char buf[256];
res = read(fileDescriptor1, buf, sizeof(buf));
if (res > 0)
{
printf("Read %d bytes from channel1\n", res);
}
res = read(fileDescriptor2, buf, sizeof(buf));
if (res > 0)
{
printf("Read %d bytes from channel2\n", res);
}
}
If you add the code and run it, you would notice that:
The program actually does what you want - it reads from both pipes.
CPU utilization is 100% for one core, i.e. program wastes CPU even when there is no data to read.
To solve issue, select and poll APIs are introduced. For select we need to know descriptors (we do), and the maximum out of them.
So let's modify the code a bit:
for (;;)
{
fd_set fds;
int maxfd;
FD_ZERO(&fds); // Clear FD set for select
FD_SET(fileDescriptor1, &fds);
FD_SET(fileDescriptor2, &fds);
maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2;
select(maxfd + 1, &fds, NULL, NULL, NULL);
// The minimum information for select: we are asking only about
// read operations, ignoring write and error ones; and not
// defining any time restrictions on wait.
// do reads as in previous example here
}
When running the improved code, the CPU would not be wasted as much, but you will notice, that the read operation is performed even when there is no data for a particular pipe, but there is for another.
To check, which pipe actually has the data, use FD_ISSET after select call:
if (FD_ISSET(fileDescriptor1, &fds))
{
// We can read from fileDescriptor1
}
if (FD_ISSET(fileDescriptor2, &fds))
{
// We can read from fileDescriptor2
}
So, after joining said above, the code would look like:
for (;;)
{
fd_set fds;
int maxfd;
int res;
char buf[256];
FD_ZERO(&fds); // Clear FD set for select
FD_SET(fileDescriptor1, &fds);
FD_SET(fileDescriptor2, &fds);
maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2;
select(maxfd + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(fileDescriptor1, &fds))
{
// We can read from fileDescriptor1
res = read(fileDescriptor1, buf, sizeof(buf));
if (res > 0)
{
printf("Read %d bytes from channel1\n", res);
}
}
if (FD_ISSET(fileDescriptor2, &fds))
{
// We can read from fileDescriptor2
res = read(fileDescriptor2, buf, sizeof(buf));
if (res > 0)
{
printf("Read %d bytes from channel2\n", res);
}
}
}
So, add error handling, and you would be set.
Hello i need to read all data from socket, have such function
void handle_socket(int sockfd) {
int len;
int n;
char buf[1024];
n = recv(sockfd, buf, sizeof(buf) - 1, 0);
while (n > 0) {
buf[n] = '\0';
printf("%s", buf);
n = recv(sockfd, buf, sizeof(buf) - 1, 0);
}
printf("Exiting\n");
}
But i can't see Exiting in terminal, what i am do wrong?
Socket sockfd = socket(AF_INET, SOCK_STREAM, 0);
PS i need to read response from POP3 server i try to find \r\n.\r\n but i can't be sure that this string will be at one buffer(for example \r\n at 1st buffer and .\r\n at 2nd
recv() function doesn't return 0 (EOF) untill process on the other end close connection and there is no data left to read. So you will see "Exiting" when the other process close connection.
Your function override old data by new one. You should decide whether you put a limit on the size of buffer or dynamically expand memory for new data. I don't know what your restrictions are, but I recommend to create FILE instance by calling fdopen() and work with standard functions like fgetc, fgets or getLine.
The objective of my program is to use select to manage multiple sockets. However, I thought of trying it with one socket first. Now, the problem that I am facing is that initially client sends data to server, and server receives it and displays it, but then when client again sends some data, the server code remains still at select command.
here are some snippets that will give you an idea of how I am initializing the socket.
if((master_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
exit(1);
}
if((bind(master_socket, (struct sockaddr *)&req, sizeof(req))) < 0)
{
exit(1);
}
listen(master_socket, 5);
FD_SET(master_socket,&listening);
/* wait for connection, then receive and print text */
len = sizeof(struct sockaddr);
while(1)
{
FD_ZERO(&listening); //Flush out everything in socket
FD_SET(master_socket,&listening); // Add master
if(f_client>0) // Add client if any
{
FD_SET(f_client,&listening);
}
printf("Checking for new connection \n");
//Timeout is null, so waiting indefinitely
rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
if (FD_ISSET(master_socket, &listening))
{
printf("Master side invoked\n");
if((f_client = accept(master_socket, (struct sockaddr *)&req, &len)) < 0)
{
exit(1);
}
}
else if (FD_ISSET(f_client,&listening))
{
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
}
}
Basically in above program, it connects to the server, maintains a file descriptor for client f_client and add it. And in every round, it clears the socket, add master socket, and client socket if any, and then checks. Problem here is, first time it works, but second time when client sends some data. it gets hang to rc = select(FD_SETSIZE, &listening, NULL, NULL, NULL);
I am not to understand what is wrong here. Can anyone help?
if ((valread = read( f_client , buf, 1024)) == 0)
{
close(f_client);
f_client=0;
}
else
{
fputs(buf, stdout);
}
This code is broken. The fputs function can only be used with a C-style string. You just have arbitrary data with no particular structure. Since you ignore valread, you also have no idea how many bytes you read. (Think about it, how could fputs possibly know how many bytes to output? That information is only in valread, and you don't pass it that information.)
You've already received the data, this broken code just threw it away. If you log valread, you'll see that you actually already read it in your last call to read before the call to select that hung.
instead of fputs, you could use something like this:
for (int i = 0; i < valread; ++i)
putchar(buf[i]);
I basically have a server set up and I'm accepting new clients(UNIX) and i'm using select() command to wait for activity on file descriptor but I'm not sure how to write from the clients side and then read it on the servers side
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
while (1) {
testfds = readfds;
select(4 + MAXCLIENTS, &testfds, NULL, NULL, NULL);
for (fd = 0; fd < 4 + MAX_CLIENTS; fd++) {
if (FD_ISSET(fd, &testfds)) {
if (fd == server_sockfd) { /* new connection request */
client_sockfd = accept(server_sockfd, NULL, NULL);
if (num_clients < MAXCLIENTS) {
FD_SET(client_sockfd, &readfds);
num_clients++;
} else {
sprintf(message, "XSorry, too many clients. Try again later.\n");
write(client_sockfd, message, strlen(message));
close(client_sockfd);
}
} else if (fd == STDIN_FILENO) {
fgets(kb_message, BUFSIZ + 1, stdin);
if (strcmp(kb_message, "quit\n") == 0) {
sprintf(message, "XServer is shutting down.\n");
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++) {
if (FD_ISSET(fd2, &readfds)) {
write(fd2, message, strlen(message));
close(fd2);
}
}
close(server_sockfd);
exit(EXIT_SUCCESS);
} else {
sprintf(message, "M%s", kb_message);
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++)
if (FD_ISSET(fd2, &readfds))
write(fd2, message, strlen(message));
}
} else { /* client leaving */
close(fd);
FD_CLR(fd, &readfds);
num_clients--;
}
}
}
}
How would I handle write request from clients and then write back to them, would it be under "else" and how can I check if client is exiting or writing.
Thanks
The most common mistake with select(2) is not re-initializing the descriptor sets, since second, third, and forth arguments are input-output parameters.
Setup an fd_set, for reading before the outer loop, add listening socket descriptor to it, enter the loop, make a copy of the this fd_set and give the copy to select(2). When new connection arrives, add its descriptor to the original fd_set. Same for closed socket (error or EOF on read(2)) - remove the descriptor from the original fd_set.
Hope this helps.
You are correct in thinking you need the read code in your 'else' block. If a file descriptor triggers and it isn't stdin or the 'connect' descriptor, then it is one of your clients attempting to send you data. When one of those file descriptors is triggered in the select, you need to call 'read' on that descriptor to read the data into the buffer. The read command will return you the number of bytes read. If this is a positive number, then it indicates the client has sent you data. If it is zero, then that indicates that the client has ended the TCP connection to your server.
The else block will look something like:
else
{
//Existing connection has data for us to read
if((nBytes = read(fd, buffer, MAXBUFFER)) <= 0)
{
if(nBytes == 0)
{
//Actually, its sending us zero bytes, connection closed
printf("Socket %d hung up\n", fd;
}
else
printf ("Read Error"\n)
}
Also, Follow Nikolai N Fetissov's advice above and make sure that when client's connect you store their fd in a permanent fd_set structure, as the one you are using is being modified by the select call.
Your problem might be that you have a variable called read. It's going to mask one of hte functions you need to use - the read() system call to get data out of the socket. The client puts it in with write(). You might also want to check the return value from select(), which will tell you how many of the file descriptors are ready for reading. Then you can check which ones using FD_ISSET(). It looks like you're doing that part already (except you seem to be checking the wrong variable?)... just call read() on that file descriptor to get out the data the client wrote.
else
{
bzero(buf,100);
n=read(i,buf,100); // Read the client message
buf[n]='\0';
if(n==0) // Check the client is closed or not
{
printf("%d is closed\n",i);
close(i);
FD_CLR(i, &master);
if(i==fdmax)
fdmax--;
}
else
{
n=strlen(buf);
write(1,buf,n);
fflush(stdout);
write(1,"Enter the message\n",18);
bzero(buf,100);
read(0,buf,100);
buf[n]='\0';
write(i,buf,n);
fflush(stdout);
}
}
Notes:
After accept the client, Add the client in the fd set.
Then read the message from the client
If the message is equal to 0, then the client is closed.
If you want to send the message to the client, using the client fd, you can send to the client