C TCP Socket using select() to get multiple inputs from same client - c

I'm new to socket, I am trying to send a message to the server, and if the server does not receive another message from client within 5 seconds, then send a warning to client, otherwise combine two messages and send back to client.
I'm using select, and the server is not able to recv second message once the select() is called, it's always timeout.
What did I do wrong??
Server
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUF_SIZE 256
char *concat(const char *s1, const char *s2);
int main(int argc, char const *argv[]) {
struct sockaddr_in server, client;
struct timeval tv;
int sock, readSize, fd = 0;
char buf[BUF_SIZE], stringA[BUF_SIZE], stringB[BUF_SIZE];
socklen_t addressSize;
fd_set readfds;
bzero(&server, sizeof(server));
server.sin_family = PF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(6000);
// TCP check
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("socket: %s\n", strerror(errno));
return 1;
}
// Handle binding error
if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == -1) {
printf("bind: %s\n", strerror(errno));
return 1;
}
// Handle connection error
if (listen(sock, 5) == -1) {
printf("listen: %s\n", strerror(errno));
return 1;
}
// Handle client acceptance error
addressSize = sizeof(client);
sock = accept(sock, (struct sockaddr *)&client, &addressSize);
while ((readSize = recv(sock, stringA, sizeof(stringA), 0))) {
stringA[readSize] = '\0';
printf("Read Message A: %s", stringA);
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
FD_SET(0, &readfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
if (select(sock + 1, &readfds, NULL, NULL, &tv) < 0) {
printf("ERROR in select");
}
char *result;
// If more messgae receve within 5 seconds, but the program never reached this part
if (FD_ISSET(sock, &readfds)) {
// Get string B
readSize = recv(sock, stringB, sizeof(stringB), 0);
stringB[readSize] = '\0';
result = concat(stringA, stringB);
printf("Some more input received\n");
} else {
printf("Time out\n");
}
send(sock, &result, sizeof(result), 0);
}
printf("Client has closed the connection.\n");
close(sock);
return 0;
}
char *concat(const char *s1, const char *s2) {
char *result = malloc(strlen(s1) + strlen(s2) + 1);
strcpy(result, s1);
strcat(result, s2);
return result;
}
Client
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#define BUF_SIZE 256
int main(int argc, char const *argv[]) {
struct sockaddr_in server;
struct timeval tv;
int sock, readSize, addressSize;
char buf[BUF_SIZE];
bzero(&server, sizeof(server));
server.sin_family = PF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(6000);
sock = socket(PF_INET, SOCK_STREAM, 0);
addressSize = sizeof(server);
// TCP check
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("socket: %s\n", strerror(errno));
return 1;
}
// Handle connection error
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == -1) {
printf("connect: %s\n", strerror(errno));
return 1;
}
while (1) {
fgets(buf, 256, stdin);
if (feof(stdin)) break;
send(sock, &buf, sizeof(buf), 0);
readSize = recv(sock, buf, sizeof(buf), 0);
buf[readSize] = '\0';
printf("%s\n", buf);
}
close(sock);
return 0;
}

The problem you're asking about appears to be that the client tries to receive a response from the server after each send(). That will block until it can read at least one byte or the server closes the connection. Meanwhile, the server expects the client to send messages one right after another, without any responses to the odd-numbered messages.
That is symptomatic of a design problem. This ...
I am trying to send a message to the server, and if the server does
not receive another message from client within 5 seconds, then send a
warning to client, otherwise combine two messages and send back to
client.
... may sound simple and seem to make sense, but in fact it is neither very simple nor very sensible. Suppose you have a well-intentioned client of of your service. It knows that the service expects two messages, one after the other, to which it will respond with a concatenation of the two. Why would such a client fail to send the expected second messages? The most likely explanations are
it can't, because it is suspended, because a network link went down, because its host machine went down, or similar.
it didn't, because it was killed before it could.
it didn't, because it is buggy.
None of those is rescued by the server sending a warning message. (But if the client is killed before delivering the second message, then the server will probably see EOF on the socket and signal read-readiness.)
Moreover, suppose the client is suspended, the server sends a warning, and then the client resumes. When it tries to read the expected response from the server then it gets the warning message instead, or quite possibly the warning concatenated with the expected response. How is the client supposed to distinguish warning messages from normal responses?
I would suggest dropping the warnings altogether. The server can instead just disconnect non-responsive clients.
If you want to retain the warnings and continue to use just one socket then you need to augment your protocol to make the warning messages distinguishable and separable from normal responses. For example, the server's responses might be in two parts -- a response code identifying the message type, followed by a response body.
Alternatively, you might use separate sockets for the two distinct message streams, but I think that would be more complicated than you want to handle right now.
There are other issues with your code, though. The main ones are
You seem to assume that send/write and recv/read calls will pair up so that the data sent from one side by one call is exactly what is received by one call on the other side. That is not at all a safe assumption. You are using a stream-oriented socket, and one of the characteristics of such a socket is that the data stream does not have any built-in message boundaries. If you want to divide the data into logically separate messages, then you need to layer that on top of the stream.
You do not account for the fact that send/write and recv/read may (successfully) transfer fewer bytes than you request. Generally speaking, you need to pay attention to the return value of these functions and be prepared to use multiple calls to transfer all the bytes of a given transmission.

