Does fgets change the file descriptor set? - c

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.

Related

Broken Pipe for C-Socket. How to only keep server running?

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.

C - printf output varies depending on subsequent code [duplicate]

This question already has answers here:
Why does printf not flush after the call unless a newline is in the format string?
(10 answers)
Closed 5 years ago.
Preface: I am just engaging C, so forgive my incompetence. The cause of this problem is probably basic.
Problem: I am trying to read a file and serve it over http with a socket. For some reason, the printf output of a previously read file varies depending on how much of the subsequent code is included.
Here is my code.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
int main()
{
// open a file to serve
FILE *file;
file = fopen("index.html", "r");
if (file == NULL)
{
printf("Failed to open file.");
exit(EXIT_FAILURE);
}
// Get file content
char file_content[1024];
if (fgets(file_content, 1024, file) != NULL)
{
fclose(file);
// Add header to file content
char http_header[2048] = "HTTP/1.1 200 OK\r\n\n";
strncat(http_header, file_content, 1028);
// This output varies depending on inclusion of proceeding code.
printf("%s", http_header);
// create a socket
int server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
// define the address
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8001);
server_address.sin_addr.s_addr = INADDR_ANY;
bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));
listen(server_socket, 1);
int client_socket;
while (1)
{
client_socket = accept(server_socket, NULL, NULL);
send(client_socket, http_header, sizeof(http_header), 0);
close(client_socket);
}
return 0;
}
}
If I comment out everything past the printf statement, I get this intended output..
HTTP/1.1 200 OK
<html><body>Hi</body></html>
But if I run all the code, I get this instead..
HTTP/1.1 200 OK
If I can improve my question at all, please let me know how. Thank you.
Mark Benningfield and Yano helped me understand the problem, thanks so much to both of you.
When you execute printf(), instead of the string going directly into the terminal window, each line goes into a buffer.
Each line in the buffer must be flushed for them to show in the terminal. The buffer can be automatically flushed by these events
Strings ending in \n are automatically flushed, this is why the first line HTTP/1.1 200 OK \r\n\n displays fine. The second line <html><body>hi</body></html> does not end with a \n, or trigger any other automatic flush events, so it does not display.
Usually this is not a problem because when the program terminates, the buffer is automatically flushed, and all lines that entered the buffer through printf show up in the terminal. Since my program at the bottom enters an infinite while loop, the program never terminates, and the buffer is never flushed.
Calling fflush(stdout) after printf forces a write of stdout's entire buffer.
More info on fflush

When abruptly exiting a C program mid-loop, why do additional loop iterations occur?

