Related
I'm working on a TCP client server program which is supposed to support several clients using threads.
The socket creation, connection, bind and accept work as expected since I receive no errors when running the code.
However whenever I try to read() from the server the code enters an infinite loop and nothing happens.
I tried writing from the server first and the write result was written to the server's terminal.
Client code:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
//int rv;
//char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
exit(1);
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Open file for reading
FILE *fp;
fp = fopen(FILE_ADDR, "r");
if (fp == NULL) {
perror("Error in file open\n");
}
printf("file opened\n");
size_t numOfbytesRead = fread(buffer, sizeof(char), length, fp);
if (numOfbytesRead != length) {
perror("Error reading from file\n");
}
printf("Buffer is %s\n", buffer);
char* ptr;
unsigned int N = strtoul(argv[3],&ptr,10);
int convertedNum = htonl(N);
if (write(sockfd, &convertedNum, sizeof(unsigned int)) < 0) { //Send number of bytes
perror("error writing to socket");
}
if (write(sockfd, buffer, sizeof(buffer) < 0)) {//Send bytes read from file
perror("error writing to socket");
}
printf("send is done \n");
int bytes_read = read(sockfd, recvBuf, sizeof(recvBuf)); //Recieve response from server
if (bytes_read <= 0) {
perror("Error in recieving result from server\n");
}
unsigned int C = 0;
sprintf(recvBuf[0], "%d", C);
fclose(fp);
printf("# of printable characters: %u\n", C);
exit(0);
free(buffer);
free(recvBuf);
}
Server code:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
void *compute(void *socket_desc) {
int count = 0;
int sock = *(int*) socket_desc;
printf("now will allocate N \n");
int n=0;
if (write(sock, "hi", 2) < 0) { //Send number of bytes
perror("error writing to socket\n");
}
if (read(sock, &n, sizeof(unsigned int)) < 0) {
perror("Error reading from socket\n");
exit(1);
}
int N = ntohl(n);
printf("len is %d\n", N);
char* data = calloc(N, sizeof(char));
int len = read(sock, data, N);
printf("data is %s\n", data);
if (len < 0) {
perror("Error reading from socket\n");
exit(1);
}
for (int i = 0; i < len; i++) {
int tmp = 0;
sprintf(data[i], "%d", tmp);
if (tmp >= 32 & tmp <= 126) {
count++;
__sync_fetch_and_add(&pcc_total[tmp], 1);
}
}
char scount[100];
atoi(count);
write(sock, count, strlen(scount));
free(data);
pthread_exit(NULL);
close(sock);
exit(0);
}
int main(int argc, char *argv[]) {
unsigned int port = atoi(argv[1]);
signal(SIGINT, intHandler);
int socket_desc, client_sock, c, *new_sock;
struct sockaddr_in server, client;
c = sizeof(struct sockaddr_in);
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create socket");
exit(1);
}
printf("socket created\n");
memset(&server, 0, c);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
if (0 != bind(socket_desc, (struct sockaddr*) &server, sizeof(server))) {
perror("\n Error : Bind Failed \n");
exit(1);
}
printf("bind created\n");
if (0 != listen(socket_desc, 10)) {
perror("\n Error : Listen Failed \n");
exit(1);
}
printf("listen created\n");
while (keepRunning) {
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
exit(1);
}
printf("accept created\n");
pthread_t tid;
new_sock = malloc(100*sizeof(int));
*new_sock = client_sock;
if ((pthread_create(&tid, NULL, compute, (void*) new_sock)) < 0) {
perror("could not create thread\n");
exit(1);
}
printf("thread created\n");
// close socket
close(client_sock);
free(new_sock);
pthread_join(tid, NULL);
}
exit(0);
}
I run the code with the following commands:
gcc -std=c99 -O3 -Wall -o pcc_server pcc_server.c -pthread
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
There are a number of problems with your code.
On the client side:
When calling fread(), you need to use "rb" instead of "r".
when calling printf() to output the file data that was actually read, you are not null-terminating buffer, or passing its length to printf(). You need to do so.
You are assigning the return value of htonl() to an int instead of an unsigned int.
when calling write() to send the buffer, you are using sizeof(buffer) when you should be using length or N instead (and why are you using two separate variables to hold the same command-line parameter value?). buffer is a pointer to memory allocated with malloc(), so sizeof(buffer) is the same as sizeof(void*), which is not what you want. Also, you are not even calling write() correctly, because your parenthesis are all wrong (they are correct on the previous write() call when sending convertedNum).
likewise, when calling read() to read the recvBuf, you are using sizeof(recvBuf) when you should be using 10 instead, sicne recvBuf is also a pointer to malloced memory.
you are not reading the "hi" greeting that the server sends to the client upon connection, so you lump in those bytes with the bytes of the following size value of the next message, and thus end up with a corrupted C value.
On the server side:
your compute thread routine sends a "hi" greeting to the client, but it does not use any kind of delimiter, like prefixing the greeting with its length, or terminating it with a line break or null character or other unique character, to separate it from any subsequent data. You should always delimit your messages in some manner.
you are closing the accepted socket and freeing the malloced new_sock as soon as you create a worker thread to handle that client. You are ripping away memory from behind the thread's proverbial back. The thread needs to be the one to close the socket and free the memory when it is done using them, not the accept loop.
The thread does attempt to close the socket (but not free the memory), but after it calls pthread_exit() first, which is wrong. pthread_exit() terminates the calling thread, so it needs to be the last thing that the thread calls (DO NOT call exit()!). In fact, don't even call pthread_exit() directly at all, just return from compute(), the pthreads library will then call pthread_exit() for you, passing it whatever void* value you choose to return.
your accept loop should not be calling pthread_join() at all. It blocks the calling thread until the specified thread terminates. That defeats the whole purpose of using threads to handle your clients, and prevents your server from accepting more than 1 client at a time. If you are going to use pthread_join() at all, it should be after the accept loop has ended, so you can wait for any worker threads that may still be running before exiting the app. But that also means keeping track of the pthread_t values that pthread_create() returns, which is more work.
With that said, try this code instead:
Client code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
char* readMsg(int sockfd, size_t *msgSize)
{
*msgSize = 0;
unsigned int length = 0;
int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
return NULL;
}
length = ntohl(length);
char *buffer = malloc(length+1);
if (!buffer) {
perror("Error in allocating memory to receive message from server\n");
return NULL;
}
char *pbuf = buffer;
unsigned int buflen = length;
while (buflen > 0) {
bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
free(buffer);
return NULL;
}
pbuf += bytes_read;
buflen -= bytes_read;
}
*msgSize = length;
return buffer;
}
int sendMsg(int sockfd, char *msg, size_t msgSize)
{
unsigned int convertedNum = htonl(msgSize);
if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
perror("error writing to socket");
return -1;
}
if (write(sockfd, msg, msgSize) < 0) { //Send bytes
perror("error writing to socket");
return -1;
}
return 0;
}
int main(int argc, char *argv[]) {
char* ptr;
//Get command line arguments
unsigned int port = atoi(argv[2]);
unsigned int length = strtoul(argv[3], &ptr, 10); //Number of bytes to read
char* buffer = malloc(length); //Buffer to hold data read from file
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd == -1) {
perror("socket create/connect error\n");
return 1;
}
size_t msgSize;
char *msg = readMsg(sockfd, &msgSize);
if (!msg) {
close(sockfd);
return 1;
}
printf("%.*s\n", (int)msgSize, msg);
free(msg);
//Open file for reading
FILE *fp = fopen(FILE_ADDR, "rb");
if (fp == NULL) {
perror("Error in file open\n");
close(sockfd);
return 1;
}
printf("file opened\n");
if (fread(buffer, 1, length, fp) != length) {
perror("Error reading from file\n");
fclose(fp);
close(sockfd);
return 1;
}
fclose(fp);
printf("Buffer is %.*s\n", (int)length, buffer);
if (sendMsg(sockfd, buffer, length) != 0) {
free(buffer);
close(sockfd);
return 1;
}
free(buffer);
printf("send is done \n");
msg = readMsg(sockfd, &msgSize); // response from server
if (!msg) {
close(sockfd);
return 1;
}
printf("# of printable characters: %.*s\n", (int)msgSize, msg);
free(msg);
return 0;
}
Server code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
char* readMsg(int sockfd, size_t *msgSize)
{
*msgSize = 0;
unsigned int length = 0;
int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
return NULL;
}
length = ntohl(length);
char *buffer = malloc(length+1);
if (!buffer) {
perror("Error in allocating memory to receive message from server\n");
return NULL;
}
char *pbuf = buffer;
unsigned int buflen = length;
while (buflen > 0) {
bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
free(buffer);
return NULL;
}
pbuf += bytes_read;
buflen -= bytes_read;
}
*msgSize = length;
return buffer;
}
int sendMsg(int sockfd, char *msg, size_t msgSize)
{
unsigned int convertedNum = htonl(msgSize);
if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
perror("error writing to socket");
return -1;
}
if (write(sockfd, msg, msgSize) < 0) { //Send bytes
perror("error writing to socket");
return -1;
}
return 0;
}
void *compute(void *socket_desc) {
int sock = * (int*) socket_desc;
free(socket_desc);
if (sendMsg(sock, "hi", 2) != 0) {
perror("error writing to socket\n");
close(sock);
return NULL;
}
size_t length = 0;
char *data = readMsg(sock, &length);
if (!msg) {
close(sock);
return NULL;
}
printf("len is %d\n", (int)length);
printf("data is %.*s\n", (int)length, data);
int count = 0;
for (size_t i = 0; i < length; i++) {
// ...
}
free(data);
char scount[20];
sprintf(scount, "%d", count);
sendMsg(sock, scount, strlen(scount));
close(sock);
return NULL;
}
int main(int argc, char *argv[]) {
unsigned int port = atoi(argv[1]);
signal(SIGINT, intHandler);
int socket_desc, client_sock, c, *new_sock;
struct sockaddr_in server, client;
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create server socket");
return 1;
}
printf("server socket created\n");
memset(&server, 0, c);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
if (bind(socket_desc, (struct sockaddr*) &server, sizeof(server)) < 0) {
perror("\n Error : Bind Failed \n");
close(socket_desc);
return 1;
}
printf("bind created\n");
if (listen(socket_desc, 10) < 0) {
perror("\n Error : Listen Failed \n");
close(socket_desc);
return 1;
}
printf("listening\n");
while (keepRunning) {
c = sizeof(client);
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
continue;
}
printf("client accepted\n");
new_sock = malloc(sizeof(int));
if (!new_sock) {
perror("\n Error : Malloc Failed\n");
close(client_sock);
continue;
}
*new_sock = client_sock;
pthread_t tid;
if (pthread_create(&tid, NULL, &compute, new_sock) != 0) {
perror("\n Error : Thread Create Failed\n");
free(new_sock);
close(client_sock);
continue;
}
printf("thread created\n");
}
close(socket_desc);
return 0;
}
I think you should remove the two lines
close(client_sock);
free(new_sock);
in the server code because the newly created thread can't perform on those variables and memory area if it is freed up at such an early point. Can you try the code again without it?
Your server closes the connected socket and frees the memory in which you stored its file handle immediately after launching the thread to handle that connection. You're unlucky that the server only hangs as a result, but you have data races, so formally your program's behavior is undefined.
Since the server isn't going to do anything else until the thread has finished, you might as well move the close() and free() after the pthread_join(). Or, considering that you do join before creating any other threads, how about just calling compute() synchronously instead of creating a new thread for it to run in?
I am trying to create a portscanner in c. If the port is open, I want to get a response from the server. When I use regular blocking sockets, this works fine. For example, I know that for a certain address on my network, if I check port 80, it will return the html page to me when I call recv. I have tested this, and it works correctly every time.
However, I want to use nonblocking sockets, because sometimes certain servers will not respond and will cause the program to hang. I was able to get the nonblocking sockets to (kindof) work (the code is currently commented out below). I could see which ports were open, which were closed, and which timed out, but I was not able to get a response from the server (even though I know it should send one). What am I doing wrong?
tl;dr: When using nonblocking sockets (vs blocking), recv doesn't return any data.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, test_sock;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
fd_set fdset;
struct timeval tv;
int opts;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
//printf("Here1\n");
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
//server_addr.sin_port = htons(atoi(argv[2]));
test_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
for(int i=START_PORT; i<=END_PORT; i++) {
printf("Here2\n");
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //created the tcp socket
//opts = fcntl(sock, F_SETFL, O_NONBLOCK);
printf("Here3\n");
if (sock < 0)
{
perror("Socket()\n");
exit(1);
}
server_addr.sin_port = htons(i);
// connect to server
printf("Here4\n");
err_code = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
printf("Here5\n");
/* ... */
if (err_code < 0) {
printf("Port %d: connection refused\n", i);
//exit(3);
} else {
printf("Port %d:\n", i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock, message, strlen(message), 0);
if (num_bytes < 0) {
perror("send");
exit(4);
}
unsigned total_bytes_received = 0;
while(1) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if(num_bytes <= 0){
break;
}
total_bytes_received += num_bytes;
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
close(sock);
}
// close sock to release resource
close(sock);
return 0;
}
SOLUTION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, sock_test;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
int valid = 1;
fd_set fdset;
struct timeval tv;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
for(int i=START_PORT; i<=END_PORT; i++) {
sock_test = socket(AF_INET, SOCK_STREAM, 0);
if (sock_test < 0)
{
perror("Socket()\n");
exit(1);
}
fcntl(sock_test, F_SETFL, O_NONBLOCK);
server_addr.sin_port = htons(i);
connect(sock_test, (struct sockaddr *)&server_addr, sizeof(server_addr));
FD_ZERO(&fdset);
FD_SET(sock_test, &fdset);
tv.tv_sec = 3;
tv.tv_usec = 0;
if (select(sock_test + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock_test, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", argv[1], i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
printf("Here6\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock_test, message, strlen(message), 0);
printf("Here7\n");
int retry = 3;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock_test, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
else
{
//printf("%s:%d is closed\n", argv[1], i);
}
} else {
printf("timed out\n");
valid = 0; //set the boolean flag to false
}
close(sock_test);
}
// close sock to release resource
close(sock_test);
return 0;
}
As pointed in comments, in non-blocking mode, you have to handle cases when
server is not ready to send data.
For man recv(3)
Return Value
Upon successful completion, recv() shall return the length of the message in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.
Errors
The recv() function shall fail if:
EAGAIN or EWOULDBLOCK
The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is available and either the socket's file descriptor is marked O_NONBLOCK or the socket does not support blocking to await out-of-band data.
Since your client may try to read before the server send something, you must
adapt your code to wait:
/* maximum number of retry, one second per retry */
int retry = 10;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}
I have a question about socket.I send N-size data from client to server, N-size less than 100 byte.So I think my data should not be split to multiple tcp packet.In my opinion, Client send data should be done at one times and Server can receive data at one time.But The result is not satisfactory.Real situation is the server need call read data.I don't understand it.Follow code:
epoll_server.cpp(only receive data.)
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netdb.h>
#define BUFSIZE 1024
#define INITSIZE 1024
#define MAXEVENTCOUNT 10240
// add non-blocking to sockfd
int make_socket_non_blocking(int fd)
{
// get initial flag
int src_flags;
src_flags= fcntl(fd, F_GETFL,0);
if(src_flags == -1)
{
perror("fcntl get error.");
return-1;
}
// add non-blocking
int new_flags = src_flags | O_NONBLOCK;
int ret_value;
ret_value = fcntl(fd, F_SETFL, new_flags);
if(ret_value == -1)
{
perror("fcntl set error.");
return-1;
}
return 0;
}
// main function
int main(int argc, char* argv[])
{
int server_sockfd, client_sockfd;
int server_len;
struct sockaddr_in server_address;
// create server socket fd
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
// init server address struct
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9567);
server_address.sin_addr.s_addr = INADDR_ANY;
server_len = sizeof(server_address);
// bind server address info for server fd
if((bind(server_sockfd, (struct sockaddr*)&server_address, server_len)) == -1)
{
perror("bind error");
exit(EXIT_FAILURE);
}
// let server is listened state
listen(server_sockfd, 5);
printf("server start waiting for connect...\r\n");
// only suggestion
int efd = epoll_create(INITSIZE);
if(-1 == efd)
{
printf("epoll_create error happen.\n");
return -1;
}
// set server_sockfd
struct epoll_event server_event, event;
server_event.data.fd = server_sockfd;
server_event.events = EPOLLIN | EPOLLET;
int ret_epollctl = epoll_ctl(efd, EPOLL_CTL_ADD, server_sockfd, &server_event);
if(-1 == ret_epollctl)
{
printf("epoll_ctl error happen when efd is adding server_sockfd.\n");
return -1;
}
/* event loop */
struct epoll_event* return_events;
// set timeout is 3000 ms
int timeout_msecond = 3000;
return_events = (struct epoll_event*)malloc(MAXEVENTCOUNT*sizeof(struct epoll_event));
int count = 0;
while(1)
{
int ret_epollwait = epoll_wait(efd, return_events, MAXEVENTCOUNT, timeout_msecond);
// part_1:epoll_wait error happen
if(-1 == ret_epollwait)
{
printf("logged epoll_wait error happen.\n");
continue;
}
// part_2:epoll_wait timeout
if(0 == ret_epollwait)
{
printf("logged epoll_wait timeout.\n");
continue;
}
// part_3:do some other event
int index = 0;
for(index = 0; index < MAXEVENTCOUNT; index++)
{
// part_3-1:hup ...
if((return_events[index].events & EPOLLERR)
|| (return_events[index].events & EPOLLHUP)
|| !(return_events[index].events & EPOLLIN) )
{
continue;
}
// part_3-2:is connection
if(return_events[index].data.fd == server_sockfd)
{
struct sockaddr_in client_address;
int client_len = sizeof(client_address);
// server accept connection from client
int client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, (socklen_t*)&client_len);
// part_3-2-1:connection error happen
if(-1 == client_sockfd)
{
if((EAGAIN == errno)
|| (EWOULDBLOCK == errno) )
{
continue;
}
else
{
printf("accept error occured.\n");
continue;
}
}
else // part_3-2-2:normal connection
{
// get clinet some information
char hostinfo_buf[BUFSIZE] = {0};
char servname_buf[BUFSIZE] = {0};
int tmp_ret = getnameinfo((struct sockaddr*)&client_address, client_len, hostinfo_buf, sizeof(hostinfo_buf), servname_buf, sizeof(servname_buf), NI_NUMERICHOST| NI_NUMERICSERV);
if(0 == tmp_ret)
{
printf("Accepted connection on descriptor %d:ip=%s, port=%s.\n", client_sockfd, hostinfo_buf, servname_buf);
}
// set client_sockfd to non-blocking
tmp_ret = make_socket_non_blocking(client_sockfd);
if(-1 == tmp_ret)
{
printf("set client_sockfd=%d to non-blocking error occured.\n", client_sockfd);
abort();
}
// set client_sockfd is EPOLLIN, EPOLLET
event.data.fd = client_sockfd;
event.events = EPOLLIN | EPOLLET;
tmp_ret = epoll_ctl(efd, EPOLL_CTL_ADD, client_sockfd, &event);
if(tmp_ret == -1)
{
printf("efd add %d has a error.\n", client_sockfd);
continue;
}
printf("add descriptor %d:ip=%s, port=%s successfully.\n", client_sockfd, hostinfo_buf, servname_buf);
}
continue;
}
// part_3-3:read data from client
printf("read data start++++\n");
int temp = 0;
// get recv_cache size start
int recvsize = 0;
socklen_t optlen = sizeof(recvsize);
int err = getsockopt(return_events[index].data.fd, SOL_SOCKET, SO_RCVBUF, &recvsize, &optlen);
printf("recv cache size :%d\n", recvsize);
// get recv_cache size end
while(1) // start while(1)
{
printf("%d times read data\n", ++temp);
char* recv_buffer = (char*)malloc(1024+1);
memset(recv_buffer, 0, 1025);
// int ret_read = read(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer));
int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
// part_3-3-1:read return error
if(-1 == ret_read)
{
if(EAGAIN != errno)
{
printf("read data from %d error occured, errno=%d, %s.\n", return_events[index].data.fd, errno, strerror(errno));
}
break;
}
// part_3-3-2:no data
if(0 == ret_read)
{
continue;
}
// part_3-3-3:output data. If data is 'bye', connection will close.
if(ret_read > 0)
{
printf("%d client's data:size=%dbyte, content=%s\n", return_events[index].data.fd, ret_read, recv_buffer);
// part_3-3-3-1:close connection and remove client_sockfd
if((recv_buffer[0] == 'b')
&& (recv_buffer[1] == 'y')
&& (recv_buffer[2] == 'e') )
{
close(return_events[index].data.fd);
printf("close %d, ", return_events[index].data.fd);
int tmp_ret = epoll_ctl(efd, EPOLL_CTL_DEL, return_events[index].data.fd, NULL);
if(tmp_ret == -1)
{
printf("efd del %d has a error.\n", client_sockfd);
}
printf("remove descriptor %d successfully.\n", return_events[index].data.fd);
}
}
} // end of while(1)
printf("read data finish------\n");
}
}
free(return_events);
// close server_sockfd
shutdown(server_sockfd, 2);
return 0;
}
epoll_client.cpp(only send data.)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFSIZE 1024
int main(int argc, char* argv[])
{
int sock_clientfd, ret_recvsize, i;
struct sockaddr_in dest, mine;
char send_buffer[BUFSIZE + 1];
// create socket fd
if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Socket");
exit(EXIT_FAILURE);
}
// init server address that client will connetct to.
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(9567);
if(argc != 2)
{
printf("Usage: %s <dest ip>\n", argv[0]);
printf("Usage: %s 127.0.0.1\n", argv[0]);
return -1;
}
printf("-----\n");
if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
{
perror(argv[1]);
exit(1);
}
// connect to server
printf("will connect!\n");
if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
{
perror("Connect ");
exit(EXIT_FAILURE);
}
while(1)
{
bzero(send_buffer, BUFSIZE + 1);
printf("input message:");
fgets(send_buffer, BUFSIZE, stdin);
send_buffer[strlen(send_buffer) - 1] = '\0';
printf("%d\n", strlen(send_buffer));
int send_retsize = send(sock_clientfd, send_buffer, strlen(send_buffer), 0);
if(send_retsize == -1)
{
perror("send data to client error happen!");
exit(EXIT_FAILURE);
}
printf("send succ data:%s\n", send_buffer);
if((send_buffer[0] == 'b')
&& (send_buffer[1] == 'y')
&& (send_buffer[2] == 'e') )
{
printf("client active close connect.\n");
break;
}
}
// close sock_clientfd
close(sock_clientfd);
return 0;
}
Follow pircture is some run info:
epoll_server.png
epoll_client.png
The server read data is only 8 byte, Is the kernel design epoll is this?
I guess the reasons are as follows pirture:
The reason you don't receive everything that is available in one read is because you only read 8 bytes at a time.
char* recv_buffer = (char*)malloc(1024+1);
int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
// part_3-3-1:read return error
recv_buffer is a char* not an array, so sizeof recv_buffer equals the size of a pointer which in your case is 8.
Note that you should never rely on data arriving in packages. If your message protocol states that you should be getting 10 bytes never expect all 10 bytes to be available at once. You should always code in a way that can handle data being split up into multiple reads.
If the thread handles a single socket then a simple do { read... } while (total_bytes_received < expected_bytes); will suffice.
If the thread handles multiple connections, then you need to save the bytes you have read and then continue to manage other sockets that are ready before returning to your handling loop that will use select/epoll to wait for more data.
I am benchmarking local socket performance on Linux and Solaris for a project. For some reason I cannot find out, performance on Solaris is roughly 100x worse than on Linux. In Linux, opening a socket, exchanging one very short (2 char) message each way and closing it takes about 10us elapsed time. On Solaris, the same thing takes about 1000us.
Set-up is Solaris 10 developer vm in Virtual Box and Linux both in the same Virtual Box and directly on the same hardware (makes no difference).
Is this a known issue with Solaris? Any ways to work around it? I cannot use a local network connection instead for reasons I cannot go into here.
Code for client and server below. Compile with "cc -fast -m64 -lrt -lsocket -lnsl -o server server.c" and the equivalent for the client. Gcc 3.4.3 as delivered with Solaris 10 gives comparable results. This code has been cut down, for example timeouts have been removed end error handling is minimal.
server.c:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#define DIRECTORY "sub/"
#define FULL_PATH "sub/c_socket"
#define MAX_COMMAND_LEN 8192
#define PERMISSIONS 0700
void on_error(int err, char * msg) { // simple convenient error handler
if (err == -1) { // Tests whether 'err' is -1 and
perror(msg); // prints error and msg if so.
exit(-1);
}
}
int main() {
struct sockaddr_un addr;
int srv_fd, inst_fd;
int inst_adr_size;
char c;
int ret;
char readbuf[MAX_COMMAND_LEN];
int num_read;
fd_set rfds;
int fail;
int i;
// make address
memset(&addr, 0, sizeof(addr)); // clear out addr
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, FULL_PATH, sizeof(addr.sun_path));
// Remove old pseudo file if present
ret = unlink(FULL_PATH);
if (ret == -1 && errno != ENOENT) {
on_error(ret,"\nRemoving old socket file\n");
}
// Remove old directory if present
ret = rmdir(DIRECTORY);
if (ret == -1 && errno != ENOENT) {
on_error(ret, "\nRemoving old socket directory\n");
}
// Re-create new directory with appropriate permissonsm
ret = mkdir(DIRECTORY, PERMISSIONS);
on_error(ret,"\nCreating directoroy for socket file\n");
// create server listening socket
srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
on_error(srv_fd, "\nSocket creation:\n");
// bind server listening socket to address
ret = bind(srv_fd, (struct sockaddr *) &addr, sizeof(addr));
on_error(ret, "\nSocket binding:\n");
// set file permissions for socket file (somewhat redundant)
ret = chmod(FULL_PATH, PERMISSIONS);
on_error(ret, "\nSetting socket file permissions\n");
// set socket listening and queue length
ret = listen(srv_fd, 10);
on_error(ret, "\nSet socket to listen:\n");
while(1) {
// accept requests
inst_fd = accept(srv_fd, NULL, NULL);
on_error(inst_fd, "\n accepting connection:\n");
// prepare to use select on inst_fd
FD_ZERO(&rfds);
FD_SET(inst_fd, &rfds);
// now interact with the client on the instance socket.
while(1) {
num_read = 0;
while (1) {
// read a line terminated by '\n'
ret = select(inst_fd + 1, &rfds, NULL, NULL, NULL);
on_error(ret, "\nSelect on socket\n");
if (ret == 1) {
// we can read something
ret = recv(inst_fd, readbuf+num_read, MAX_COMMAND_LEN-num_read, 0
on_error(ret, "\nrecv:\n");
if (ret == 0) {
break; // we have EOF
}
num_read += ret;
if (readbuf[num_read - 1] == '\n') {
break;
}
}
} /* reading one input line done */
if (num_read == 0) break; // EOF propagated
// process command: Just send 2 chars back
ret = send(inst_fd, "n\n", 2, 0);
}
close(inst_fd); // clean up
}
// runs forever...
}
client.c:
#include <stdio.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RCVBUFSIZE 8192 /* Size of receive buffer */
#define FULL_PATH "sub/c_socket"
#define CYCLES 100000
void on_error(int err, char * msg) { // more convenient error output
if (err == -1) { // Tests whether 'err' is -1 and
perror(msg); // prints error and msg if so.
exit(-1);
}
}
int main(int argc, char *argv[]) {
int client_fd;
struct sockaddr_un addr;
char readbuf[RCVBUFSIZE+1];
int num_read;
int ret;
int count;
fd_set rfds;
char * msg = "N\n";
// make address
memset(&addr, 0, sizeof(addr)); // clear out addr
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, FULL_PATH, sizeof(addr.sun_path));
for(count = 0; count < CYCLES; count++) {
// create socket
client_fd = socket(PF_UNIX, SOCK_STREAM, 0);
on_error(client_fd, "socket() failed");
// prepare to use select on inst_fd
FD_ZERO(&rfds);
FD_SET(client_fd, &rfds);
// connect
ret = connect(client_fd, (struct sockaddr *) &addr, sizeof(addr));
on_error(ret, "connect() failed");
// send msg to server
ret = send(client_fd, msg, 2, 0);
if (ret != 2) {
on_error(-1, "\nnot all bytes sent\n");
}
num_read = 0;
// read until we have a '\n'
while (1) {
ret = select(client_fd + 1, &rfds, NULL, NULL, NULL);
on_error(ret, "\nSelect on socket\n");
if (ret == 1) {
// we can read something
ret = recv(client_fd, readbuf + num_read, RCVBUFSIZE - num_read, 0)
on_error(ret, "\nrecv:\n");
num_read += ret;
if (readbuf[num_read - 1] == '\n') break;
}
}
if (num_read == 0) break;
close(client_fd);
}
return(0);
}
I had a similar issue when I was studying sockets and tried writing an ftp server: because of a bug in the conversion to ascii I ended up writing files one byte at a time, but on linux it was ok, while on windows I ended up with something like 100KB/s on the loop interface... if that is the case, increasing the number of bytes should lessen the difference a lot.
It seems that under linux the act of requesting a system call is simply faster.
PS
I don't know much about the internals of an operating system, so if anyone can share some pointers to understand the issue (like http://yarchive.net/comp/linux/linux_speed.html) I'd be grateful.
There are some strange things happening in my client-server application. Please, look at these simple fork client/server:
CLIENT:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/wait.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
#define NUMFILES 3
double timeElapsed(struct timeval* before, struct timeval* after) {
return after->tv_sec - before->tv_sec + (double) (after->tv_usec - before->tv_usec)/1000000;
}
void getFile(char *request, struct sockaddr_in server) {
char buffer[1024];
int sockProc, res;
int file;
int sizeServ = sizeof(server);
int writeFile;
sockProc = socket(AF_INET, SOCK_STREAM, 0);
if (sockProc < 0) {
printf("Error on creating socket client\n");
perror("");
exit(1);
}
file = open(request, O_CREAT | O_WRONLY, S_IRWXU);
res = connect(sockProc, (struct sockaddr*)&server, (socklen_t)sizeServ);
if (res < 0) {
printf("Error on connecting to server!\n");
perror("");
exit(1);
}
res = send(sockProc, (void*)request, strlen(request), 0);
memset(buffer, 0, sizeof(buffer));
while((res = recv(sockProc, (void*)buffer, sizeof(buffer), 0)) > 0) {
write(file, (void*)buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
}
close(sockProc);
close(file);
return;
}
int main(int argc, char** argv) {
int sockCli, res, i;
struct sockaddr_in server;
int sizeServ = sizeof(server);
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
inet_pton(AF_INET, IP_SERVER, &server.sin_addr);
server.sin_port = htons(PORT_SERVER);
char files[NUMFILES][32];
char nameFile[32];
char command[32] = "rm *.txt";
system(command);
struct timeval begin;
struct timeval end;
pid_t processes[NUMFILES];
for(i = 0; i<NUMFILES; i++) {
memset(nameFile, 0, sizeof(nameFile));
printf("Inserisci nome file (con estensione) da ricevere:\n");
scanf("%s", nameFile);
strcpy(files[i], nameFile);
}
gettimeofday(&begin, NULL);
for(i=0; i<NUMFILES; i++) {
pid_t child = fork();
if(child == 0) {
getFile(files[i], server);
exit(0);
}
else {
processes[i] = child;
continue;
}
}
/*for(i=0; i<NUMFILES; i++) {
waitpid(processes[i], NULL, 0);
}*/
wait(NULL);
gettimeofday(&end, NULL);
printf("Time elapsed on TCP is %f seconds\n", timeElapsed(&begin, &end));
return 0;
}
and the SERVER:
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
void execRequest(int* sockCli, struct sockaddr_in* client) {
char buffer[BUFFERSIZE];
char request[BUFFERSIZE];
int res;
memset(request, 0, sizeof(request));
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %s\n", request);
char resource[32] = "files/";
strcat(resource, request);
int file = open(resource, O_RDONLY);
if (file < 0) {
printf("File %s does not exist\n", request);
exit(1);
}
memset(buffer, 0, sizeof(buffer));
while((res = read(file, (void*)buffer, sizeof(buffer))) > 0) {
send(*sockCli, (void*)buffer, strlen(buffer), 0);
memset(buffer, 0, sizeof(buffer));
}
close((*sockCli));
close(file);
free(sockCli);
free(client);
return;
}
int main(int argc, char** argv) {
int sockServ, i, res;
int *sockCli;
struct sockaddr_in server;
struct sockaddr_in* client;
sockServ = socket(AF_INET, SOCK_STREAM, 0);
if(sockServ < 0) {
printf("Error in creating socket\n");
perror("");
exit(1);
}
memset(&server, 0, sizeof(server));
server.sin_addr.s_addr = inet_addr(IP_SERVER);
server.sin_port = htons(PORT_SERVER);
server.sin_family = AF_INET;
int reuse = 1;
res = setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if (res < 0) {
printf("setsockopt() REUSEADDR failed\n");
perror("");
exit(1);
}
res = bind(sockServ, (struct sockaddr*)&server, sizeof(server));
if (res < 0) {
printf("Error on bindind TCP server!\n");
perror("");
exit(1);
}
res = listen(sockServ, 5);
if (res < 0) {
printf("Error on listening TCP server!\n");
perror("");
exit(1);
}
while(1) {
sockCli = (int*)malloc(sizeof(int));
client = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
int sizeClient = sizeof(struct sockaddr_in);
*sockCli = accept(sockServ, (struct sockaddr*)client, &sizeClient);
if ((*sockCli) < 0) {
printf("accept() failed\n");
perror("");
continue;
}
printf("Connected to %s:%d\n", inet_ntoa(client->sin_addr), client->sin_port);
if( !fork() ) {
execRequest(sockCli, client);
exit(0);
}
else
continue;
}
return 0;
}
This is very strange. The processes created by the client don't terminate even if the server closes the sockets and so recv() should return 0 and let client processes exit from the loop. Moreover there's something strange about reading files:
the server simply reads files.txt but in doing this it includes the string ".txt" in the read characters and sends all this mixture to the client...why?
they are simple file mono character like
aaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaa
but the server reads and and sends:
aaaaaaaaaaaaaaaaaa.txt
aaaaaaaaaaaaaaaaaa
can I solve all this?
You can't use strlen(buffer), just because you're loading characters from a text file doesn't mean that buffer will be a valid string unless you take steps to ensure it is. And you don't; there's no termination since you can fill all of buffer with data from the file.
How many times must we play the broken record here on Stack Overflow? Don't cast malloc!
I chalk this error to failure to read the manual(s), to find out what header to include, what a string is (and hence what strlen/strcat/str*{anything}* expects of its input, what printf expects of arguments that correspond to a %s format specifier, etc.) and what read/recv produces.
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %.*s\n", res, request); // NOTE the field width provided by 'res'
By the manual, examples such as res = read(file, (void*)buffer, sizeof(buffer)) supposedly store either an error or a length. The condition ensures that the send code will only execute when it's a length value, so why not use it as one? send(*sockCli, (void*)buffer, res, 0);?
The presense of these problems seems to indicate that your method of learning isn't working. Which book are you reading? Learning C without a book is a bit like learning which berries are poisonous without communication.