Related

Client and Server send() and recv() in C

This is my first time socket programming and have a question about send and recv. I'm sending a file from server to client which works fine. But, when I want to continue my program with more send() and recv() calls (the commented code at the end of client and server), the client receives this data and writes it to newfile.txt.
So, my question is, how do I call send() from the server to only send data from test.txt, and then recv() on the client to only receive and write data from test.txt to newfile.txt? After this, I would want to continue with my program with more send() and recv() calls which dont get mixed up with the file transfer code.
Client:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
int port = 8001;
int client_socket;
struct sockaddr_in server_addr;
int connect_status;
client_socket = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM for TCP
if (client_socket < 0)
{
printf("Error creating socket\n");
exit(1);
}
printf("Socket created\n");
//address for socket to connect to
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); //8001 arbitrary port
server_addr.sin_addr.s_addr = INADDR_ANY;
//connect to address of socket
connect_status = connect(client_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
if(connect_status == -1)
{
printf("Error connecting to server\n");
exit(1);
}
printf("Connected to Server\n");
FILE* fp = fopen( "newfile.txt", "w");
char data[512];
int b;
while((b=recv(client_socket, data, 512,0))>0){
fwrite(data, 1, b, fp);
}
fclose(fp);
//recv(client_socket, data, 512,0);
//printf("client buffer: %s\n", data);
close(client_socket);
return 0;
}
Server:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main(int argc, char *argv[]){
int port = 8001;
int server_socket;
struct sockaddr_in server_addr;
int client_socket, client_addr;
int bind_status, listen_status;
server_socket = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM for TCP
if (server_socket < 0)
{
printf("Error creating socket\n");
exit(1);
}
printf("Socket created\n");
//address for socket to connect to
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
//bind socket to the IP and port
bind_status = bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (bind_status < 0)
{
printf("Error binding to socket\n");
exit(1);
}
printf("Socket binded\n");
//listen for client connections
listen_status = listen(server_socket, 10);
if (listen_status == -1)
{
printf("Error listening to socket\n");
exit(1);
}
printf("Listening\n");
//accept client connection
socklen_t addr_len;
addr_len = sizeof(client_socket);
client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &addr_len);
FILE *fp = fopen("test.txt", "r");
char data[512];
int b;
while((b = fread(data, 1, 512, fp))>0){
send(client_socket, data, b, 0);
}
fclose(fp);
// strcpy(data,"test message");
//printf("server buffer: %s\n", data);
//send(client_socket, data, 512, 0);
close(server_socket);
return 0;
}
You need some way to indicate the server that you have finished sending a file, and now you want to send another thing.
While the socket abstraction seems to show you that the recv and send calls are somehow synchronized, this means that the data you send from the client in one call to send is recv'd in the server with exactly one recv, that is not true, due fundamentally to how the sockets are implemented (the client tcp can decide to split your transfer in several packets, and the unattending of the server can make all those packets to buffer n the receiver before the server receives part of them in one call to recve and others in the text call.
The only thing sure is that a byte that has been sent before, is receive before, and no repeated or missing bytes in between. But the number of bytes received at some recve call is dictated only by the amount of buffered data that one side of the connection has.
This means that, for telling the server that you are finished with your file and some other thing is to be sent, you must do something that allows the server to recognize that the data has ended and more data on a different thing is about to come.
There are several approaches here... you can send an inband sequence (some control sequence) that, wen read in the other side, will be recognized as the end of a block of data and the beginning of the next. Some systems use a packet encoding that simply prepends each block with a number of bytes to follow, and an empty packet (e.g. a single number 0, meaning 0 bytes to follow) can represent this sequence. Another way, can be to use a specific sequence you know is improbable to occur in the data (e.g. \n.\m, two newlines with one dot interspersed ---this has been used many times in unix's ed editor, for example, or the post office protocol uses it.) and if it is the case that such a sequence happens to be in the file, just double the dot, to indicate that it is not the separator sequence. And both ends must agree on this (so called) protocol.
Other mechanisms are valid, you can close the connection and use another socket for a new data exchange.... for example, FTP protocol uses this approach by using one control connection for FTP commands, while using other connections for data transfers.
I have not included code because there are plenty of examples in the literature. Try to get access to the book "Unix Network Programming" of Richard W. Stevens. That's a very good reference to get initiated on socket protocols and how to deal with all these things.

message from client to server in C

I have such a task, I need to write the “client” code so that the message from the “client” is sent to the server (which was created by my teacher, ip “127.0.0.1”). After the message arrives at the server (for example, “Nursultan Nazarbayev”, the server will reply “ok” and the client should send “quit”)
I'm just learning C. How to make a break with the server? I wanted to send "quit", but this did not work, there was a constant error, how can I do this?
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
char message[2048];//=(char*)malloc(sizeof(char));// transmission message
char buf[sizeof(message)];
int port,ch;
//----------------------------------------------------------------------------
if(argc!=3){
printf("Wrong number of arguments!\nThere must be 2 arguments (Port, server ip-address)!\n");
exit(0);
}
int sock; // socket descriptor
struct sockaddr_in addr; // structure with address
struct hostent* hostinfo;
port = atoi(argv[1]);
hostinfo = argv[2];
sock = socket(AF_INET, SOCK_STREAM, 0); // create TCP socket
if(sock < 0)
{
perror("socket");
exit(1);
}
// Specify server parameters
addr.sin_family = AF_INET; // Internet domains
addr.sin_port = htons(port); // or any other port ...
addr.sin_addr.s_addr=inet_addr("hostinfo");
// addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
//addr.sin_addr.s_addr = inet_addr(host_ip);
if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) // establishing a connection to the server
{
perror("Connection");
exit(2);
}
while(1){//WHILE <---
//----------------------------------------------------------------------------
printf("Enter a message to the server (To exit: quit): ");
if (!strcmp(gets(message), "quit")){close(sock);return 0;}
//----------------------------------------------------------------------------
printf("sending a message to the server...\n");
send(sock, message, sizeof(message), 0); // sending a message to the server
int bytes_read = 0;
printf("Message Waiting\n");
bytes_read = recv(sock, buf, sizeof(message), 0);
printf("received %d bytes\tMessage: %s\n", bytes_read, buf); // receiving a message from the server
}//END_WHILE
return 0;
}
This probably is wrong:
addr.sin_addr.s_addr=inet_addr("hostinfo");
You are passing the literal string, "hostinfo" to the inet_addr() function. Meanwhile, you have a variable named hostinfo, to which you assign a string value, but you never use it anywhere in the program.
Maybe you meant to do this instead:
addr.sin_addr.s_addr=inet_addr(hostinfo);
P.S., I like long, descriptive names. If that was my program, the name of the variable would be something like, server_address_as_string.

