Server socket in C, bug with "\n" [duplicate] - c

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 4 years ago.
I'm taking a class in the college that require me to create a server-client application, i came up with this code but apparently it has some strange error when I remove the "\n" at the end of the printf(). I already checked with my teacher and it was him who found out that this is was causing my server to freeze and display latter when we send the message, so he put \n in the end of each print(in the beggining there wasn't). We spend a couple of hours trying to find a reason but we didn't find anything to explain, so i'm reaching for you guys(so as my teacher who is curious about), i don't know if we let something pass by.
To create the error that i'm talking about, simply remove \n from each printf and run the code, he'll run but not work properly (even though the port seems to be listen in the telnet command) the lines who was supposed to appear will not show.
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
//criar socket -> bind -> listen -> connection
int socket_name = 0, new_conn = 0;
struct sockaddr_in serv_addr;
char msg_recv[20];
char sendBuff[1025];
socket_name = socket(AF_INET, SOCK_STREAM, 0);
if(socket_name < 0)
{
printf("%s","Failed to create socket, exiting\n");
}else{
printf("%s", "Success\n");
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(socket_name, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(socket_name, 10);
while (1)
{
new_conn = accept(socket_name, (struct sockaddr*)NULL, NULL);
read(new_conn, &msg_recv, sizeof(msg_recv));
printf("New: %s\n", msg_recv);
close(new_conn);
}
return 0;
}
I am testing with nc command, sending a message there, without a client implementation.

I'm pretty sure you guys aren't experiencing any errors, the adding of \n does nothing more than flush your printing buffer and make it, in fact, a visible indication within your stdout or a terminal for this matter.

Related

How to close sockets in C (GCC on Linux)

I'm completely new to C.
And am following this yt tutorial on sockets. However he's using the close(sock) function. There's 2 problems:
There's no variable called sock. The socket is called something else.
I can't find the close function. The compiler is saying that the function doesn't exist
Can you please explain. This question might seem a little too dumb.
Here's the Client code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
// Create a socket
int network_socket;
network_socket = socket(AF_INET, SOCK_STREAM, 0);
// Address for the socket
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9002);
server_address.sin_addr.s_addr = INADDR_ANY;
int connection = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));
printf("%d\n", connection);
char response[256];
recv(network_socket, &response, sizeof(response), 0);
printf("%s\n", response);
return 0;
}
There's no variable called sock. The socket is called something else.
Yes, it's network_socket in your code. So you want close(network_socket);.
I can't find the close function. The compiler is saying that the function doesn't exist
You need #include <unistd.h>, which is where close() is declared. (If you look at man close you'll see this at the top.)
You need to pass the socket file descriptor as the argument for close(). Which, for your code, is network_socket. So, close() need to be called as close(network_socket);
Also, you need to use the <unistd.h> header for the close() function.

Bluetooth on the EV3

Before I get started. Yes, I could use leJOS, ev3dev, or some others, but I'd like to do it this way because that is how I learn.
I am using the CodeSourcery arm-2009q1 arm toolchain. I fetched the required libraries (bluetooth) from here: https://github.com/mindboards/ev3sources.
I am uploading the programs to the brick by using this tool: https://github.com/c4ev3/ev3duder
I have also fetched the brick's shared libraries, but I can not get them to work properly and there is 0 documentation on how to write a c program for the ev3 using the shared libraries. If I could get that working I might be able to use the c_com module to handle bluetooth, but right now bluez and rfcomm in conjunction with: https://github.com/c4ev3/EV3-API for motor and sensor control seems to be my best bet.
Now, with that out of the way:
I'd like to run the EV3 as a bluetooth "server" meaning that I start a program on it and the program opens a socket, binds it, listens for a connection, and then accepts a single connection.
I am able to do open a socket, bind it to anything but channel 1 (I believe this might be the crux of my issue), I am able to listen. These all return 0 (OK) and everything is fine.
Then I try to accept a connection. That instantly returns -1 and sets the remote to address 00:00:00:00:00:00.
My code is pretty much the same as can be found here: https://people.csail.mit.edu/albert/bluez-intro/x502.html
Here it is:
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <ev3.h>
int main(int argc, char **argv)
{
InitEV3();
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int sock, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = 2; // <-- Anything but 1. 1 seems to be taken
bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
listen(sock, 1);
// accept one connection <-- PROGRAM FAILS HERE AS accept() returns -1
client = accept(sock, (struct sockaddr *)&rem_addr, &opt);
// ---- All following code is irrelevant because accept fails ----
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 )
printf("received [%s]\n", buf);
close(client);
close(sock);
FreeEV3();
return 0;
}
I am able to get the same code working on my pi. Even communication back and forth when the ev3api-specific functions are commented out. I just can't fathom why it won't work on the EV3.
I figured it out.
On my raspberry PI, the accept call worked as expected with no quirks. On the EV3 however, the accept call is non-blocking even if it has not been told to act like so.
The solution was to place the accept call in a loop until an incoming connection was in the queue.
while (errno == EAGAIN && ButtonIsUp(BTNEXIT) && client < 0)
client = accept(sock, (struct sockaddr*)&rem_addr, sizeof(rem_addr));
I'll upload the code to github. Contact me if you'd like to do something similar with the EV3.

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.

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.

recv() function doesn't read the input (Socket Programming)

This is a simple program that simulates a credential validation server. Clients should connect using telnet to the server on port 80. The client should enter a username and a matching password. The problem is that the recv() function does not seem to receive the right input (so that further processing can be done on it).
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int main()
{
char * credentialsList[7][2] = {{"Alice","abcdef"}, {"Bob","1234567"}, {"Cindy","qwerty"}, {"David","abababab"}, {"Eve", "cdefgh"}, {"Frank","7654321"}, {"George", "12341234"}};
int serverSocket=socket(AF_INET, SOCK_STREAM, 0);
int new_socket, i;
char *message, client_message[10];
struct sockaddr_in server, client;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(80);
bind(serverSocket, (struct sockaddr *)&server, sizeof(server));
listen(serverSocket,2);
int c = sizeof(struct sockaddr_in);
new_socket = accept(serverSocket, (struct sockaddr *)&client, (socklen_t*)&c);
message = "Welcome! You Are Now Connected To The Server.\n\n";
write(new_socket, message, strlen(message));
message = "Please Enter A Valid Username: ";
write(new_socket, message, strlen(message));
memset(client_message,0,sizeof(client_message));
recv(new_socket, client_message, 10, 0);
//int x = strcmp(client_message,credentialsList[0][0]);
//printf("%i", x);
puts(client_message);
return 0;
}
How do you detect the end of the username? Is it detected by the end of the connection, by a newline or perhaps by a '\0' character? It's impossible to answer this since you didn't provide the client code.
Anyway, you're not checking the return value of recv() and the commented-out implicitly assumes that it's a '\0'-terminated string which may not be true. You should always check the return code of system calls and never assume the client data to be formatted according to a certain format.
What you need to do is to read the credentials and nothing else. This may even require reading 1 byte at a time until you reach the newline if it's newline-delimited (a more efficient implementation of this would be the implementation of a buffering layer on top of recv()). Note that reading the credentials may require multiple recv() calls as the credentials may arrive in 1-byte-sized TCP segments.

Resources