Consider the basic client and server programs below (just bare bones / to illustrate my question). The client initiates a connection with the server, prompts the user to enter a message, which is then sent to the server and printed to screen.
If I abruptly quit the client program in the middle of the loop (e.g. by closing the terminal window), sometimes the client will continue to iterate through the loop for a period of time (i.e. the last message sent to the server / currently residing in the write buffer at the time the client is closed, is repeatedly sent to the server, typically until the loop is exhausted). Other times however, the read() call on the server correctly returns 0, and the connection is closed without issue (the behavior seems to be fairly random).
I don't quite understand what's going on here. First off, why do additional loop iterations occur after the program closes? Is there just a lag time between when the terminal window is closed, and when the actual process itself ends? Even if additional loop iterations do occur, shouldn't the call to fgets() block until a message is entered by the user?
I'm using Fedora 25 Workstation with XFCE desktop.
I tried searching for info on this, but didn't have much luck (I'm not sure how to search for this in a succinct way). Any help is much appreciated.
Thanks
CLIENT:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(3000);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
connect(sockfd, (struct sockaddr *)&server, sizeof(server));
int i;
for (i = 0; i < 20; i++) {
char buf[512];
printf("Send a message: ");
fgets(buf, 512, stdin);
write(sockfd, buf, sizeof(buf));
}
close(sockfd);
}
SERVER:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void) {
int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(3000);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
bind(listenfd, (struct sockaddr *)&server, sizeof(server));
listen(listenfd, 10);
printf("Listening...\n");
struct sockaddr_in client;
socklen_t client_size = sizeof(client);
int clientfd = accept(listenfd, (struct sockaddr *)&client, &client_size);
for (;;) {
char buf[512];
int i = read(clientfd, buf, sizeof(buf));
if (i == 0) {
close(clientfd);
printf("Connection Closed\n");
break;
} else {
printf("%s", buf);
}
}
close(listenfd);
}
When your terminal (and thus the remote/master side of the pty device connected to your process's stdin/out/err) is closed, fgets will see end-of-file status on stdin, and will return immediately with either an incomplete line (not ending in \n) or no input at all (null return value); in practice it's going to be the latter. If you checked the result, you would see this and be able to act accordingly.
in the server, this line:
printf("%s", buf);
should be replaced with:
*buf[i] = '\n';
printf( "%s", buf );
so there is a valid string to print (read() will not terminate the string)
Note: if a I/O error occurs or a signal occurs, etc then read() will return a value less than 0 and should result in exiting the for(;;;) loop, not continuing in the loop, printing the 'old' contents of buf[]
in this line in the client:
write(sockfd, buf, sizeof(buf));
if the write fails, it will return a value less than 0 if/when such an event occurs, the loop should exit, not continue looping,
It is very important to check all error conditions. such error checking (and the resulting handling of the error) can easily double the size of the code, but it must be done; otherwise such 'odd' events as you are seeing will happen, with no simple explanation of what happened.
When a system function returns an error indication, use the function perror() to have some text you provide displayed on stderr, along with the message from the system as to why it thinks the error occurred.
If I abruptly quit the client program in the middle of the loop (e.g. by closing the terminal window),
Closing the terminal window does not quit the client program -- it continues running, just with no input (so any reads from the now-closed terminal will return EOF). However, you never check the return value of fgets in the client so you you never notice, you just keep looping, sending the same buffer.
In addition, the code:
fgets(buf, 512, stdin);
write(sockfd, buf, sizeof(buf));
reads a line of up to 511 chars from the input, but then sends the entire 512 byte buffer, regardless of how long the actual message is. What you want is something more like:
if (fgets(buf, 512, stdin))
write(sockfd, buf, strlen(buf));
else
break; // there was an error or EOF on reading from stdin
Of course, this still has issues with lines longer than 511 bytes and then there's the issue that TCP does not preserve message boundaries, so on the server you might get more than one or only part of a message in a single read call.

C trying to understand select() and FD_ISSET()

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).

"Illegal seek" error when working with socket streams with non-empty read buffers