http server (c) - send command doesn't work (empty reply from server)

I got a task to implement a http server in c code.
it should include handling several connections, but for now I just want to make sure it works with just a single connection.
first, here is my code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#define BUF_SIZE 1025
int main(int argc, char **argv)
{
uint16_t portNum = 80;
int connfd = 0, listenFd;
struct sockaddr_in serv_addr, peer_addr;
if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("socket syscall failed: %s.\nExiting...\n", strerror(errno));
exit(EXIT_FAILURE);
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portNum);
if (bind(listenFd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)))
{
printf("bind syscall failed: %s\nExiting...\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (listen(listenFd, 5))
{
printf("listen syscall failed: %s\nExiting...\n", strerror(errno));
exit(EXIT_FAILURE);
}
while (1)
{
/* new connection */
socklen_t addrsize = sizeof(struct sockaddr_in);
connfd = accept(listenFd, (struct sockaddr *) &peer_addr, &addrsize);
if (connfd < 0)
{
printf("\n Error : Accept Failed. %s \n", strerror(errno));
return 1;
}
char httpRequest[BUF_SIZE] = {0};
if ((recv(connfd, httpRequest, BUF_SIZE, 0)) == -1)
{
printf("recv syscall failed: %s\nExiting...\n", strerror(errno));
exit(EXIT_FAILURE);
}
char msg[BUF_SIZE] = {0};
strcpy(msg, "200 OK THIS IS A TEST");
printf("sending message...\n");
int len = strlen(msg);
if (send(connfd, msg, len, 0) < 0)
{
printf("SEND ERROR\n");
exit(EXIT_FAILURE);
}
printf("message sent!\n");
close(connfd);
close(listenFd);
// THIS IS FOR DEBUG - ignore...
return 0;
}
return EXIT_SUCCESS;
}
here's my problem:
to test my code I ran the following command in another linux terminal:
curl -v loaclhost:80/~/ex.txt
(where ex.txt is simply a test file...)
here is the problem:
I see the http request both in the server and in the curl output, but it seems like the "send" command of the server doesn't work - in the curl window beneath the http rquest it says:
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server
any ideas?
Buffer of 1024 is too small for anything serious. For just playing locally it will work OK. If you ever want to do some serious work buffer of 8192 bytes works fine even for heavy loads.
recv() is in most implementations blocking function, which means that it will block until it receives the entered number of bytes or a client closes the connection after sending data. I didn't see your socket being defined and treated as non-blocking. So that part is up to curl to overcome.
Your HTTP response is wrong.
The header should look like:
"HTTP/1.1 200 OK\r\nStatus: 200 OK\r\nContent-Type: text/plain\r\n\r\n"
You may also add a header:
"Content-Length: 0"
as you aren't sending any data back except the header.
Also, if you continue using HTTP/0.9 or switch to HtTP/1.0, you should close the connection with a client after sending the data to him.
HTTP/1.1 supports opened connection, but it is usually controlled via Keep-Alive and Connection headers.
ok, -1 for me, it didn't work due to the missing '\n' character in strcpy..

Socket arbitrarily connects - or doesnt

I'm working on a university project, in which I have to connect a raspberry pi to an Android smartphone to control 2 motors.
We are new to socket programming, so we started out with an example we found on wikibooks and tried to modify in to our needs. We're now facing the problem, that the connection between server and client is very arbitrary and unstable, sometimes connecting, and after a brief disconnect doesnt connect again. The weird thing (for me) is, that after we edit the code above the part responsible for connection:
/* bind serv information to mysocket */
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr));
/* start listening, allowing a queue of up to 2 pending connection */
listen(mysocket, 2);
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
like inserting in a printf, the next time we launch the programm, everthing does work, sometimes two or three times, and then it just stops connecting.
I've searched all over google and so for a similar problem, but I haven't found an equivalent, so I turn to you directly now.
This is code for our server running on the raspberry pi, which also serves as a network hotspot:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <bcm2835.h>
#define PORTNUM 5298
#define MAXRCVLEN 1000
#define PIN9 RPI_GPIO_P1_21
#define PIN10 RPI_GPIO_P1_19
#define PIN11 RPI_GPIO_P1_23
#define PIN22 RPI_GPIO_P1_15
int setpins();
int forward();
int backward();
int main(int argc, char *argv[])
{
char msg[] = "Connected!\n";
char testchar[] = "stillthere?";
char quitstring[] = "quit";
char *recbuf;
int qflag = 0;
int lflag = 0;
int mysocket, consocket, len; /* socket used to listen for incoming connections */
struct sockaddr_in dest; /* socket info about the machine connecting to us */
struct sockaddr_in serv; /* socket info about our server */
socklen_t socksize = sizeof(struct sockaddr_in);
memset(&serv, 0, sizeof(serv)); /* zero the struct before filling the fields */
serv.sin_family = AF_INET; /* set the type of connection to TCP/IP */
serv.sin_addr.s_addr = htonl(INADDR_ANY); /* set our address to any interface */
serv.sin_port = htons(PORTNUM); /* set the server port number */
mysocket = socket(AF_INET, SOCK_STREAM, 0);
/* bind serv information to mysocket */
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr));
/* start listening, allowing a queue of up to 2 pending connection */
listen(mysocket, 2);
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
if (!bcm2835_init()) return 1;
setpins();
while(consocket)
{
printf("Incoming connection from %s - sending welcome\n", inet_ntoa(dest.sin_addr));
send(consocket, msg, strlen(msg), 0);
while (!qflag && !lflag) {
// Do something when connection is lost: SO_KEEPALIVE?
// if (!send(consocket,testchar, strlen(testchar), 0)) lflag = 1;
recbuf = malloc (MAXRCVLEN+1);
len = recv(consocket, recbuf, MAXRCVLEN, 0);
recbuf[len] = '\0';
if (len > 0) printf("Client sent %s (%d bytes). \n", recbuf, len);
if (recbuf[0] == 'v') forward(); // this function lets our car drive forward
if (recbuf[0] == 'r') backward();// this one backwards ;)
// Leave this loop if the client sends you the quitstring
if (!strcmp (recbuf, quitstring)) qflag = 1;
free(recbuf);
}
if (qflag) break;
listen(mysocket, 1);
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
}
close(consocket);
close(mysocket);
printf("sockets closed\n");
return EXIT_SUCCESS;
}
One line in there
// if (!send(consocket,testchar, strlen(testchar), 0)) lflag = 1;
is our idea to test wether the connection is still up, is this viable?
And this is the client code, thats not in Java yet but in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MAXRCVLEN 500
#define PORTNUM 5298
int main(int argc, char *argv[])
{
char buffer[MAXRCVLEN + 1]; /* +1 so we can add null terminator */
int len, mysocket;
struct sockaddr_in dest;
mysocket = socket(AF_INET, SOCK_STREAM, 0);
memset(&dest, 0, sizeof(dest)); /* zero the struct */
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("192.168.42.1"); /* set destination IP number */
dest.sin_port = htons(PORTNUM); /* set destination port number */
do {
connect(mysocket, (struct sockaddr *)&dest, sizeof(struct sockaddr));
len = recv(mysocket, buffer, MAXRCVLEN, 0);
}while(len < 0);
/* We have to null terminate the received data ourselves */
buffer[len] = '\0';
// Received
printf("Received %s (%d bytes).\n", buffer, len);
// send:
char msg[] = " ";
do{
scanf("%s",msg);
printf("Sending Msg to %s \n", inet_ntoa(dest.sin_addr));
send( mysocket, msg, strlen(msg),0);
}while (strcmp(msg,"quit"));
close(mysocket);
return EXIT_SUCCESS;
}
Any ideas what we did wrong?
Thanks in advance!
Unless what you actually, really want to learn is low-level berkeley socket manipulation, I'd suggest you look at libevent or a similar library.
The structure of your main loop is a little unusual. You can clearly only handle one connection at a time, and you don't cope well with any connection attempts that happened while you were servicing a previous connection.
bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr));
bind can fail, e.g. if another process has recently had the socket open and the OS hasn't finished cleaning up use of the port. You can change this behavior, but you should still check, from die.net's bind manpage
Return Value
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
so
if(bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr))) {
perror("bind failed");
exit(1);
}
listen() only needs to be called once, but also needs to be checked
if(listen(mysocket, 2)) {
perror("listen failed");
exit(1);
}
after this, if you are content to do the single-service approach, then you can do the following:
mysocket = socket(AF_INET, SOCK_STREAM, 0);
if(mysocket < 0) {
perror("socket failed");
exit(1);
}
if(bind(mysocket, (struct sockaddr *)&serv, sizeof(struct sockaddr))) {
perror("bind failed");
exit(1);
}
if(listen(mysocket, 2)) {
perror("listen failed");
exit(1);
}
for (;;) {
consocket = accept(mysocket, (struct sockaddr *)&dest, &socksize);
if(consocket < 0) // might return if the connection has already gone away.
continue;
if (!sendGreeting(consocket)) {
// sendGreeting should return -1 if it was unable to send, 0 if successful
while (!readLoop(consocket, recvBuf, MAXRCVLEN))
;
}
close(consocket);
}
readLoop would then be something like:
int readLoop(int socket, char* buffer, size_t bufSize) {
int len = recv(socket, buffer, bufSize);
if (len > 0)
return processBuffer(socket, buffer, len);
if (len < 0 && (errno == EINTR || errno == EAGAIN))
return 0; // do-over
return -1;
}
make sure that processBuffer also returns 0 or -1 accordingly.
As I mentioned above, there are still problems with this approach, but it's not my intent here to teach you everything you need to know about sockets in one pass :) If you want to further develop your socket knowledge, your next stop should be learning about select or poll with non-blocking sockets so that you can host multiple sockets and service them as they become active.
Generally, you should use tcpdump/wireshark to see what packets are seen by you Rpi, and strace to see what your program does. My first guess about your connections sometimes not working would be loss of packets. By using wired LAN (Ethernet), you could rule this possibility out.
But the example server code that you're using is a rather bad example. Even if you only want to accept a single client connection at a time, your server should not use blocking waits for any remote message. You should read about using non-blocking I/O, select or poll, and look at examples using these. Also, please read about SO_REUSEADDR, you probably need that one in your server as well.
This line code
char msg[] = " ";
do{
scanf("%s",msg);
will fail miserably if the number of bytes scanned in is larger then 1 character, as msg provides exactly two bytes (from which one is always used as 0-terminator). Feeding more would write out of the bounds of msg and doing so will provoke undefined behaviuor.
To fix this providing at least a minimum of 255 characters to so:
char msg[256] = "";
do{
scanf("%255s",msg);

Passing multiple messages from client -> server and server -> client sockets in C

Could someone help identify why my server cannot accept more than one message from the client?
I am attempting to have the flow be like the following:
1. Client sends size of message to server
2. Server receives the size and sends a response back. In this case 0.
3. Client checks response and then writes message to server.
4. Server reads message and prints it out.
The problem I am getting is that the accept() at step 4 is never unblocking.
CLIENT
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[])
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s_address;
s_address.sin_family = AF_INET;
s_address.sin_port = htons(51717);
s_address.sin_addr.s_addr = INADDR_ANY;
if (connect(sock, (struct sockaddr *) &s_address, sizeof(s_address)) < 0) {
printf("ERROR: Cannot connect()\n");
exit(0);
}
char *org_msg = "Hello";
printf("Writing size of Hello\n");
char msg1[1];
msg1[0] = sizeof(org_msg);
write(sock, msg1, sizeof(msg1));
printf("Waiting for response from server\n");
struct sockaddr_in c_address;
socklen_t c_length = sizeof(c_address);
int new_sock = accept(sock, (struct sockaddr *) &c_address, &c_length);
printf("Reading response from server\n");
char stat[1];
read(new_sock, stat, 1);
if (atoi(stat) == 0) {
printf("Writing Hello to server\n");
write(sock, org_msg, sizeof(org_msg));
}
close(sock);
close(new_sock);
}
SERVER
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[])
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s_address;
s_address.sin_family = AF_INET;
s_address.sin_port = htons(51717);
s_address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *) &s_address, sizeof(s_address)) < 0) {
printf("ERROR: Cannot bind()\n");
exit(0);
}
listen(sock, 3);
printf("Waiting for client message\n");
struct sockaddr_in c_address;
socklen_t c_length = sizeof(c_address);
int new_sock = accept(sock, (struct sockaddr *) &c_address, &c_length);
printf("Reading client message\n");
char msg[1];
read(new_sock, msg, 1);
printf("Writing response to client\n");
char stat[1];
stat[0] = '0';
write(new_sock, stat, sizeof(stat));
printf("Waiting for client message\n");
int new_sock2 = accept(sock, (struct sockaddr *) &c_address, &c_length);
printf("Reading client message\n");
char msg2[atoi(msg)];
read(new_sock2, msg2, sizeof(msg2));
printf("MESSAGE: %s\n", msg2);
close(sock);
close(new_sock);
close(new_sock2);
}
You should not call accept() on an already-connected socket. Once you have a connected socket in the server (the socket returned by accept()) you should just keep reading and writing that socket until the connection is closed. The steps for the server should be similar to:
listen_socket = socket(...);
listen(listen_socket, ...);
connected_socket = accept(listen_socket, ...);
read(connected_socket, ...)
write(connected_socket, ...)
read(connected_socket, ...)
write(connected_socket, ...)
...
Similarly the client should just keep reading and writing the socket once it has been connected successfully - the steps for the client should be:
connected_socket = socket(...);
connect(connected_socket, ...);
write(connected_socket, ...);
read(connected_socket, ...);
write(connected_socket, ...);
read(connected_socket, ...);
...
INADDR_ANY works in the server but your client needs to specify what host it's connecting to.
If both are on the same machine, just use 127.0.0.1 or localhost (you'll have to do a transform so that it's the right format)
More information here, but a short answer would be
#define INADDR_LOOPBACK 0x7f000001
and then s_address.sin_addr.s_addr = htonl (INADDR_LOOPBACK)
On the client you try to accept a new connection with the socket you previously connected to the server, which will be bound to a system-chosen port number. The server never tries to connect to the client, so the accept call on the client never returns (actually it may return but with an error, because you never call listen on that socket).
Why not just perform step 3 with the same socket used in the previous steps? If for some reason you do need a new socket, you should create a new socket in the client instead of reusing the previous socket (or call close on the previous socket and then call connect on it again).
BTW if all you need is IPC, sockets are a really bad way to do it. I suggest something like Java RMI.

Resources