I'm trying to write a server-client program in C wherein the client will send a bunch of messages in the form of 5 bytes: the first byte will contain a command, and the next four will contain a key. It looks something like this:
rc = write(sockfd, &op, 1);
if (rc != 1)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
uint32_t net_num = htonl(num);
int nsent = 0;
while (nsent < 4)
{
rc = write(sockfd, &net_num + nsent, 4 - nsent);
if (rc <= 0)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
nsent += rc;
}
if (rc <= 0)
break;
}
On the receiving end, I have:
while((bytes = recv(socket,buffer,5,0)) > 0)
{
//printf("%d\t%d\t%d\t%d\t%d\n",(int)buffer[0],(int)buffer[1], (int)buffer[2],(int)buffer[3],(int)buffer[4]);
key = ((buffer[4] << 24) | (buffer[3] << 16) | (buffer[2] << 8) | (buffer[1]));
if((int)buffer[0] == 0)
{
do command 0, etc...
The problem I'm having is that I cant get the key. I've tried switching the order of the shifts, but all I'm getting are numbers that don't match the keys that the client is sending. I'm at a loss.
Even stranger, is that if I compile the server without the print under the while, I get seg-faulted. If I uncomment the printf, it works fine. This seems super strange.
Does anyone know what might be causing this?
Thanks in advance.
rc = write(sockfd, &net_num + nsent, 4 - nsent); is wrong, &net_num is a pointer to a 32 bits object, so &net_num+1 would point to the next 32bits object. That is beyond the object. You could cast to a char pointer, or copy to a small char buffer before sending, like below:
uint32_t net_num = htonl(num);
char buff[sizeof net_num];
memcpy (buff, &net_num, sizeof buff);
int nsent;
for nsent=0; nsent < sizeof buff; nsent += rc;)
{
rc = write(sockfd, buff + nsent, 4 - nsent);
if (rc == -1 && errno == EAGAIN) { rc=0; continue; }
if (rc <= 0)
{
printf("error! write() failed: %s\n", strerror(errno));
break;
}
}
Related
I am trying to create a client that communicate with a server by sending 2 types of messages:
The word QUIT that communicate to the server to close the connection.
An operation with the following syntax: operator first_operand second_operand. For example: + 3 3, - 5 6 etc. (the operands must be positive integers, and there must be only 2 operands).
If the server receive an operation, it executes it and returns the result to the client. The problem is that the first operation I send returns the right result, while the following ones work randomly (sometimes they return the right result, other times the function strtok() doesn't get the second operand and returns NULL...).
This is code of the client that process the message written by the user in the prompt and that scan the message to check if the operation is written with the correct syntax (WARNING: the code is written in an extremely unprofessional and unclean way).
The code part that creates the problem is inside the while(1).
#define MAXLENGTH 256
int main (int argc, char *argv[]) {
int simpleSocket = 0;
int simplePort = 0;
int returnStatus = 0;
char first[10], second[10];
char* operator;
char buffer[MAXLENGTH] = "";
char message[50];
char terminationCommand[] = "QUIT\n";
char space[2] = " ";
struct sockaddr_in simpleServer;
if (3 != argc) {
fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
exit(1);
}
/* create a streaming socket */
simpleSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (simpleSocket == -1) {
fprintf(stderr, "Could not create a socket!\n");
exit(1);
} else {
fprintf(stderr, "Socket created!\n");
}
/* retrieve the port number for connecting */
simplePort = atoi(argv[2]);
/* setup the address structure */
/* use the IP address sent as an argument for the server address */
//bzero(&simpleServer, sizeof(simpleServer));
memset(&simpleServer, '\0', sizeof(simpleServer));
simpleServer.sin_family = AF_INET;
//inet_addr(argv[2], &simpleServer.sin_addr.s_addr);
simpleServer.sin_addr.s_addr=inet_addr(argv[1]);
simpleServer.sin_port = htons(simplePort);
/* connect to the address and port with our socket */
returnStatus = connect(simpleSocket, (struct sockaddr *)&simpleServer, sizeof(simpleServer));
if (returnStatus == 0) {
fprintf(stderr, "Connect successful!\n\n");
} else {
fprintf(stderr, "Could not connect to address!\n");
close(simpleSocket);
exit(1);
}
/* get the message from the server */
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", &buffer[3]);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
memset(&buffer, '\0', sizeof(buffer));
printf("You can execute 2 commands:\n");
printf("1. Operations ( +, -, *, /, % ) with the following syntax: operator + first operand + second operand.\n");
printf("Example: + 5 2 \n");
printf("2. Termination of the connection with the following syntax: QUIT + press Enter.\n");
while(1) {
printf("\nEnter a command:\n");
fgets(message, 1000, stdin);
// the if with the termination command works fine
if (strcmp(message, terminationCommand) == 0) {
if (send(simpleSocket, message, strlen(message), 0) < 0) {
printf("Send failed.");
return 1;
}
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", &buffer[4]);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
close(simpleSocket);
exit(1);
}
operator = strtok(message, space);
if (strcmp(operator, "+") == 0 || strcmp(operator, "-") == 0 || strcmp(operator, "/") == 0 || strcmp(operator, "%") == 0 || strcmp(operator, "*") == 0) {
char *first_operand = strtok(NULL, space);
if (first_operand != NULL) {
if (strcmp(first_operand, "ANS") == 0)
strcpy(first, "ANS");
else
strcpy(first, first_operand);
printf("%s\n", operator);
printf("%s\n", first);
char *second_operand = strtok(NULL, space);
printf("%s\n", second_operand);
if (second_operand != NULL && strtok(NULL, space) == NULL && (atoi(first) > 0 || strcmp(first, "ANS") == 0)) {
if (strcmp(second_operand, "ANS\n") == 0)
strcpy(second, "ANS");
else {
strcpy(second, second_operand);
}
if (atoi(second) > 0 || strcmp(second, "ANS") == 0) {
printf("OK\n");
char operation[] = "";
strcat(operation, operator);
strcat(operation, " ");
strcat(operation, first);
strcat(operation, " ");
strcat(operation, second);
if (send(simpleSocket, operation, strlen(operation), 0) < 0) {
printf("Send failed.");
return 1;
}
returnStatus = read(simpleSocket, buffer, sizeof(buffer));
if (returnStatus > 0) {
printf("%s\n", buffer);
} else {
fprintf(stderr, "Return Status = %d \n", returnStatus);
}
}
}
}
}
// after everything I reset the buffers I use to memorize the message and the elements of the message
memset(&buffer, '\0', sizeof(buffer));
memset(&first, '\0', sizeof(first));
memset(&second, '\0', sizeof(second));
memset(&message, '\0', sizeof(message));
memset(operator, '\0', sizeof(operator));
}
}
Can someone tell me why the second strtok() acts weird 90% of the times? What am I doing wrong?
There are multiple issues in you program:
You send newline terminated messages and you assume on the other end the read will return exactly the bytes sent by the other party, which is an incorrect assumption for the TCP/IP communications, only the order of bytes received is guaranteed, but the messages can be split on the way and received in chunks different from the sending sequence. You should instead read the socket into a buffer and only handle it once you receive a newline.
In your case, there is another problem which is more pressing: the buffer into which you read the data is not null terminated, so you should not pass it to standard C functions such as strtok().
So I'm reading in files and outputting the contents of the files onto the console using system calls. Also I want to add a space to the output, every 20 lines I encounter. This is where I'm having trouble, in that despite the following few lines of code, the entire file is being displayed without the spaces
//Write file contenst
while( (nReadFile = read(nOpenFile, buffer, 1) != 0))
{
write(1, &nLineCount, sizeof(nLineCount));
if(nLineCount == 20)
{
write(1, "\n", 2);
nLineCount = 0;
}
if(nReadFile = write(1, buffer, nReadFile) == '\n')
{
nLineCount++;
}
}
Here is the entire program (Excluding the .h file containing the libraries)
#include"mymore.h"
int main(int argCount, char *argv[])
{
struct termios initial_settings, new_settings;
tcgetattr(fileno(stdin), &initial_settings);
new_settings=initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
if (tcsetattr(fileno(stdin), TCSANOW, &new_settings)!=0)
{
fprintf(stderr, "could not set attributes\n");
}
int nLineCount = 0;
int nCheckFile = 0;
int nFileCountCounter = 1; //first arguement interested in is argv[1]
int nOpenFile = 0;
int nReadFile = 0;
int nCount = 0;
char *cData;
char buffer[0];
//check that arguements have been provided
if( argCount < 2)
{
write(1, "There needs to be at least one file provided! \n", 50);
return 1;
}
do
{
printf("%d", argCount);
//check if file exists
nCheckFile = access(argv[nFileCountCounter], F_OK);
if(nCheckFile != 0) //if file does not exist
{
write(1, "The file ", 10);
write(1, argv[nFileCountCounter], strlen(argv[1]));
write(1," does not exist! \n", 25);
return 1;
}
else //file does exist
{
write(1, "Opening ", 10);
write(1, argv[nFileCountCounter], strlen(argv[1]));
write(1, "\n", 2);
}
//open the file
nOpenFile = open(argv[nFileCountCounter], O_RDONLY);
if(nOpenFile < 0)
{
write(1, "Failed to open file ", 10);
write(1, argv[nFileCountCounter], strlen(argv[nFileCountCounter]));
write(1, "\n", 2);
return 1;
}
//read file
cData = (char *) malloc(100 * sizeof(char));
cData[nReadFile] = '\0';//append null terminator
//find length of source file
while(cData[nCount] != 0)
{
nCount++;
}
//Write file contenst
while( (nReadFile = read(nOpenFile, buffer, 1) != 0))
{
write(1, &nLineCount, sizeof(nLineCount));
if(nLineCount == 20)
{
write(1, "\n", 2);
nLineCount = 0;
}
if(nReadFile = write(1, buffer, nReadFile) == '\n')
{
nLineCount++;
}
}
cout << nLineCount << endl;
//close file
close(nOpenFile);
//Increment to next file
nFileCountCounter++;
}while(nFileCountCounter < argCount);//while there are still arguements
tcsetattr(fileno(stdin), TCSANOW, &initial_settings);
return 0;
}
This is actually my first experience using system calls, and one thing I think I've noticed is that the write command is being executed before any of the c code?
Any ideas?
Thanks
There are plenty of mistakes in your code:
char buffer[0]; should be char buffer[1];
char buffer[0]; defines a buffer which can store zero character, I suppose it is not what you want.
write(1, "There needs to be at least one file provided! \n", 50); should be
char *msg = "There needs to be at least one file provided! \n";
write(1, msg, strlen(msg));
the message is longer than 50. There are serval similar mistakes, and can be fixed similarly.
while( (nReadFile = read(nOpenFile, buffer, 1) != 0)) should be
while( (nReadFile = read(nOpenFile, buffer, 1)) != 0)
In C, operator != has higher precedence than =, so nReadFile = read(nOpenFile, buffer, 1) != 0 meas nReadFile = (read(nOpenFile, buffer, 1) != 0).
if(nReadFile = write(1, buffer, nReadFile) == '\n')
If you want to check whether the currect character is a newline, you should to something like if (buffer[0] == '\n').
cout << nLineCount << endl;
This is C++, not C.
I was searching for hours to get an answer about my question, but didnt find anything. Maybe I get some help here.
What I'm trying to do:
A Java-Client sends a message to a C-Server. The message contains different types like integer, short and also a string (e.g. message = int: total_msg_length; short: operation; string: hello --> total_msg-length=4 (size of integer), operation = 2 (size of short), hello = 5 (each letter is 1 byte=5).
So, how can I receive the message in my server? The code below receives an Integer (works fine). Next step will be to receive a short and then a string (converted in US-ASCII).
int *msg;
int recv_size;
int final_msg;
if( (recv_size = recv(client_socket, &msg, sizeof(msg), 0 )) < 0 ){
error_exit("Fehler bei recv(message_len)");
}
final_msg = endian_swap(msg);
printf("Message: %d\n", final_msg);
return final_msg;
Is there a way to use a byte array instead of char buffer? Im thankful for every help. Please excuse my bad english, I'm from germany :-)
You need to create a generic "read_n_bytes" function.
This you can use to read the message-size, the operation and the text, in three successive calls.
Those three calls you then wrap in a function to be called to read an entire message.
A generic reader might look like this:
/*
* Reads n bytes from sd into where p points to.
*
* returns 0 on succes or -1 on error.
*
* Note:
* The function's name is inspired by and dedicated to "W. Richard Stevens" (RIP).
*/
int readn(int sd, void * p, size_t n)
{
size_t bytes_to_read = n;
size_t bytes_read = 0;
while (bytes_to_read > bytes_read)
{
ssize_t result = read(sd, p + bytes_read, bytes_to_read);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
# ifdef DEBUG
{
int errno_save = errno;
perror("read() failed");
errno = errno_save;
}
# endif
break;
}
else if(0 == result)
{
# ifdef DEBUG
{
int errno_save = errno;
fprintf(stderr, "%s: Connection closed by peer.", __FUNCTION__);
errno = errno_save;
}
# endif
break;
}
bytes_to_read -= result;
bytes_read += result;
}
return (bytes_read < bytes_to_read) ?-1 :0;
}
My server code is as follows:
while(bytes_written < filesize){
//Send from send_ptr
bw = send(child_socket, send_ptr, newLength, 0);
printf("%d bytes written\n", (int)bw);
//Increment bytes written
bytes_written += bw;
//Move send pointer
send_ptr = send_ptr + bw;
}
And my client code is as follows:
while((num_bytes_recv = read(sd, jpgBufferPointer, BUFFER_LENGTH))>0){
total_bytes_recv += num_bytes_recv;
printf("Read %d bytes\n",num_bytes_recv);
//Check for error
if(jpgError == NULL)
jpgError = strstr(jpgBufferPointer, "404 Not Found");
if(jpgError != NULL){
//Forwarding error response
if(send(sd, jpgBuffer, num_bytes_recv, 0) == -1){
error("Failed to send response message to client");
}
}
else{
//Find content size
contentSizeBuffer = strstr(jpgBufferPointer,"Content-Length");
if(contentSizeBuffer != NULL){
contentSizeBuffer=contentSizeBuffer+16;
contentSize=atoi(contentSizeBuffer);
jpgBuffer=(char*)realloc(jpgBuffer,(contentSize+FILE_NAME_LENGTH*2)*sizeof(char));
jpgBufferPointer=jpgBuffer;
}
jpgBufferPointer+=num_bytes_recv;
}
}
The server is saying it has sent all 43000 bytes, but client says it has received only 32768 bytes.
Appreciate any help! Thanks
You have a bug in the sending part, you should update newLength, because if you have 1 byte left to send from the file, it will send more, going out of the memory area where the content you want to send is stored. You should fix in this way:
bw = send(child_socket, send_ptr, newLength<(filesize-bytes_written)?newLength:(filesize-bytes_written), 0);
In this way the last send will have the correct size.
Also, use write instead of send if you are not using any flags.
You need to have the similar loop as you have on the writing side (bytes_written < filesize) on the reading side (i.e., while you can read more bytes, you should read them and append them).
The network doesn't guarantee that one read() call will return all available data.
The best way of writing client-server socket programming is to have a header before your data. The header should state the amount of data that it is going to transfer.
For example, To send data "Hello World", then send it as "0011+HELLO WORLD"
Here 11 stands for the size of the data the sender is planning to send now. The receiver on reading the first 4 bytes can understand that he should be ready to read next 11 bytes of data from the sender.
So reader will do two read:
hRead = 5 /* With 5 you are saying it can read upto max of 9999 bytes from data".
read(sd, buff, hRead);
dRead = atoi(buff);
readn(sd, buff, dRead);
For Example : Server
size_t sendn(int fd, const void *vptr, size_t n) {
size_t nleft;
size_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = send(fd, vptr, nleft, 0)) <= 0) {
if (errno == EINTR)
nwritten = 0;
else {
fprintf(stderr, "send failed %d - %s\n", fd, strerror(errno));
return (-1);
}
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
To send message:
sprintf(buff, "%d + %d + %s\r\n", MSG_LOGIN, strlen("Hello World"), Hello World);
sendn(sd, buff, strlen(buff));
Client:
size_t readn(int fd, void *vptr, size_t n) {
size_t nleft;
size_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nread = recv(fd, ptr, nleft, 0)) < 0) {
if (errno == EINTR)
nread = 0;
else {
fprintf(stderr, "read failed %d - %s\n", fd, strerror(errno));
return (-1);
}
} else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (n - nleft);
}
i've got a problem: sometimes (not regularly) recv returns -1 and errno == EAGAIN while using epoll in edge-triggered mode. piece of code:
server_sock = startup(&port);
if ( (epollfd = epoll_create(4096)) < 0) {
perror("epoll_create error");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = server_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &ev) == -1) {
perror("epoll_ctl: server_sock");
exit(EXIT_FAILURE);
}
while (1) {
int nfds = epoll_wait(epollfd, events, 4096, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == server_sock) {
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
(socklen_t *)(&client_name_len));
if (client_sock == -1) //server overloaded
continue;
if (events[i].events & EPOLLIN) {
std::cout << "EPOLLIN on " << client_sock << std::endl;
}
Arch::set_nonblocking(client_sock);
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; //input data and connection closing
ev.data.fd = client_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &ev) == -1) {
perror("epoll_ctl: client_socket");
exit(EXIT_FAILURE);
}
accept_request(client_sock);
} else {
if (events[i].events & EPOLLRDHUP) {
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
}
}
}
}
startup(&port) creates nonblocking socket, binding with port and so on. my script sends following data:
GET /connect?id=1&secret=1 HTTP/1.0\r\n\r\n but sometimes recv return -1 in this function (calling inside of accept_request) :
/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
* carriage return, or a CRLF combination. Terminates the string read
* with a null character. If no newline indicator is found before the
* end of the buffer, the string is terminated with a null. If any of
* the above three line terminators is read, the last character of the
* string will be a linefeed and the string will be terminated with a
* null character.
* Parameters: the socket descriptor
* the buffer to save the data in
* the size of the buffer
* Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size) {
int i = 0;
char c = '\0';
int n;
while ((i < size - 1) && (c != '\n')) {
n = recv(sock, &c, 1, 0);
//debug
std::cout << "n = " << n << std::endl;
if (n > 0) {
if (c == '\r') {
n = recv(sock, &c, 1, MSG_PEEK);
if ((n > 0) && (c == '\n'))
recv(sock, &c, 1, 0);
else
c = '\n';
}
buf[i] = c;
i++;
} else {
//debug
if (errno == EWOULDBLOCK)
std::cout << "EWOULDBLOCK" << std::endl;
c = '\n';
}
}
buf[i] = '\0';
return(i);
}
as epoll man page wrote i have to read/write until i get EAGAIN, but i got it already! and i sure the buffer don't be empty. what i do wrong?
UPD: i've found out an interesting thing: when such situation is happened i use in my code sleep(1) and recc(...) again and i get data that i expect! it's a dirty trick. is there any more graceful approach to solve this problem?
It's completely normal for the first recv() in that case to return EAGAIN. epoll() never told you if it was readable yet or not.
Every single recv() should be prepared to handle EAGAIN if you are using non-blocking sockets. Spurious wakeups are possible, so whenever an API like select(), poll() or epoll() tells you that a socket is readable, it's only saying "it might be readable - give it a try".