I've created, as an homework, a big project which simulate a mailbox server (only through process on the same computer, so through fifo, it's a homework)
I can't post the project because is big (there are a lot of files), but I can say that sometimes I lost some data or it doesn't preserve it's integrity.
I use these code snippet to transmit data, is it somewhat wrong?Network_IO is the function I'm talking about:
#include "Network.h"
int Network_Open(const char* path,int oflag)
{
return open(path,oflag);
}
ssize_t Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
errno = 0;
if (tmpDataSize == 0) return 0;
while ((retsize = (opcode == NetworkOpCode_Write? write(fifo,data,tmpDataSize) : read(fifo,data,tmpDataSize))) != tmpDataSize)
{
if (errno != EINTR) break;
}
return retsize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return ((ssize_t)dataSize) == Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
Boolean Network_Close(int fifo)
{
if (fifo >= 0)
return close(fifo) == 0;
}
Edit 1: Code snippet which I'm using to test actually
Boolean Network_IO(int fifo,NetworkOpCodes opcode,void* data,size_t dataSize)
{
ssize_t retsize = 0;
ssize_t tmpDataSize = (ssize_t)dataSize;
ssize_t sentDataSize = 0;
errno = 0;
if (tmpDataSize == 0) return True;
while (sentDataSize < tmpDataSize)
{
switch(opcode)
{
case NetworkOpCode_Write:
retsize = write(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
case NetworkOpCode_Read:
retsize = read(fifo,data + sentDataSize,tmpDataSize - sentDataSize);
break;
}
if (retsize < 0)
{
if (errno != EINTR) return False;
else
{
errno = 0;
continue;
}
}
sentDataSize += retsize;
}
if (errno != 0)
return False;
return sentDataSize == tmpDataSize;
}
Boolean Network_Send(int fifo,const void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Write,(void*)data,dataSize);
}
Boolean Network_Receive(int fifo,void* data,size_t dataSize)
{
return Network_IO(fifo,NetworkOpCode_Read,data,dataSize);
}
IMHO the Network_IO() function serves no purpose. It's only purpose is to 'demultiplex' the opcodes for read/write calls, that were given to it by the Network_Send() and Network_Receive() functions. Better would be to call read() and write directly in the Network_Send() and Network_Receive() functions. Your choice of return type (Boolean) is also strange.
The error conditions on read() and write() could be different, in the future maybe more than just EINTR needs to be handled in one of them. Also: your functions block, that means: they don't return until the desired amount has actually been sent or received. Also note that for pipes and fifos, the amount of bufferspace supplied by the kernel is very limited, typically 1 memory page. This increases the chance of the reader or writer blocking in reads or writes, and results in (at least) two context switches per block of data transferred.
The "loop until done" method; as supplied by Mat is about the standard way of doing things. Also be be prepared for read/write returning zero.
EDIT: what Mat meant is that you need to handle partial reads/writes: you need to start over where you left off, sending/receiving the remaining part of the buffer. Here is a start:
int mywrite(int fd, char *buff, size_t size)
{
int rc;
size_t done, todo;
for (done=0; done < size; ) {
todo = size - done;
rc = write (fd, buff+done, todo);
switch (rc) {
case -1: /* some read error: check it */
switch(errno) {
case EINTR: continue;
/* ... maybe some other cases you need to handle */
default: return -1;
}
break;
case 0: /* (in some cases) the other side closed the connection */
/* handle it here; possibly return error */
break;
default: /* the normal case */
done += rc;
break;
}
}
return done;
}
For the write case, your code boils down to
while ((retsize = write(fifo,data,tmpDataSize)) != tmpDataSize) { ... }
Imagine that on the first write, only one byte gets written. If that happens, you need the next write to attempt to push tmpDataSize-1 bytes, starting at data+1. But what you do now will resend everything, including that first byte.
In pseudo-code, the logic should be something like:
while (bytesLeftToSend > 0) {
sent = write(fifo, data, bytesLeftToSend);
if (sent == -1) {
// report error and bail out
}
bytesLeftToSend -= sent;
data += sent;
}
Same thing for the read case.
BTW, that while with an assignment and a ?: construct is really hard to read.
Related
I have this child process in infinite loop and i want it to stop the loop when recive SIGUSR1 from parent pid.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
int GameOver = 0;
jmp_buf here; // <------- After Joshua's Answer
void trataSIGUSR1(int sig, siginfo_t *info, void *extra);
int main(int argc, char** argv){
int someNumber = 0, score = 0;
char word[15],c;
struct sigaction new_action;
// new_action.sa_flags = SA_SIGINFO; // <------- Before Joshua's Answer
new_action.sa_flags = SA_SIGINFO | SA_RESTART; // <------- After Joshua's Answer
new_action.sa_sigaction = &trataSIGUSR1;
sigfillset(&new_action.sa_mask);
if (sigaction(SIGUSR1, &new_action, NULL) == -1){
perror("Error: cannot handle SIGUSR1"); // não deve acontecer
return EXIT_FAILURE;
}
FILE *f;
f = fopen("randomfile.txt", "r");
if (f == NULL){
printf("Errr Opening File!\n");
return EXIT_FAILURE;
}
// setjmp(here); // <------- After Joshua's Answer
sigsetjmp(here,1); // <-- After wildplasser's Answer
while (!GameOver){
fscanf(f, "%s", word);
printf("\nWord -> %s\n", word);
if(!scanf("%d", &someNumber)){
puts("Invalid Value!");
while ((c = getchar()) != '\n' && c != EOF);
continue;
}
if(someNumber == strlen(word) && !GameOver)
score ++;
if(feof(f)){
printf("\nEnd of file.\n");
break;
}
}
if( GameOver )
puts("\nAcabou o tempo!"); // <-- After wildplasser's Answer
fclose(f);
return score;
}
void trataSIGUSR1(int sig, siginfo_t *info, void *extra){
if (info->si_pid == getppid()){ // only end when parent send SIGUSR1
// puts("\nAcabou o tempo!"); // <-- Before wildplasser's Answer
GameOver = 1;
// longjmp(here,1); // <------- After Joshua's Answer
siglongjmp(here,1); // <---- After wildplasser's Answer
}
}
It works fine but if i send SIGUSR1 to child pid from another process scanf get interupted... I want to interupt the scanf and automaticly stop the loop only when signal come from parent, in other case just ignore. Is there any way to change the flag to new_action.sa_flags = SA_RESTART; when signal comes from other process?!
There are several possibilities, ranging from a huge hack, to proper (but complicated).
The simplest thing is to have the SIGUSR1 from parent reopen standard input to /dev/null. Then, when scanf() fails, instead of complaining and retrying, you can break out of the loop if feof(stdin) is true. Unfortunately, freopen() is not async-signal safe, so this is not a standards (POSIX, in this case) compliant way of doing things.
The standards-compliant way of doing things is to implement your own read input line into a dynamically allocated string -type of function, which detects when the signal handler sets the flag. The flag should also be of volatile sig_atomic_t type, not an int; the volatile in particular tells the compiler that the value may be changed unexpectedly (by the signal handler), so whenever referenced, the compiler must re-read the variable value, instead of remembering it from a previous access. The sig_atomic_t type is an atomic integer type: the process and the signal handler will only ever see either the new, or the old value, never a mix of the two, but might have as small valid range as 0 to 127, inclusive.
Signal delivery to an userspace handler (installed without SA_RESTART) does interrupt a blocking I/O operation (like read or write; in the thread used for signal delivery – you only have one, so that will always be used), but it might occur between the flag check and the scanf(), so in this case, it is not reliable.
The proper solution here is to not use stdin at all, and instead use the low-level <unistd.h> I/O for this. Note that it is imperative to not mix stdin/scanf() and low-level I/O for the same stream. You can safely use printf(), fprintf(stdout, ...), fprintf(stderr, ...), and so on. The reason is that the C library internal stdin stream structure will not be updated correctly by our low-level access, and will be out-of-sync with reality if we mix both (for the same stream).
Here is an example program showing one implementation (licensed under Creative Commons Zero v1.0 International – do as you wish with it, no guarantees though):
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Maximum poll() timeout, in milliseconds, so that done flag is checked often enough.
*/
#ifndef DONE_POLL_INTERVAL_MS
#define DONE_POLL_INTERVAL_MS 100
#endif
static volatile sig_atomic_t done = 0;
static void handle_done(int signum, siginfo_t *info, void *context)
{
/* This silences warnings about context not being used. It does nothing. */
(void)context;
if (signum == SIGUSR1 && info->si_pid == getppid()) {
/* SIGUSR1 is only accepted if it comes from the parent process */
done = 1;
} else {
/* All other signals are accepted from all processes (that have the necessary privileges) */
done = 1;
}
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&(act.sa_mask));
act.sa_sigaction = handle_done;
act.sa_flags = SA_SIGINFO;
return sigaction(signum, &act, NULL);
}
/* Our own input stream structure type. */
struct input {
int descriptor;
char *data;
size_t size;
size_t head;
size_t tail;
};
/* Associating an input stream with a file descriptor.
Do not mix stdin use and input stream on descriptor STDIN_FILENO!
*/
static int input_use(struct input *const in, const int descriptor)
{
/* Check that the parameters are not obviously invalid. */
if (!in || descriptor == -1) {
errno = EINVAL;
return -1;
}
/* Set the descriptor nonblocking. */
{
int flags = fcntl(descriptor, F_GETFL);
if (flags == -1) {
/* errno set by fcntl(). */
return -1;
}
if (fcntl(descriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
/* errno set by fcntl(). */
return -1;
}
}
/* Initialize the stream structure. */
in->descriptor = descriptor;
in->data = NULL;
in->size = 0;
in->head = 0;
in->tail = 0;
/* Success. */
return 0;
}
/* Read until delimiter from an input stream.
* If 'done' is set at any point, will return 0 with errno==EINTR.
* Returns 0 if an error occurs, with errno set.
* Returns 0 with errno==0 when end of input stream.
*/
static size_t input_getdelim(struct input *const in,
int const delim,
char **const dataptr,
size_t *const sizeptr,
const double timeout)
{
const clockid_t timeout_clk = CLOCK_BOOTTIME;
struct timespec then;
/* Verify none of the pointers are NULL. */
if (!in || !dataptr || !sizeptr) {
errno = EINVAL;
return 0;
}
/* Record current time for timeout measurement. */
clock_gettime(timeout_clk, &then);
char *line_data = *dataptr;
size_t line_size = *sizeptr;
/* If (*sizeptr) is zero, then we ignore dataptr value, like getline() does. */
if (!line_size)
line_data = NULL;
while (1) {
struct timespec now;
struct pollfd fds[1];
ssize_t n;
int ms = DONE_POLL_INTERVAL_MS;
/* Done flag set? */
if (done) {
errno = EINTR;
return 0;
}
/* Is there a complete line in the input buffer? */
if (in->tail > in->head) {
const char *ptr = memchr(in->data + in->head, delim, in->tail - in->head);
if (ptr) {
const size_t len = ptr - (in->data + in->head);
if (len + 2 > line_size) {
/* Since we do not have any meaningful data in line_data,
and it would be overwritten anyway if there was,
instead of reallocating it we just free an allocate it. */
free(line_data); /* Note: free(null) is safe. */
line_size = len + 2;
line_data = malloc(line_size);
if (!line_data) {
/* Oops, we lost the buffer. */
*dataptr = NULL;
*sizeptr = 0;
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
/* Copy the line, including the separator, */
memcpy(line_data, in->data + in->head, len + 1);
/* add a terminating nul char, */
line_data[len + 1] = '\0';
/* and update stream buffer state. */
in->head += len + 1;
return len + 1;
}
/* No, we shall read more data. Prepare the buffer. */
if (in->head > 0) {
memmove(in->data, in->data + in->head, in->tail - in->head);
in->tail -= in->head;
in->head = 0;
}
} else {
/* Input buffer is empty. */
in->head = 0;
in->tail = 0;
}
/* Do we need to grow input stream buffer? */
if (in->head >= in->tail) {
/* TODO: Better buffer size growth policy! */
const size_t size = (in->tail + 65535) | 65537;
char *data;
data = realloc(in->data, size);
if (!data) {
errno = ENOMEM;
return 0;
}
in->data = data;
in->size = size;
}
/* Try to read additional data. It is imperative that the descriptor
has been marked nonblocking, as otherwise this will block. */
n = read(in->descriptor, in->data + in->tail, in->size - in->tail);
if (n > 0) {
/* We read more data without blocking. */
in->tail += n;
continue;
} else
if (n == 0) {
/* End of input mark (Ctrl+D at the beginning of line, if a terminal) */
const size_t len = in->tail - in->head;
if (len < 1) {
/* No data buffered, read end of input. */
if (line_size < 1) {
line_size = 1;
line_data = malloc(line_size);
if (!line_data) {
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
line_data[0] = '\0';
errno = 0;
return 0;
}
if (len + 1 > line_size) {
/* Since we do not have any meaningful data in line_data,
and it would be overwritten anyway if there was,
instead of reallocating it we just free an allocate it. */
free(line_data); /* Note: free(null) is safe. */
line_size = len + 1;
line_data = malloc(line_size);
if (!line_data) {
/* Oops, we lost the buffer. */
*dataptr = NULL;
*sizeptr = 0;
errno = ENOMEM;
return 0;
}
*dataptr = line_data;
*sizeptr = line_size;
}
memmove(line_data, in->data, len);
line_data[len] = '\0';
in->head = 0;
in->tail = 0;
return 0;
} else
if (n != -1) {
/* This should never occur; it would be a C library bug. */
errno = EIO;
return 0;
} else {
const int err = errno;
if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR)
return 0;
/* EAGAIN, EWOULDBLOCK, and EINTR are not real errors. */
}
/* Nonblocking operation, with timeout == 0.0? */
if (timeout == 0.0) {
errno = ETIMEDOUT;
return 0;
} else
if (timeout > 0.0) {
/* Obtain current time. */
clock_gettime(timeout_clk, &now);
const double elapsed = (double)(now.tv_sec - then.tv_sec)
+ (double)(now.tv_nsec - then.tv_nsec) / 1000000000.0;
/* Timed out? */
if (elapsed >= (double)timeout / 1000.0) {
errno = ETIMEDOUT;
return 0;
}
if (timeout - elapsed < (double)DONE_POLL_INTERVAL_MS / 1000.0) {
ms = (int)(1000 * (timeout - elapsed));
if (ms < 1) {
errno = ETIMEDOUT;
return 0;
}
}
}
/* Negative timeout values means no timeout check,
and ms retains its initialized value. */
/* Another done check; it's cheap. */
if (done) {
errno = 0;
return EINTR;
}
/* Wait for input, but not longer than ms milliseconds. */
fds[0].fd = in->descriptor;
fds[0].events = POLLIN;
fds[0].revents = 0;
poll(fds, 1, ms);
/* We don't actually care about the result at this point. */
}
/* Never reached. */
}
static inline size_t input_getline(struct input *const in,
char **const dataptr,
size_t *const sizeptr,
const double timeout)
{
return input_getdelim(in, '\n', dataptr, sizeptr, timeout);
}
int main(void)
{
struct input in;
char *line = NULL;
size_t size = 0;
size_t len;
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGTERM) == -1 ||
install_done(SIGUSR1) == -1) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (input_use(&in, STDIN_FILENO)) {
fprintf(stderr, "BUG in input_use(): %s.\n", strerror(errno));
return EXIT_FAILURE;
}
while (!done) {
/* Wait for input for five seconds. */
len = input_getline(&in, &line, &size, 5000);
if (len > 0) {
/* Remove the newline at end, if any. */
line[strcspn(line, "\n")] = '\0';
printf("Received: \"%s\" (%zu chars)\n", line, len);
fflush(stdout);
continue;
} else
if (errno == 0) {
/* This is the special case: input_getline() returns 0 with
errno == 0 when there is no more input. */
fprintf(stderr, "End of standard input.\n");
return EXIT_SUCCESS;
} else
if (errno == ETIMEDOUT) {
printf("(No input for five seconds.)\n");
fflush(stdout);
} else
if (errno == EINTR) {
/* Break or continue works here, since input_getline() only
returns 0 with errno==EINTR if done==1. */
break;
} else {
fprintf(stderr, "Error reading from standard input: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
}
printf("Signal received; done.\n");
return EXIT_SUCCESS;
}
Save it as e.g. example.c, compile using e.g. gcc -Wall -Wextra -O2 example.c -o example, and run using ./example. Type input and enter to supply lines, or Ctrl+D at the beginning of a line to end input, or Ctrl+C to send the process a SIGINT signal.
Note the compile-time constant DONE_POLL_INTERVAL_MS. If the signal is delivered between a done check and poll(), this is the maximum delay, in milliseconds (1000ths of a second), that the poll may block; and therefore is roughly the maximum delay from receiving the signal and acting upon it.
To make the example more interesting, it also implements a timeout on reading a full line also. The above example prints when it is reached, but that messes up how the user sees the input they're typing. (It does not affect the input.)
This is by no means a perfect example of such functions, but I hope it is a readable one, with the comments explaining the reasoning behind each code block.
Historically we solved this problem by always setting SA_RESTART and calling longjump() to get out of the signal handler when the condition is met.
The standard makes this undefined but I think this does the right thing when stdin is connected to the keyboard. Don't try it with redirected handles. It won't work well. At least you can check for this condition with isatty(0).
If it doesn't work and you are bent on using signals like this, you'll need to abandon scanf() and friends and get all your input using read().
Fairly new to socket programming, so here goes my question. What is wrong? What my function does, is take input from the telnet session and then when you press 'enter' it should go break the while loop. But it doesn't for some reason. I can't figure out why either, I've tried various ways but, nothing I've tried thus far worked yet.
void handle_clients(socket,address)
int *socket;
const char *address;
{
char msg[256];
char cmd[128];
int bytes;
memset(msg,0,sizeof(msg));
memset(cmd,0,sizeof(cmd));
while(1) {
send(*socket,"CMD >> ",7,0);
bytes = 0;
while((bytes = recv(*socket,cmd,sizeof(cmd),0)) > 0) {
if(bytes < 0) {
sprintf(msg,"Error: receiving from %s.\r\n",
address);
send(*socket,msg,strlen(msg),0);
break;
}
if(cmd[bytes] == 10 || cmd[bytes] == 13) {
break;
}
}
if(strcmp(cmd,"exit") == 0) {
break;
} else if(strcmp(cmd,"help") == 0) {
sprintf(msg,"Commands: [exit,cmd,help]\r\n");
send(*socket,msg,strlen(msg),0);
} else if(strcmp(cmd,"cmd") == 0) {
memset(cmd,0,sizeof(cmd));
send(*socket,"Enter command: ",15,0);
bytes = 0;
while((bytes = recv(*socket,cmd,sizeof(cmd),0)) > 0) {
if(bytes < 0) {
sprintf(msg,"Error: receiving from %s.\r\n",
address);
send(*socket,msg,strlen(msg),0);
break;
}
if(cmd[bytes] == 10 || cmd[bytes] == 13) {
break;
}
}
system(cmd);
} else {
sprintf(msg,"Unknown command.\r\n");
send(*socket,msg,strlen(msg),0);
}
memset(msg,0,sizeof(msg));
memset(cmd,0,sizeof(cmd));
}
}
it should go break the while loop. But it doesn't for some reason.
In addition to the issues pointed out by my peers, I think the answer to your specific inquiry lies in the nested while loops and the missing \r\n chars from the strcmp.
Since the while loops are nested, than break might break the nested loop while the container loop remains - so you're back in the next nested loop in your next iteration.
For example:
while(1) {
send(*socket,"CMD >> ",7,0);
bytes = 0;
while((bytes = recv(*socket,cmd,sizeof(cmd),0))) {
if(bytes < 0) {
sprintf(msg,"Error: receiving from %s.\r\n",
address);
send(*socket,msg,strlen(msg),0);
break; /* <= WHICH LOOP ARE WE BREAKING? */
}
if(cmd[bytes] == 10 || cmd[bytes] == 13) {
break; /* <= WHICH LOOP ARE WE BREAKING? */
}
}
// ...
}
This is one of those cases where goto might be your friend...
...although I do think there are better and more elegant solutions and changes that might be required to your code before you go there.
Also, What's the point of re-reading the data, if you're overwriting the existing buffer?
I find it almost (in this case) more convenient and likely to ignore the possible case of incomplete TCP/IP packets/commands.
In this specific case, it seems okay to assume that if the command doesn't end with a new line, something is wrong and we can disconnect. Such short commands should pass in a single TCP/IP packet. i.e.:
while(1) {
send(*socket,"CMD >> ",7,0);
bytes = 0;
bytes = recv(*socket,cmd,sizeof(cmd),0);
if(!bytes) { // closed by peer
close(*socket);
return;
}
if(bytes < 0 || cmd[bytes-1] == 10 || cmd[bytes-1] == 13) {
if(errno == EINTR)
continue;
sprintf(msg,"Error: receiving from %s.\r\n",
address);
close(*socket);
return;
}
if(strcmp(cmd,"exit\r\n") == 0) {
break;
} else if(strcmp(cmd,"help\r\n") == 0) {}
// ...
}
This code obviously ignores the possibility of multiple commands being read in a single recv (i.e., the buffer being "help\r\ncmd\r\nX\r\nexit\r\n")... but you'll solve that at some point.
This has a logic problem:
while((bytes = recv(*socket,cmd,sizeof(cmd),0)) > 0) {
if(bytes < 0) {
}
}
bytes will be always greater than zero so the if(bytes<0){} block will never
execute.
You may want to change it to
while((bytes = recv(*socket,cmd,sizeof(cmd),0))) {
if(bytes < 0) {
}
}
If bytes equals to zero, it will not enter while loop.
I am trying to write and read Integer value into/from C socket. Sometimes ntohs() return very big values like 55000 , 32000 etc...Though client is always sending value <1500. If I run the program it happens after 10-15 minutes...Sometimes after 20-30 minutes.
Can you please check below code and tell me
Why this line getting printed ?
printf("Garbage value - ntohs problem ..Exiting... ");
// write exactly n byte
inline int write_n(int fd, char *buf, int n) {
int nwrite, left = n;
int totalwrite = 0;
while (totalwrite != n) {
if ((nwrite = write(fd, buf, left)) <= 0) {
break;
} else {
totalwrite = totalwrite + nwrite;
left -= nwrite;
buf += nwrite;
}
}
if (totalwrite == 0)
return nwrite;
return totalwrite;
}
// send exactly n byte
inline int send_n(int fd, char *buf, int n) {
int nwrite, left = n;
int totalwrite = 0;
while (totalwrite != n) {
if ((nwrite = send(fd, buf, left, MSG_NOSIGNAL)) <= 0) {
break;
} else {
totalwrite = totalwrite + nwrite;
left -= nwrite;
buf += nwrite;
}
}
if (totalwrite == 0)
return nwrite;
return totalwrite;
}
uint16_t nread, len, plength, nsend;
int MTU = 1500;
char buffer[2000];
// Server receive ( Linux 64 bit)
while (1) {
// read packet length
nread = read_n(TCP_SOCKFD, (char *) &plength, sizeof(plength));
if (nread <=0) {
break;
}
len = ntohs(plength);
if (len <=0 || len > 1500 ) {
**printf("Garbage value - ntohs problem ..Exiting... "); // WHY ?**
break;
}
// read packat data
nread = read_n(SOCKFD, buffer, len);
if (nread != len) {
break;
}
}
//---------------------
// CLIENT send ( Android 5 )
while (1) {
nread = read(tunfd, buffer, MTU);
if (nread <= 0 || nread > 1500) { // always <=1500
break;
}
plength = htons(nread);
// send packet lenght
nsend = send_n(TCP_SOCKFD, (char *) &plength, sizeof(plength));
if (nsend != sizeof(plength)) {
break;
}
// send packet data
nsend = send_n(TCP_SOCKFD, buffer, nread);
if (nsend != nread) {
break;
}
}
Thank you
We cannot tell you with certainty what's happening because you cannot provide a verifiable example. Additionally, you've not presented the implementation of read_n(), but supposing that it follows the same model as write_n() and send_n(), we can nevertheless perform some analysis.
Each of the data transfer functions returns a short count in the event that data transfer is interrupted by an error. The client code watches for this, and breaks out of its loop if it detects it. Well and good. The server code does not do this when reading plength, however. Since plength, as a uint16_t, is two bytes in size, a partial read is possible and would go unnoticed by your server code.
In your example, plength is modified only via the one read_n() call presented. Network byte order is big-endian, so the most-significant byte is read first. It is possible that the combination of that byte with the stale one left over from the previous read would represent a number exceeding 1500. For example, if a 221(0x00dd)-byte packet is followed by a 1280(0x0500)-byte packet, and a partial read occurs on the second packet size, then the combined result will be 1501(0x05dd).
I don't presently see any reason to think that the client sends data different in nature than you think it does, and I don't presently see any other way that your server code could give the appearance of receiving different data than the client sends, especially since client and server each abort at the first recognized sign of trouble.
Do note, however, that this code could still be made more robust. In particular, consider that read(), write(), and send() can fail even when there is no problem with the underlying socket or data transfer request. In particular, they can fail with EINTR if the call is interrupted by a signal, and if the socket is in non-blocking mode then they can fail with EAGAIN. There may be others. It does not seem useful to operate your socket in non-blocking mode, but you might indeed want to watch for EINTR and resume reading after receiving it.
I would also suggest that, at least during development, you emit more data about the nature of the error. Call perror(), for example, and afterward print the bad data. You might even consider logging data sent and received.
Got some trouble with TCP socket multiplexing.
//socket is non-blocking
const int MAX = 4096;
char *buff[MAX];
char *p = buff;
int fd, rvalue;
rvalue = 0;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
int didsend, didread;
int shouldsend;
while ((didread = read(fd, buff, MAX)) > 0) {
p = buff;
shouldsend = didread;
while ( 1 ) {
didsend = send(sockfd, p, shouldsend, 0);
//if send succeeds and returns the number of bytes fewer than asked for then try to send rest part in next time.
if (didsend < shouldsend) {
p += didsent;
shouldsend -= didsend;
continue;
}
//if there is no place for new data to send, then wait a brief time and try again.
if ( didsend < 0 && (errno == EWOULDBLOCK || errno == EAGAIN) ) {
usleep(1000);
continue;
}
//if all data has been sent then sending loop is over.
if (didsend == shouldsend) {
break;
}
//send error
if ( didsend < 0 ) {
rvalue = errno;
break;
}
}
}
close(fd);
if (didread == -1) {
return errno;
}
return rvalue;
}
Assume I use an I/O Multiplexing function poll() or kqueue(), and non-blocking socket, then if there are only some small data like send a short message, it works fine.
But if it comes to large data, I mean larger than send()'s buffer size, since using non-blocking socket, send() will just send a portion of data, and return how much data it sends, the rest part of data can only be sent in another call of send(), but it takes time, and can't tell how long it will takes. So the second while() is actually a blocking send which using non-blocking socket.
Equivalent to:
//socket is blocking
const int MAX = 4096;
char *buff[MAX];
int fd, n;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
while ((n = read(fd, buff, MAX)) > 0) {
if (send(sockfd, buff, n, 0) < 0) {
return errno;
}
}
close(fd);
return 0;
}
So, what is the solution to this, multithreading might work but that's kind of wasting resource maybe.
This is the general pattern for a single-threaded server that works with multiple connections and non-blocking sockets.
It's primarily pseudo-code in C and doesn't do the necessary error checking. But it gives you an idea that for each accepted connection, you keep a struct instance that maintains the socket handle, request parsing state, response stream, and any other "state" members of that connection. Then you just loop using "select" to wait or having multiple threads doing this same thing.
Again this is only pseudo-code and uses select/poll as an example. You can get even more scalability with epoll.
while (1)
{
fd_set readset = {};
fd_set writeset = {};
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
FD_SET(client_connection.sock, &readset);
else
FD_SET(client_connection.sock, &writeset);
}
// add the listen socket to the read set
FD_SET(listen_socket, &readset);
select(n + 1, &readset, &writeset, &timeout); // wait for a socket to be ready (not shown - check for errors and return value)
if (FD_ISSET(listen_socket, &readset))
{
int new_client_socket = accept(listen_socket, &addr, &addrlength);
// create a struct that keeps track of the connection state data
struct ConnectionData client_connection = {};
client_connection.sock = new_client_socket;
client_connection.reading_request = 1; // awaiting for all the request bytes to come in
client_connections[number_of_client_connections++] = client_connection; // pseudo code, add the client_connection to the list
}
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
{
if (FD_ISSET(client_connections[i], &readset))
{
char buffer[2000];
int len = recv(client_connections[i].sock, buffer, 2000, 0);
// not shown - handle error case when (recv < 0)
// not shown - handle case when (recv == 0)
ProcessIncomingData(client_connections[i], buffer, len); // do all the request parsing here. Flip the client_connections[i].reading_request to 0 if ready to respond
}
}
else if (client_connections[i].reading_request == 0)
{
if (FD_ISSET(client_connections[i], &writeset))
{
client_connection* conn = &client_connections[i];
int len = send(conn->sock, conn->response_buffer + conn->txCount, conn->response_size - conn->txCount, 0);
conn->txCount += len;
if (conn->txCount == conn->response_size)
{
// done sending response - we can close this connection or change it to back to the reading state
}
}
}
}
I'm programming in C an IRC chat client. everything it's working well except I can't read the whole answer sent by the server. here's the code:
char buffer[2048];
write_on_screen(current_page(), "LOG COMMAND", command);
write(sockfd, command, strlen(command)); //write to socket
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
write_on_screen(current_page(), "RESPONSE", buffer);
return buffer;
most of the time buffer will contain just a piece of the response (which is shorter than 2048 bytes) and other times it contains nothing. in both cases if I do another read() after the first one, it returns me the rest of the answer or another small piece (and then I've to do another read() again). if I put a sleep(1) between write() and read() I get the whole answer, but I'm sure this not a good pratice.
Is there some way I can avoid this?
thank you in advance
You're making the usual mistakes. It is impossible to write correct network code without storing the result of read() or recv() into a variable. You have to:
Check it for -1, and if so look at errno to see whether was fatal, which it almost always is except for EAGAIN/EWOULDBLOCK, and if fatal close the socket and abandon the process.
Check it for zero, which means the peer disconnected. Again you must close the socket and abandon the process.
Use it as the count of bytes actually received. These functions are not obliged nor guaranteed to fill the buffer. Their contract in blocking mode is that they block until an error, end of stream, or at least one byte is transferred. If you're expecting more than one byte, you normally have to loop until you get it.
According to RFC-1459, a single line of text in IRC can contain up to 512 characters and is terminated by a CRLF (\r\n) pair. However:
You're not guaranteed to receive exactly 512 bytes each time. For example, you might receive a comparatively short message from someone else one in the channel: Hi!
Related to the above: A group of 512 bytes might represent more than one message. For example, the buffer might contain a whole line, plus part of the next line: PRIVMSG <msgtarget> <message>\r\nPRIVMS
Given that you could have zero-or-more complete lines plus zero-or-one incomplete lines in your buffer[] at any time, you could try doing something along the lines of:
char buffer[2048];
while(keep_going)
{
char **lines;
int i, num_lines;
// Receive data from the internet.
receiveData(buffer);
// Create an array of all COMPLETE lines in the buffer (split on \r\n).
lines = getCompleteLines(buffer, &num_lines);
removeCompleteLinesFromBuffer(buffer);
// Handle each COMPLETE line in the array.
for (i = 0; i < num_lines; ++i) { handle_line(lines[i]); }
freeLines(lines);
}
This would allow you to handle zero or more complete lines in one go, with any incomplete line (i.e anything after the final \r\n pair) being kept around until the next call to receiveData().
You need to loop around read() until a CRLF had been detected.
A possible way to do this would be:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt)
{
ssize_t bytes_read = 0;
ssize_t result = 0;
int read_cr = 0;
int read_crlf = 0;
while (bytes_read < s)
{
result = read(sd, p + bytes_read, 1);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
else if (EINTR == errno)
{
if (break_on_interupt)
{
break;
}
continue;
}
else
{
perror("read() failed");
break;
}
}
else if (0 == result)
{
break; /* peer disconnected */
}
if ('\r' == p[bytes_read])
{
read_cr = 1;
}
else if (('\n' == p[bytes_read]) && read_cr)
{
read_crlf = 1;
break; /* CRLF detected */
}
else
{
read_cr = 0;
}
++bytes_read;
}
if (!read_crlf)
{
result = -1; /* Buffer full without having read a CRLF. */
errno = ENOSPC; /* ... or whatever might suite. */
}
return (0 >= result) ?result :bytes_read;
}
Call it like this:
#include <stdio.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt);
int main(void)
{
int sd = -1;
/* init sd here */
{
char line[2048] = "";
ssize_t result = read_until_crlf(sd, line, sizeof line, 0);
if (-1 == result)
{
perror("read_until_newline() failed");
}
printf("read '%s'\n", line);
}
return 0;
}