I'm currently writing a server application on Linux x86_64 using <sys/socket.h>.
After accepting a connection via accept(), I use fdopen() to wrap the retrieved socket into a FILE* stream.
Writing to, and reading from, that FILE* stream usually works quite well, but the socket becomes unsusable as soon as I write to it while it has a non-empty read buffer.
For demonstration purposes, I've written some code that listens for a connection, then reads the input, line by line, into a read buffer using fgetc(). If the line is too long to fit into the buffer, it's not completely read, but instead read during the next iteration.
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
FILE* listen_on_port(unsigned short port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0)
perror("bind failed");
listen(sock, 5);
int newsock = accept(sock, 0, 0);
return fdopen(newsock, "r+");
}
int main(int argc, char** argv) {
int bufsize = 8;
char buf[9];
buf[8] = 0; //ensure null termination
int data;
int size;
//listen on the port specified in argv[1]
FILE* sock = listen_on_port(atoi(argv[1]));
puts("New connection incoming");
while(1) {
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
//check if the read failed due to an EOF
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
//try to write ack
if(fputs("ack\n", sock) == EOF)
perror("sending 'ack' failed");
//try to flush
if(fflush(sock) == EOF)
perror("fflush failed");
}
puts("Connection closed");
}
The code should compile in gcc without any special parameters. Run it with the port number as argument and use netcat to connect to it locally.
Now, if you try sending strings that are shorter than 8 characters, this will run flawlessly.
But if you send a string containing more than 10 characters, the program will fail.
This sample input:
ab
cd
abcdefghij
Will create this output:
New connection incoming
Input line: 'ab'
Input line: 'cd'
Input line: 'abcdefgh'
fflush failed: Illegal seek
EOF: Connection reset by peer: Illegal seek
Connection closed
As you see, (rightly) only the first 8 characters of abcdefgh are read, but when the program tries to send the 'ack' string (which the client never receves), and then flush the output buffer, we receive an Illegal seek error, and the next call to fgetc() returns EOF.
If the fflush() part is commented out, the same error still occurs, but the
fflush failed: Illegal seek
line is missing from the server output.
If the fputs(ack) part is commented out, everything seems to work as intended, but a perror() manually called from gdb still reports an 'Illegal seek' error.
If both fputs(ack) and fflush() are commented out, everything does work as intended.
Unfortunately, I've not been able to find any good documentation, nor any Internet discussions on this problem, so your help would be greatly appreciated.
edit
The solution i finally settled for is to not use fdopen() and FILE*, since there seems to be no clean way of converting a socket fd into a FILE* that can reliably used in r+ mode.
Instead, I directly worked on the socket fd, writing my own replacement code for fputs and fprintf.
If anyone needs it, here is the code.
Clearly "r+" (read/write) mode does not work on sockets in this implementation, no doubt because the underlying code assumes that it must do a seek to switch between reading and writing. This is the general case with stdio streams (that you must do some kind of synchronizing operation), because back in the Dim Time, actual stdio implementations had only a single counter per stream, and it was either a counter of "number of characters left to read from stream buffer via getc macro" (in read mode) or "number of characters that can safely be written to stream buffer via putc macro (in write mode). To that that single counter re-set, you had to do a seek-type operation.
Seeks are not allowed on pipes and sockets (since "file offset" is not meaningful there).
One solution is not to wrap a socket with stdio at all. Another, probably easier / better for your purposes, is to wrap it with, not one, but two stdio streams:
FILE *in = fdopen(newsock, "r");
FILE *out = fdopen(newsock, "w");
There's another flaw here though, because when you go to fclose one stream, that closes the other's file descriptor. To work around that, you need to dup the socket descriptor once (in either of the two calls above, it does not matter which one).
If you intend to use select or poll or similar on the socket at some point, you should generally go for the "don't wrap with stdio" solution, since there's no nice clean portable way to track stdio buffering. (There are implementation-specific ways).
Don't use fflush() on network sockets. They are unbuffered streams.
Also, this code:
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
does not read a single line. It only reads up to the buffer size, which you defined as 8. sock will still have data for you to receive which you should receive before writing to the stream with fputs. BTW you can replace that whole block with
fgets(buf, bufsize, sock);
Yes, you can use one file stream to handle your socket, at least on Linux.
But you should be careful with it: you must only use ferror() to test for errors. I have some code that use this and run flawlessly in production on a major French site.
If you use errno or perror() you'll catch any internal error that the stream will encounter, even if it wants to hide it to you. And "Illegal seek" is one of them.
Also, to test for real EOF conditions, you should use feof(), since when returning true it's mutually exclusive with ferror() returning a non-zero value. It's because, when using fgetc() you don't have any mean to differentiate error from real EOF conditions. So you should probably better use fgets() as another user pointed out.
So, your test:
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
Should be written as:
int sock_error = ferror(sock);
if (sock_error) {
fprintf(stderr, "Error while reading: %s", strerror(sock_error));
} else {
printf("Input line: '%s'\n", buf);
}
Try this :
#define BUFSIZE 88
FILE* listen_on_port(unsigned short port) {
...
}
int main(int argc, char** argv) {
int bufsize = BUFSIZE;
char buf[ BUFSIZE ];

Resources