Linux local communication sockets: Why is bind() not failing as expected? - c

I have this server application that sets up a local communication stream socket for clients to connect to. If a second instance of the server is launched and it tries to bind to the same address (file name), bind() should fail with EADDRINUSE. But it does not. Why?
Here is pared-down code that showcases the problem:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
int make_socket(const char *filename, int style) {
struct sockaddr_un name;
int sock;
size_t size;
sock = socket(PF_LOCAL, style, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
if ((!filename) || (filename[0] == '\0')) {
return sock;
}
name.sun_family = AF_LOCAL;
strncpy(name.sun_path, filename, sizeof(name.sun_path));
name.sun_path[sizeof(name.sun_path) - 1] = '\0';
size = SUN_LEN(&name);
if (bind(sock, (struct sockaddr *) &name, size) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
return sock;
}
int make_stream_socket(const char *filename) {
return make_socket(filename, SOCK_STREAM);
}
#define TS_SERVER "/tmp/socket-server"
int main(int argc, char *argv[]) {
int sock_server, sock_client;
struct sockaddr_un name_client;
size_t size_name_client;
int i;
fd_set client_set, selected_set;
int quitting = 0;
fprintf(stderr, "Server: starting up\n");
unlink(TS_SERVER);
sock_server = make_stream_socket(TS_SERVER);
fprintf(stderr, "Server: accepting connections on %d\n", sock_server);
if (0 > listen(sock_server, 1)) {
perror("listen(connection requests)");
exit(EXIT_FAILURE);
}
FD_ZERO(&client_set);
FD_SET(sock_server, &client_set);
while (!quitting) {
fprintf(stderr, "Server: waiting for connections\n");
selected_set = client_set; //shallow-clone client_set
if (0 > select(FD_SETSIZE, &selected_set, NULL, NULL, NULL)) {
perror("select(for readability)");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Server: connection(s) received\n");
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET(i, &selected_set)) {
if (i == sock_server) {
fprintf(stderr, "Server: accepting connection\n");
size_name_client = sizeof(name_client);
sock_client = accept(sock_server,
(struct sockaddr *) &name_client,
(socklen_t *) &size_name_client);
//Ubuntu Launchpad bug 1463553: accept() returns
// an off-by-one size_name_client
size_name_client = SUN_LEN(&name_client);
if (sock_client < 0) {
perror("accept(connection request)");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Server: accepted connection request from '%s' on %d\n",
name_client.sun_path, sock_client);
quitting = 1;
close(sock_client);
break; //out of the for
} //if (i == sock_server)
} //if (FD_ISSET(i, &selected_set))
} //for
} //while
fprintf(stderr, "Server: shutting down\n");
close(sock_server);
unlink(TS_SERVER);
exit(EXIT_SUCCESS);
}
After compilation, run a first instance from the command line, then run a second instance from another command line. The second instance should fail on bind(), but does not.

Because:
unlink(TS_SERVER);
You remove the existing socket file, which allows a new one to be created in its place.

Related

multi threaded file transfer with socket problem

what I want to make is the multi-client,server file transfer with socket.
it compiled well and seems realistic.
But Problem is, Code won't act properly and client don't send file to server folder.
I don't know what is the problem.
Can anybody see code and tell what is the problem?
it would be grateful if you change my code also.
client
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
// NOTE/BUG: this didn't provide enough space for a 5 digit port + EOS char
#if 0
enum { PORTSIZE = 5 };
#else
enum { PORTSIZE = 6 };
#endif
void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received on client !!\n");
}
int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
char *server_hostname = "127.0.0.1";
char file_path[BUFSIZ];
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
int filefd;
int sockfd;
ssize_t read_return;
struct hostent *hostent;
unsigned short server_port = 12345;
char portNum[PORTSIZE];
char remote_file[BUFSIZ];
int select;
char *client_server_files[BUFSIZ];
int i = 0;
int j;
char protoname[] = "tcp";
struct protoent *protoent;
struct sockaddr_in sockaddr_in;
in_addr_t in_addr;
// char filename_to_send[BUFSIZ];
if (argc != 3) {
fprintf(stderr, "Usage ./client <ip> <port>\n");
exit(EXIT_FAILURE);
}
server_hostname = argv[1];
server_port = strtol(argv[2], NULL, 10);
/* Get socket. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Prepare sockaddr_in. */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Do the actual connection. */
if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
return EXIT_FAILURE;
}
while (1) {
if (signal(SIGINT, sig_handler)) {
break;
}
puts("connected to the server");
puts("-----------------");
puts("|1 - listLocal| \n|2 - listServer| \n|3 - sendFile| \n|4 - help| \n|5 - exit| ");
puts("-----------------");
while (1) {
printf("------%d",select);
scanf("%d", &select);
while ( getchar() != '\n' );
switch (select) {
case 1: // list files of client's directory
system("find . -maxdepth 1 -type f | sort");
sprintf(remote_file, "%s", "listLocal");
send(sockfd, remote_file, sizeof(remote_file), 0);
break;
case 2: // listServer
sprintf(remote_file, "%s", "listServer");
send(sockfd, remote_file, sizeof(remote_file), 0);
puts("---- Files btw Server and the Client ----");
for (j = 0; j < i; ++j) {
puts(client_server_files[j]);
}
break;
case 3: // send file
memset(file_path, 0, sizeof file_path);
scanf("%s", file_path);
sprintf(remote_file, "%s", "sendFile");
send(sockfd, remote_file, sizeof(remote_file), 0);
memset(remote_file, 0, sizeof remote_file);
// send file name to server
sprintf(remote_file, "%s", file_path);
send(sockfd, remote_file, sizeof(remote_file), 0);
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open send file");
//exit(EXIT_FAILURE);
break;
}
while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;
if (read_return == -1) {
perror("read");
//exit(EXIT_FAILURE);
break;
}
if (write(sockfd, buffer, read_return) == -1) {
perror("write");
//exit(EXIT_FAILURE);
break;
}
}
// add files in char pointer array
client_server_files[i++] = file_path;
close(filefd);
break;
case 5:
sprintf(remote_file, "%s", "exit");
send(sockfd, remote_file, sizeof(remote_file), 0);
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
default:
puts("Wrong selection!");
break;
}
}
}
free(user_input);
free(server_reply);
exit(EXIT_SUCCESS);
}
server
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
struct client {
socklen_t client_len;
struct sockaddr_in client_address;
int client_sockfd;
pthread_t thread;
};
// NOTE: provide enough space for a 5 digit port + EOS char
enum { PORTSIZE = 6 };
double cpu_time_used;
clock_t start, end;
void *forClient(void *ptr);
void portCleaner(const char* port_num) {
char temp[100] = "sudo lsof -t -i tcp:";
sprintf(temp, "%s%s%s", temp, port_num, " | xargs kill -9;");
system(temp);
//puts(temp);
}
void
sig_handler(int signo)
{
if (signo == SIGINT)
printf("!! OUCH, CTRL - C received by server !!\n");
}
int
main(int argc, char **argv)
{
struct addrinfo hints,
*res;
int enable = 1;
//int filefd; // NOTE: this is never initialized/used
int server_sockfd;
unsigned short server_port = 12345u;
char portNum[PORTSIZE];
struct sockaddr_in server_address;
struct protoent *protoent;
char protoname[] = "tcp";
#if 0
int socket_index = 0;
#else
struct client *ctl;
#endif
if (argc != 2) {
fprintf(stderr, "Usage ./server <port>\n");
exit(EXIT_FAILURE);
}
server_port = strtol(argv[1], NULL, 10);
/* Create a socket and listen to it.. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
server_sockfd = socket(
AF_INET,
SOCK_STREAM,
protoent->p_proto
);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(server_port);
if (bind(
server_sockfd,
(struct sockaddr*)&server_address,
sizeof(server_address)
) == -1
) {
perror("bind");
portCleaner(argv[1]);
exit(EXIT_FAILURE);
}
if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,1);
start = clock();
while (1) {
ctl = malloc(sizeof(struct client));
if (ctl == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
ctl->client_len = sizeof(ctl->client_address);
puts("waiting for client");
ctl->client_sockfd = accept(server_sockfd,
(struct sockaddr *) &ctl->client_address, &ctl->client_len);
if (ctl->client_sockfd < 0) {
perror("Cannot accept connection\n");
close(server_sockfd);
exit(EXIT_FAILURE);
}
pthread_create(&ctl->thread, &attr, forClient, ctl);
}
return EXIT_SUCCESS;
}
void *
forClient(void *ptr)
{
end = clock();
cpu_time_used = 1000 * (((double) (end - start)) / CLOCKS_PER_SEC);
#if 0
int connect_socket = (int) ptr;
#else
struct client *ctl = ptr;
int connect_socket = ctl->client_sockfd;
#endif
int filefd;
ssize_t read_return;
char buffer[BUFSIZ];
char *file_path;
char receiveFileName[BUFSIZ];
char cmd[BUFSIZ];
// Thread number means client's id
printf("Connected time [%lf] --- Thread number [%ld]\n", cpu_time_used, pthread_self());
// until stop receiving go on taking information
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
if((strcmp(receiveFileName, "listServer") == 0
|| strcmp(receiveFileName, "listLocal") == 0 || strcmp(receiveFileName, "help") == 0
|| strcmp(receiveFileName, "exit") == 0 || strcmp(receiveFileName, "sendFile") == 0)) {
printf("--- Command <%s> ---\n", receiveFileName);
continue;
}
file_path = receiveFileName;
fprintf(stderr, "is the file name received? ? => %s\n", file_path);
filefd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
read_return = read(connect_socket, buffer, BUFSIZ);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
} while (read_return > 0);
// NOTE/BUG: filefd was never closed
close(filefd);
}
fprintf(stderr, "Client dropped connection\n");
// NOTE: do all client related cleanup here
// previously, the main thread was doing the close, which is why it had
// to do the pthread_join
close(connect_socket);
free(ctl);
return (void *) 0;
}
while (recv(connect_socket, receiveFileName, sizeof(receiveFileName), 0)) {
if((strcmp(receiveFileName, "listServer") == 0
You throw away the return value of recv, which is the only way to know how many bytes of data you received. So the rest of your code has no idea what data you actually received.
Then you pass the chunk of data you read to strcmp. But it's just a chunk of arbitrary data. It's not a string. You cannot pass something to strcmp unless it's a string.
You are missing a message protocol. Your client is supposed to send messages and your server needs to process messages. To do this, you need a message protocol that defines what a message is and then you need to write code to send and receive messages.
The recv function has no idea what your messages are and has no way to know where the message ends.
Since you're not experienced at using TCP, you should always start by specifying the protocol the server and client will use on top of TCP. If it's a message protocol, define specifically how messages will be represented on the wire. It may be helpful to look at the specifications for existing protocols layered on top of TCP such as SMTP or HTTP.
Otherwise, use a library that provides functions to send and receive messages instead of trying to use TCP directly.

Having some problems with a simple IPv6 TCP client

For some reason this keeps on throwing an error when it tries to connect to the server. The server in this case was written by the professor so I am sure that it works but I cannot seem to get this to work for me despite checking it multiple times and running all sorts of test. I will say that htons takes port 5000 and changes it which I believe it is supposed to and for some reason "::1" is also transformed to {0,0,0,0,0,0,256} in serverInfo.sin6_addr after going through inet_pton(). I believe that one of these could be the issue but it doesn't seem to matter if I hard code it in.
// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <ws2ipdef.h>
#define RCVBUFSIZ 50
void DisplayFatalErr(char *errMsg);
int main(int argc, char *argv[])
{
WSADATA wsaData;
struct sockaddr_in6 serverInfo;
//verify the args
if (argc != 4)
{
fprintf(stderr, "Not enough entries. Please try again.");
exit(1);
}
else
{
char* serverIPaddr = argv[1];
int serverport = atoi(argv[2]);
char* msg = argv[3];
size_t msgLen = sizeof(msg);
//make word for the sending of the message.
int iResult = WSAStartup(MAKEWORD(2, 0), &wsaData);
if (iResult != 0)
{
printf("Winsock did not start properly");
exit(1);
}
//create the socket
int sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
DisplayFatalErr("socket() function failed.");
}
else
{
printf("Socket connection was successful");
//getchar();
}
serverInfo.sin6_family = AF_INET6;
serverInfo.sin6_port = htons(serverport);
inet_pton(AF_INET6, serverIPaddr, &serverInfo.sin6_addr);
if (connect(sock, (struct sockaddr*) &serverInfo, sizeof(serverInfo)) < 0)
{
DisplayFatalErr("connect() function failed.");
}
else
{
printf("Connection acheived.");
}
//send the message
if (send(sock, msg, msgLen, 0) != msgLen)
{
DisplayFatalErr("Send error.");
}
else
{
printf("Messenge sent.");
}
//recieve the message
int n = 0;
int len = 0;
char rcvBuffer[BUFSIZ];
char* mrcvBuffer = rcvBuffer;
while ((n = recv(sock, rcvBuffer, RCVBUFSIZ, 0)) > 0)
{
mrcvBuffer += n;
len += n;
rcvBuffer[len] = '\0';
printf("Got a message: '%s'\n", rcvBuffer);
}
printf("You reached the end \n");
close(sock);
}
exit(0);
}

Second recv call doesn't receive data, halts the execution in C

This is my client program that requests files from the server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
void setstring(char *str){
str[MAX_LINE-1]='\0';
}
int main(int argc, char * argv[]){
FILE *fp;
struct hostent *hp;
struct sockaddr_in sin;
char *host;
char filename[MAX_LINE],buf[MAX_LINE],reply[MAX_LINE],rec_line[MAX_LINE];
int s;
char msg[MAX_LINE];
int len,new_len,rec_file;
if (argc==2) {
host = argv[1];
}
else {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
/* translate host name into peer's IP address */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
else
printf("Client's remote host: %s\n", argv[1]);
/* build address data structure */
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
/* active open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
else
printf("Client created socket.\n");
int send_file_name,rec_msg;
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
else{
printf("Client connected.\n");
/* main loop: get and send lines of text */
printf("Hello from server\n");
while(!(strcmp(reply,"bye")==0)){
printf("Enter the file name:\n");
scanf("%s",filename);
setstring(filename);
send_file_name=send(s,filename,strlen(filename)+1,0);
if(send_file_name<0)
fputs("Error sending filename",stdout);
rec_msg=recv(s,msg,sizeof(msg),0);
if(strcmp(msg,"File not found")==0)
printf("File not found\n");
else{
printf("%s\n",msg);
fp=fopen(filename,"w");
printf("CP1\n");
if(rec_file=recv(s,rec_line,sizeof(rec_line),0)>0){
printf("CP2");
printf("String recieved:%s\n",rec_line);
if(len=fwrite(rec_line,1,rec_file+1,fp)>0)
printf("Recieved file\n");
else
printf("Error writing to file\n");
}
else
printf("Not recieved\n");
}
printf("Enter 'bye' to terminate requesting files\n");
scanf("%s",reply);
}
}
return 0;
}
This is my server program that accepts request for files from the client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 256
void setstring(char* str){
str[MAX_LINE-1]='\0';
}
int main(){
FILE *fp;
struct sockaddr_in sin;
char buf[MAX_LINE],msg[MAX_LINE],*rec_line;
int len;
int s, new_s,count;
char str[INET_ADDRSTRLEN];
int error_file,send_msg,read_line,send_file;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("0.0.0.0");
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("simplex-talk: socket");
exit(1);
}
inet_ntop(AF_INET, &(sin.sin_addr), str, INET_ADDRSTRLEN);
printf("Server is using address %s and port %d.\n", str, SERVER_PORT);
if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0) {
perror("simplex-talk: bind");
exit(1);
}
else
printf("Server bind done.\n");
listen(s, MAX_PENDING);
/* wait for connection, then receive and print text */
while(1) {
if ((new_s = accept(s, (struct sockaddr *)&sin, &len)) < 0) {
perror("simplex-talk: accept");
exit(1);
}
printf("Server Listening.\n");
printf("Greetings\n");
int rec_file_name=recv(new_s,buf,sizeof(buf),0);
if(rec_file_name>0)
printf("File requested:%s\n",buf);
fp=fopen(buf,"r");
if(fp==NULL)
{
fputs("File not found\n",stdout);
strcpy(buf,"File not found");
if(error_file=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("Successfully send error message to client\n",stdout);
}
else{
bzero(buf,MAX_LINE);
printf("File found :) \n");
strcpy(buf,"OK");
if(send_msg=send(new_s,buf,strlen(buf)+1,0)>0)
fputs("File found message sent to client\n",stdout);
fseek(fp,0,SEEK_END);
int file_size=ftell(fp);
fseek(fp,0,SEEK_SET);
printf("File size:%d\n",file_size);
rec_line=(char *)malloc(sizeof(char)*(file_size));
read_line=fread(rec_line,1,file_size+1,fp);
printf("File read: %s\n",rec_line);
if(send_file=send(new_s,rec_line,strlen(rec_line)+1,0)>0)
printf("File string sent to client\n");
}
}
close(new_s);
}
The problem is that in the client, my second recv() call, where it is supposed to receive the contents of a file, shows nothing. The programs halts at that point, but the server programs displays that it has sent the file contents. The client doesn't receive it.
The basic problem is that you're not checking the return values to see how much data you actually sent and received. So when the client calls:
rec_msg=recv(s,msg,sizeof(msg),0);
it will receive up to sizeof(msg) (512) bytes, which is probably both the OK message the server is sending AND the file contents (after the NUL). Which means when it does a second recv call to get the contents, it blocks, because it already read the contents in the first call and there's no more data waiting in the receive buffer.
Your error-checking is haphazard, and consequently you're certainly missing a problem that occurs before the behavior you're observing. I recommend you follow RW Steven's idiom:
int n;
if( (n = recv(new_s, buf, sizeof(buf), 0)) < 0 ) {
err(EXIT_FAILURE, "recv %d", __LINE__);
}
Test every function call, and handle every error. For simple programs, just call err(3) on error. Do that consistently, and the program's behavior will be much less mysterious (if still occasionally surprising). And don't be afraid of the spacebar! It's easy to hit, and easier still to read.
My other bit of advice, if I may, concerns
int send_file_name,rec_msg;
Names like that are confusing. A name is almost never an integer. For I/O sizes, just use one simple name like n, len, or size. Even if you don't care for yourself, you want to care before publishing your question in an open forum. Otherwise, when people see
send_file_name=send(s,filename,strlen(filename)+1,0);
they may think send is some function other than send(2), or that the person asking the question was careless.
The main problem I see is that neither the client nor the server are handling socket I/O correctly in general. They are not handling the cases where reads and writes transfer fewer bytes then requested, you need to loop the I/O. And the client is reading too many bytes from the server anyway, which is why your second recv() is blocking. And you are relying on a disconnect to indicate the end of the file has been reached, but that does not allow the client to do adequate error checking to know if the full file was actually received or not.
Also, when sending the content of a file, the server is attempting to read the entire file into memory (bad!), not doing adequate error checking on the file I/O, and it is treating the file content as text instead of as binary (don't use strlen() on binary data!).
Try something more like this instead:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_LINE 512
int sendstring(int sock, const char *str) {
if (!str) str = "";
int len = strlen(str) + 1;
do {
int ret = send(sock, str, len, 0);
if (ret <= 0) return -1;
str += ret;
len -= ret;
}
while (len > 0);
return 0;
}
int readbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (buflen > 0) {
int len = recv(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int readfile(int sock, int fd) {
int filesize;
char buf[MAX_LINE];
if (readbuf(sock, &filesize, sizeof(filesize)) < 0) return -1;
filesize = ntohl(filesize);
while (filesize > 0) {
int len = readbuf(sock, buf, min(sizeof(buf), filesize));
if (len < 0) return -1;
if (fwrite(buf, len, 1, fp) != 1) return -2;
filesize -= len;
}
return 0;
}
int main(int argc, char * argv[]) {
char filename[MAX_LINE], reply[MAX_LINE];
if (argc != 2) {
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
char *host = argv[1];
/* translate host name into peer's IP address */
struct hostent *hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
if (hp->h_addrtype != AF_INET) {
fprintf(stderr, "simplex-talk: unsupported address type %d for host: %s\n", hp->h_addrtype, host);
exit(1);
}
/* build address data structure */
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
printf("Host's remote IP: %s\n", inet_ntoa(&sin.sin_addr));
/* active open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Client created socket.\n");
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
printf("Client connected.\n");
/* main loop: get and send lines of text */
do {
printf("Enter the file name ('bye' to quit):\n");
if (scanf("%512s", filename) != 1) {
printf("Error reading filename\n");
break;
}
if (strcmp(filename, "bye") == 0) {
sendstring(s, "bye");
break;
}
if (sendstring(s, filename) < 0) {
printf("Error sending filename\n");
break;
}
if (readstring(s, reply, sizeof(reply)) < 0) {
printf("Error reading reply\n");
break;
}
if (strcmp(reply, "OK") != 0) {
printf("%s\n", reply);
if (strcmp(reply, "bye") == 0) break;
continue;
}
FILE *fp = fopen(filename, "wb");
if (!fp) {
printf("Error opening file\n");
break;
}
printf("Receiving file\n");
int ret = readfile(s, fd);
fclose(fp);
if (ret < 0) {
if (ret == -2)
printf("Error writing file\n");
else
printf("Error reading file\n");
break;
}
printf("Received file\n");
}
while (1);
close(s);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 512
int sendbuf(int sock, void *buf, int buflen) {
char *pbuf = (char*) buf;
while (len > 0) {
int len = send(sock, pbuf, buflen, 0);
if (len <= 0) return -1;
pbuf += len;
buflen -= len;
}
return 0;
}
int sendstring(int sock, const char *str) {
if (!str) str = "";
return sendbuf(sock, str, strlen(str) + 1);
}
int sendfile(int sock, int fd) {
char buf[MAX_LINE];
struct stat s;
if (fstat(fd, &s) < 0) return -2;
int pos = ftell(fp);
if (pos == -1) return -2;
int file_size = s.st_size - pos;
int tmp_file_size = htonl(file_size);
if (sendbuf(sock, &tmp_file_size, sizeof(tmp_file_size)) < 0) return -1;
while (file_size > 0) {
int len = fread(buf, 1, min(sizeof(buf), file_size), fp);
if (len < 1) return -2;
if (sendbuf(sock, buf, len) < 0) return -1;
file_size -= len;
}
return 0;
}
int readstring(int sock, char *str, int maxlen) {
while (maxlen > 0) {
if (recv(sock, str, 1, 0) <= 0) return -1;
if (*str == '\0') return 0;
++str;
--maxlen;
}
return -2;
}
int main() {
char msg[MAX_LINE];
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("simplex-talk: socket");
exit(1);
}
printf("Server is using address %s and port %d.\n", inet_ntoa(&(sin.sin_addr)), SERVER_PORT);
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("simplex-talk: bind");
close(s);
exit(1);
}
printf("Server bind done.\n");
if (listen(s, MAX_PENDING) < 0) {
perror("simplex-talk: listen");
close(s);
exit(1);
}
printf("Server Listening.\n");
/* wait for connection, then receive and print text */
do {
int len = sizeof(sin);
int cli_s = accept(s, (struct sockaddr *)&sin, &len);
if (cli_s < 0) {
perror("simplex-talk: accept");
close(s);
exit(1);
}
printf("Client connected\n");
do {
if (readstring(cli_s, msg, sizeof(msg)) < 0) {
printf("Error reading request\n");
break;
}
if (strcmp(msg, "bye") == 0) break;
printf("File requested: %s\n", msg);
FILE *fp = fopen(msg, "rb");
if (!fp)
{
printf("Cannot open file\n");
if (sendstring(cli_s, "Cannot open file") < 0) {
printf("Error sending reply\n");
break;
}
continue;
}
printf("File found :) \n");
if (sendstring(cli_s, "OK") < 0) {
printf("Error sending reply\n");
fclose(fp);
break;
}
ret = sendfile(cli_s, fp);
fclose(fp);
if (ret < 0) {
printf("Error sending file\n");
break;
}
printf("File sent to client\n");
}
while (1);
close(cli_s);
}
while (1);
close(s);
return 0;
}

In TCP socket program,client send some data, but server need read multiple times. why?

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.

Server doesn't alloc a thread for every new client (sockets)

I made a small program server client using sockets. I'll will explain what it needs to do and then the bug.
Application has a server witch wait's for clients. It supports max 6 clients at the same time. For each client server launch a thread in DETASH_STATE and then it come back and listen.
A client will submit the context of a file to the server. (i chose to send line by line)
The server will receive the content sent by a client and save it into a file, with a unique name like: __ft_"Any random string".txt
I hope all is clear till now.
The bug:
Well if i launch the client let say 20 times. On server side i don't have 20 files. Sometimes i have 10, sometimes 12,5,7(unpredictable). It writes in a single file the content sent by 3 or 4 or 5 clients ( here i need to have 3-4-5 files). I don't get where is the bug or what i made wrong. If you need additional information please tell me.
This is the server code (a part of it):
/**********ASSING A PROTOCOL TO A SOCKET (BIND)************/
if(bind(sock_dest, (struct sockaddr *)&server,sizeof (server))<0)
{
printf("Bind failed\n");
return 1;
}
/**********************************************************/
/*************** LISTEN FOR CONNECTIONS ********************/
listen(sock_dest,6);
printf("Waiting for connections\n");
/**********************************************************/
/****ACCEPT CONNECTION AND MAKE ATHREAD FOR EVERY CLIENT***/
c = sizeof ( struct sockaddr_in);
pthread_t thread_id;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
while( ( client_socket = accept( sock_dest, ( struct sockaddr *)&client, (socklen_t *) &c)) )
{
printf("Connection accepted.\n");
if(client_socket < 0)
{
printf("Accepting connection failed.\n");
return 1;
}
if(pthread_create( &thread_id, &attr, connection_handler, (void*) &client_socket ) < 0)
{
printf("Couldn't create a thread for the new client.\n");
return 1;
}
else
printf("Handler assigned\n");
}
return 0;
}
void *connection_handler (void *socket_dest)
{
char *dest_file_name;
char buffer[300];
int sock = *( int *) socket_dest;
char *exit_signal="EXIT";
if((dest_file_name=create_random_name())==NULL)
{
printf("Generating a random name failed\n");
return NULL;
}
int i=0;
while(1)
{
while(i<299)
{
recv(sock,buffer+i,1,0);
if(buffer[i]=='\n')
{
i=0; // setting i to 0 because next time when we read a path(string) it need to be stored from 0 pozition in array.
break;
}
++i;
}
if((strcmp(buffer,exit_signal))==0)
{
printf("Exit signal received.\n");
return NULL;
}
if((write_line_in_file(buffer,dest_file_name))==1)
{
printf("Failed to write one of the lines in %s\n",dest_file_name);
}
printf("Linie primita:%s\n",buffer);
bzero(buffer,256); // put all bytes to 0 from buffer
}
return 0;
}
char *create_random_name(void)
{
const char charset[] = "abcdefghijklmnopqrstuvwxyz";
char *file_name;
int i=0;
int key;
struct stat stbuf;
time_t t;
while(1)
{
srand((unsigned) time(&t));
if((file_name = malloc(16 * sizeof ( char )) ) == NULL)
{
printf("Failed to alloc memory space\n");
return NULL;
}
strcpy(file_name,"__ft_");
for(i=5 ; i<11 ; i++)
{
key = rand() % (int)(sizeof(charset)-1);
file_name[i]=charset[key];
}
strcat(file_name,".txt");
file_name[15] = '\0';
if(stat(file_name,&stbuf)==-1)
{
break;
}
}
return file_name;
}
/************* RETURN 0 IF SUCCES AND 1 IF FAILS ***********/
int write_line_in_file(char *line,char *file_name)
{
FILE *file;
if((file=fopen(file_name, "a")) == NULL)
{
printf("Can't open %s.\n");
return 1;
}
fprintf(file,"%s",line);
fclose(file);
return 0;
}
And this is the client code (i don't think here is the problem):
#include <stdio.h>
#include <stdlib.h>
#include<netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[])
{
/***************CHECKING THE ARGUMENT*************/
if (argc < 2)
{
printf("Usage: %s hostname\n", argv[0]);
return 1;
}
/*************************************************/
int sock_dest;
struct sockaddr_in server_addr;
char line[300];
/******************CREATE SOCKET*****************/
sock_dest = socket(AF_INET, SOCK_STREAM, 0);
if(sock_dest == -1)
{
printf("Couldn't create socket.\n");
return 1;
}
struct hostent *server;
server = gethostbyname(argv[1]);
if (server == NULL)
{
printf("ERROR, no such host\n");
return 1;
}
/***** SETTING FIELDS OF SERVER SOCKADDR_IN STRUCTURE *****/
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,(char *)&server_addr.sin_addr.s_addr,server->h_length);
server_addr.sin_port = htons ( 31000 );
/**********************************************************/
/*********TRYING TO CONNECT TO THE SERVER******************/
if (connect(sock_dest,(struct sockaddr *) &server_addr,sizeof(server_addr)) < 0)
{
printf("ERROR:(Can't connect to the server. Please check if it's online)\n");
return 1;
}
/***********************************************************/
char path_read[200];
int read_len,n;
FILE *f_read;
char *exit_signal = "\nEXIT\0";
if((f_read=fopen("test.txt","r"))==NULL)
{
printf("Failed to open path.txt.\n");
return 0;
}
while((fgets(path_read,200,f_read))!=NULL)
{
path_read[strlen(path_read)+1]='\n';
n = write(sock_dest,path_read,strlen(path_read));
if(n<0)
error("ERROR writing to socket");
bzero(path_read,200);
}
n=write(sock_dest,exit_signal,strlen(exit_signal));
fclose(f_read);
close(sock_dest);
printf("All lines in the file were sent to the server.\nExiting...\n");
return 0;
}
This is one bug:
(void*) &client_socket
Firstly, no need to cast to void*. Secondly, it looks like client_socket is a local variable, who's address you pass to the thread as context. In the starting thread, you then continue to overwrite its value with the next iteration.

Resources