Related
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
static char *host = "irc.libera.chat";
static char *port = "6667";
static char *chan = "#libera";
static char *nick = "nick";
static char *pass = NULL;
static int sock = 0;
void
message(char *fmt, ...) {
va_list ap;
/* determine size */
va_start(ap, fmt);
int n = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (n < 0) {
fputs("vsnprintf() failed", stderr);
exit(EXIT_FAILURE);
}
size_t size = n + 1;
/* construct */
char *msg = malloc(size);
if (msg == NULL) {
perror("malloc() failed");
exit(EXIT_FAILURE);
}
va_start(ap, fmt);
n = vsnprintf(msg, size, fmt, ap);
va_end(ap);
if (n < 0) {
fputs("vsnprintf() failed\n", stderr);
free(msg);
exit(EXIT_FAILURE);
}
/* send */
ssize_t nsent = send(sock, msg, size, 0);
free(msg);
if (nsent == -1) {
perror("send() failed");
exit(EXIT_FAILURE);
} else if ((size_t)nsent != size) {
fprintf(stderr,
"send() failed: expected to send %lu bytes, sent %ld instead\n",
size, nsent);
exit(EXIT_FAILURE);
}
}
int
main(void) {
/* initialize connection */
struct addrinfo hints = {
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = 0,
.ai_addrlen = 0,
.ai_addr = NULL,
.ai_canonname = NULL,
.ai_next = NULL
};
struct addrinfo *res;
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(ret));
return EXIT_FAILURE;
}
struct addrinfo *rp;
for (rp = res; rp != NULL; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock == -1) {
perror("socket() failed");
continue;
}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) == -1) {
perror("connect() failed");
close(sock);
continue;
}
break;
}
freeaddrinfo(res);
if (rp == NULL) {
fprintf(stderr, "could not connect to %s:%s\n", host, port);
return EXIT_FAILURE;
}
/* log in */
if (pass)
message("PASS %s\n", pass);
message("NICK %s\n", nick);
message("USER %s - - :%s\n", nick, nick);
/* join channel */
if (chan != NULL)
message("JOIN %s\n", chan);
/* print response */
char buffer[4096];
ssize_t nbyte;
loop:
nbyte = recv(sock, buffer, 4095, 0);
if (nbyte < 0) {
fputs("recv() failed", stderr);
return 1;
} else if (nbyte == 0) {
fputs("recv() failed: connection closed prematurely", stderr);
return 1;
}
buffer[nbyte] = '\0';
printf("%s", buffer);
goto loop;
/* unreachable */
}
outputs
:calcium.libera.chat NOTICE * :*** Checking Ident
:calcium.libera.chat NOTICE * :*** Looking up your hostname...
:calcium.libera.chat NOTICE * :*** Couldn't look up your hostname
:calcium.libera.chat NOTICE * :*** No Ident response
ERROR :Closing Link: 127.0.0.1 (Connection timed out)
recv() failed: connection closed prematurely
Why am I not receiving the proper response?
Other irc clients further output
:calcium.libera.chat 001 nick :Welcome to the Libera.Chat Internet Relay Chat Network nick
...
Could the issue be in error handling?
For example, according to send(2)
On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set to indicate the error.
so
} else if ((size_t)nsent != size) {
fprintf(stderr,
"send() failed: expected to send %lu bytes, sent %ld instead\n",
size, nsent);
exit(EXIT_FAILURE);
}
seems redundant, as well as its recv counterpart.
Am I handling vsnprintf and malloc correctly?
When you trace the application (E.g. using strace) you will see the following calls:
connect(3, {sa_family=AF_INET, sin_port=htons(6667), sin_addr=inet_addr("172.106.11.86")}, 16) = 0
sendto(3, "NICK nick\n\0", 11, 0, NULL, 0) = 11
sendto(3, "USER nick - - :nick\n\0", 21, 0, NULL, 0) = 21
sendto(3, "JOIN #libera\n\0", 14, 0, NULL, 0) = 14
Meaning when sending the NICK, USER and JOIN, those strings are begin transmitted with an additional null byte at the end and the server on the other side doesn't like that.
This implies that in your code the message() method is wrong, more specifically the calculation of the size variable. If I compile your code with size decremented before the send() call, the connection to the irc server succeeds.
You are handling vsnprintf() and malloc() fine. It is send() that you are not handling correctly. There are two problems with your usage:
you are including the formatted string's null-terminator in the transmission. Don't do that, that is not part of the IRC protocol.
you are not accounting for partial transmissions, as send() can return fewer bytes than requested, thus requiring send() to be called again to send any unsent bytes. So you need to call send() in a loop. A return value that is greater than 0 but less than the number of requested bytes is not an error condition. The only error condition is a return value that is less than 0.
Try this instead:
void
message(char *fmt, ...) {
va_list ap;
/* determine size */
va_start(ap, fmt);
int n = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (n < 0) {
fputs("vsnprintf() failed", stderr);
exit(EXIT_FAILURE);
}
size_t size = n + 1;
/* construct */
char *msg = malloc(size);
if (msg == NULL) {
perror("malloc() failed");
exit(EXIT_FAILURE);
}
va_start(ap, fmt);
n = vsnprintf(msg, size, fmt, ap);
va_end(ap);
if (n < 0) {
fputs("vsnprintf() failed\n", stderr);
free(msg);
exit(EXIT_FAILURE);
}
/* send */
char *curr = msg;
--size; // don't sent null terminator!
while (size > 0) {
ssize_t nsent = send(sock, curr, size, 0);
if (nsent < 0) {
perror("send() failed");
free(msg);
exit(EXIT_FAILURE);
}
curr += nsent;
size -= nsent;
}
free(msg);
}
That said, you really shouldn't be using a goto loop in main(), either. Use a while or do..while loop instead, eg:
int
main(void) {
...
/* print response */
char buffer[4096];
int exitCode = 0;
do {
ssize_t nbyte = recv(sock, buffer, sizeof buffer, 0);
if (nbyte < 0) {
perror("recv() failed");
exitCode = 1;
} else if (nbyte == 0) {
fputs("connection closed by peer", stderr);
exitCode = 1;
} else {
printf("%.*s", nbyte, buffer);
}
}
while (exitCode == 0);
close(sock);
return exitCode;
}
I am having trouble sending and receiving files while working with sockets in C.
Let's say I have a file that is 2,463 bytes big and I want to send it from client to server but it never sends the entire file.
I've been searching the internet for a while now but couldn't find a solution so I'd be very glad if someone could tell me how to make this work.
Here is my code:
Client:
char buffer[256];
bzero(buffer, 256);
int block_size;
while(1){
block_size = fread(buffer, sizeof(char), sizeof(buffer), fs); // read from the file
if (block_size == 0){
break;
}
if (block_size < 0){
perror("ERROR: Failed while sending data.");
exit(EXIT_FAILURE);
break;
}
void *p = buffer;
while (block_size > 0) {
int bytes_written = write(clientSocket, buffer, block_size); // send the data to server
if (bytes_written <= 0) {
perror("ERROR: Failed while sending data.");
exit(EXIT_FAILURE);
}
block_size -= bytes_written;
p += bytes_written;
}
bzero(buffer, 256);
}
Server:
bzero(buffer, 256);
int file_block_size = 0;
while (1){
bzero(buffer, 256);
file_block_size = read(incoming_socket, buffer,255); // read the data from client
fwrite(buffer, sizeof(char), file_block_size, fr); // write the data to file
if (file_block_size == 0 || file_block_size < 256){
fwrite(buffer, sizeof(char), file_block_size, fr);
break;
}
}
As I've said, this never sends the entire file that is for example 2,463 bytes big, only a portion of it.
Thanks in advance, will be glad for any help I can get.
You need to pair your read with a write. As in the current state your client code will send only the result of your last read operation since you are overwriting the contents of your buffer on each read.
Try something like this on the client side:
char buffer[256];
size_t bytes_read = 0;
ssize_t bytes_written = 0;
do{
bytes_read = fread(buffer, sizeof(char), sizeof(buffer), fs);
if(ferror(fs))
{
perror("Error while reading from file.");
close(clientSocket);
fclose(fs);
exit(EXIT_FAILURE);
}
bytes_written = write(clientSocket, buffer, bytes_read);
printf("Write operation status: %s; bytes sent: %d\n", strerror(errno), bytes_written);
} while(!feof(fs) && bytes_written != -1);
And on the server side I would do:
char buffer[256];
ssize_t bytes_read = 0;
while((bytes_read = read(incoming_socket, buffer, sizeof(buffer))) > 0)
{
printf("%d bytes read\n", bytes_read);
fwrite(buffer, sizeof(char), bytes_read, fr);
if(ferror(fr))
{
perror("Error while writing to file");
close(incoming_socket);
fclose(fr);
exit(EXIT_FAILURE);
}
}
if(bytes_read == -1)
printf("%s\n", strerror(errno));
I wanted to write a simple TCP echo server application. I managed to do the echo part, but have some problems with sending files between client and server. The idea is simple: despite sending ordinary messages, client can send a special command to server (\SENDFILE filename.txt), and after receiving such command, server should ask client for this file, and get the file from client. (Further I would like to get a file from one client, and later send it to another one).
I think "the protocol" here is simple, however, after typing \SENDFILE at client's side, client hangs up, and does not receive any further messages from server. Moreover (server and client are in different directories) at server's side there's only an empty file from client, with no content inside.
Any ideas what can be wrong here?
client.c
#include<stdio.h> //printf
#include<string.h> //
#include <sys/stat.h>
#include<sys/socket.h> //socket
#include<arpa/inet.h> //inet_addr
#include <fcntl.h>
#define SERVER_PORT 9034
#define BUFF_SIZE 2000
int sendall(int s, char *buf, int len)
{
int total = 0;
int bytesleft = len;
int n;
while(total < len)
{
n = send(s, buf+total, bytesleft, 0);
if (n == -1)
break;
total += n;
bytesleft -= n;
}
return n==-1?-1:0;
}
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}
int main(int argc , char *argv[])
{
int sock;
struct sockaddr_in server;
char bufferOUT[BUFF_SIZE] , bufferIN[BUFF_SIZE];
struct stat file_stat;
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
// puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( SERVER_PORT );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
{
perror("Connect failed. Error");
return 1;
}
// puts("Connected\n");
int read_size = 10;
//keep communicating with server
while(1)
{
printf("> ");
fgets(bufferOUT, BUFF_SIZE, stdin);
//Send some data
if( send(sock , bufferOUT , BUFF_SIZE , 0) < 0)
{
perror("Send failed");
return 1;
}
//Receive a reply from the server
if( (read_size = recv(sock , bufferIN , BUFF_SIZE , 0)) < 0)
{
perror("Recv failed");
break;
}
if(read_size == 0)
break;
if(bufferIN[0] == '\\')
{
char tmp[BUFF_SIZE], filename[BUFF_SIZE], *param;
memset(filename, BUFF_SIZE, 0);
strcpy(tmp, bufferIN);
param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))
{
param = strtok(NULL, " ");
if(param != NULL)
{
strcpy(filename, param);
FILE * fp;
int nBytes;
char buffer[BUFF_SIZE], *s;
memset(buffer, 0, BUFF_SIZE);
fp = fopen(filename, "r");
if(fp == NULL)
{
perror("fopen");
fflush(stdout);
break;
}
int remain_data = file_stat.st_size;
do
{
s = fgets(buffer, BUFF_SIZE, fp);
if(s != NULL && buffer[0] != EOF)
{
nBytes = sendall(sock, buffer, BUFF_SIZE);
remain_data -= nBytes;
}
else
break;
}
while((s != NULL) && (nBytes > 0) && (remain_data > 0));
fclose(fp);
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
continue;
}
}
}
}
else
{
printf("%s\n", bufferIN);
fflush(stdout);
}
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
}
close(sock);
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <fcntl.h>
#define SERVER_PORT 9034
#define BUFF_SIZE 2000
void StripNewline(char *s)
{
while(*s != '\0')
{
if(*s == '\r' || *s == '\n')
{
*s = '\0';
}
s++;
}
}
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
memset(msg, 0, BUFF_SIZE);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);
fp = fopen(filename, "w");
if(fp == NULL)
return 0;
memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;
else
fprintf(fp, "%s\n", buffer);
}
fclose(fp);
sleep(1);
memset(buffer, 0, BUFF_SIZE);
sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);
return 1;
}
int main(void)
{
fd_set master;
fd_set read_fds;
int fdmax;
int listener;
int client_sock;
struct sockaddr_storage remoteaddr;
socklen_t addrlen;
char bufferIN[BUFF_SIZE], bufferOUT[BUFF_SIZE], tmp[BUFF_SIZE], *datetime;
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1;
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master);
FD_ZERO(&read_fds);
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
char port[16] = "9034";
if (getaddrinfo(NULL, port, &hints, &ai) < 0)
{
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next)
{
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0)
{
continue;
}
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;
break;
}
if (p == NULL)
exit(2);
freeaddrinfo(ai);
if (listen(listener, 10) == -1)
{
perror("listen");
exit(3);
}
FD_SET(listener, &master);
fdmax = listener;
printf("Server is running ...\n\n");
for(;;)
{
read_fds = master;
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
for(i = 0; i <= fdmax; i++)
{
if (FD_ISSET(i, &read_fds))
{
if (i == listener)
{
addrlen = sizeof remoteaddr;
client_sock = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (client_sock == -1)
{
perror("accept");
}
else
{
FD_SET(client_sock, &master);
if (client_sock > fdmax)
fdmax = client_sock;
}
}
else
{
if ((nbytes = recv(i, bufferIN, BUFF_SIZE, 0)) <= 0)
{
if (nbytes == 0)
close(i);
else if(nbytes == -1)
{
perror("recv");
fflush(stdout);
}
close(i);
FD_CLR(i, &master);
}
else
{
bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);
strcpy(tmp, bufferIN);
if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");
if(!strcmp(command, "\\QUIT"))
{
close(i);
FD_CLR(i, &master);
break;
}
else if(!strcmp(command, "\\SENDFILE"))
{
param = strtok(tmp, " ");
if(param != NULL)
{
param = strtok(NULL, " ");
if(param != NULL)
{
printf("Client is sending me a file '%s'...\n", param);
GetFileFromClient(i, param);
}
}
}
else
{
SendMsgToSender(bufferIN, i);
}
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
}
else
{
SendMsgToSender(bufferIN, i);
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
return 0;
}
strcpy(tmp, bufferIN);
Here you are assuming that whatever was read was null-terminated.
param = strtok(tmp, " ");
if(param != NULL)
{
if(!strcmp(param, "\\GIVEMEFILE"))
Here you are assuming that an entire message has been received.
strcpy(filename, param);
Ditto.
memset(buffer, 0, BUFF_SIZE);
Pointless. Remove.
do
{
s = fgets(buffer, BUFF_SIZE, fp);
Here you are assuming that the file consists of lines.
if(s != NULL && buffer[0] != EOF)
Testing buffer[0] !=EOF is meaningless. If you had reached EOF, s would have been null, assuming the file consists of lines, but there is nothing about a line that says anything about what its first character can be, other than that it isn't a line terminator.
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
Both pointless. Remove.
memset(bufferOUT, 0, BUFF_SIZE);
memset(bufferIN, 0, BUFF_SIZE);
Ditto.
void StripNewline(char *s)
This method appears completely pointless. Remove.
void SendMsgToSender(char *msg, int connfd)
{
write(connfd, msg, strlen(msg));
Here you are sending a string to the peer without the trailing null, which the peer is looking for at strlen() above. Have a good think about what your application protocol actually entails.
memset(msg, 0, BUFF_SIZE);
Pointless. Remove.
int GetFileFromClient(int connfd, char *filename)
{
FILE * fp = NULL;
int bytes;
char buffer[BUFF_SIZE];
memset(buffer, 0, BUFF_SIZE);
Pointless. Remove.
memset(buffer, 0, BUFF_SIZE);
Ditto.
sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
SendMsgToSender(buffer, connfd);
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
Pointless. Remove.
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
return 0;
Here you need to distinguish between (1) bytes == 0, which means the peer disconnected, and (2) byte == -1, which indicates an error, which you need to log, via errno, strerror(), and friends.
else
fprintf(fp, "%s\n", buffer);
Change to fprintf(fp, "%.*s\n", bytes, buffer). You are assuming throughout that all messages are null-terminated by TCP. They aren't.
sleep(1);
Pointless. Remove.
memset(buffer, 0, BUFF_SIZE);
Ditto.
sprintf(buffer, "\r\n");
SendMsgToSender(buffer, connfd);
Sending a line terminator to the peer appears completely pointless.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
memset(tmp, 0, BUFF_SIZE);
All pointless. Remove.
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
continue;
Here you need to print an error mesage instead of just ignoring the condition.
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
You haven't put the listening socket into non-blocking mode. Using select() is therefore pointless.
bufferIN[nbytes-1] = '\0';
StripNewline(bufferIN);
Why?
strcpy(tmp, bufferIN);
Why? What's wrong with continuing to use bufferIN?
if(bufferIN[0] == '\\')
{
char *command, *param;
command = strtok(bufferIN, " ");
Here again you are assuming a complete command was received, complete with trailing null.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
Both pointless. Remove. This is jut cargo-cult programming. recv() returns a length. Use it.
memset(bufferIN, 0, BUFF_SIZE);
memset(bufferOUT, 0, BUFF_SIZE);
Ditto, in spades.
Basically you have an application protocol problem. Specifically, you don't have an application protocol. Just a whole lot of unwarranted assumptions. If you want a trailing null, (a) send a trailing null, and (b) loop reading until you receive it. You also have an assumption about the content of the files being sent, which is completely unnecessary. Just read bytes from the file and send them to the server. No assumption about lines or line terminators necessary. If you're sending multiple files over the same connection you will need to send the file size ahead of the file, so the receiver will know exactly how many bytes to read and copy to the file.
In essence, you need to rethink this completely.
In client.c you have to initialize file_statbefore getting the size of the file stat(filename, &file_stat);
Because of this error remain_data will always have a wrong value.
In Server.c
Because of the error in the while loop as pointed out by EJP you are overwriting the file sent by the client. Basically making it empty.
Open the client filename with "r" option.
Open another file in the server and receive data to that file.
Small example to receive data of files within BUFF_SIZE. You can use the some logic and expand it to bigger files like its done in Client.c
fd = fopen(<new_file_path>, "w");
while(1)
{
memset(buffer ,0 , BUFF_SIZE);
if((bytes = recv(connfd , buffer , BUFF_SIZE , 0) ) == BUFF_SIZE)
break;
}
fprintf(fd, "%s\n", buffer);
fclose(fd);
I created my own ftp server/client (a very simple version) using TCP. There are 5 possible commands: ls-remote, ls-local, get , put and exit. Also, I am using multiprocessing in my server code, to be able to handle multiple clients simultaneously by using fork(). Everything is working perfectly except for one thing that I am noticing: I am having a problem handling broken server connect correctly. For example, if something were to go wrong while the server sends a message, I check if the return value from the send call is less than 0, and then I close the socket, and call exit(-1) to terminate the process; however, this causes my client to hang...
my client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <stdio.h>
void readDirectory();
void syserr(char* msg)
{ perror(msg);
exit(-1);
}
int main(int argc, char* argv[])
{
int sockfd, portno, n;
struct hostent* server;
struct sockaddr_in serv_addr;
char buffer[256], temp[256];
if(argc < 3) {
fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
return 1;
}
portno = atoi(argv[2]);
server = gethostbyname(argv[1]);
if(!server) {
fprintf(stderr, "ERROR: no such host: %s\n", argv[1]);
return 2;
}
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0) syserr("can't open socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
syserr("can't connect to server");
printf("Connection to %s:%s established. Now awaiting commands...\n\n\n", argv[1], argv[2]);
do{
printf("%s:%s> ", argv[1], argv[2]); //prompt user for command
fgets(buffer, 255, stdin);
n = strlen(buffer);
if(n>0 && buffer[n-1] == '\n')
buffer[n-1] = '\0';
if(strcmp(buffer, "ls-remote") == 0){ //display files from server directory
uint32_t size;
uint32_t commandSize = strlen(buffer);
//convert to network form
commandSize = htonl(commandSize);
n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
if(n < 0) syserr("can't send to server");
n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
if(n < 0) syserr("can't send to server");
n = recv(sockfd, &size, sizeof(size), 0); // recieve the size of the directory
if(n < 0) syserr("can't receive from server");
size = ntohl(size);
int currentSize = 0;
printf("Files at the server: %s\n", argv[1]);
while(currentSize < size){
memset(&buffer[0], 0, sizeof(buffer));
n = recv(sockfd, buffer, 255, 0); // recieve directory from server
if(n < 0) syserr("can't recieve server");
currentSize = currentSize + n;
printf("%s", buffer);
}
}
else if(strcmp(buffer, "ls-local") == 0){ //display files from local directory
printf("Files at the client: \n");
readDirectory();
}
else if(strncmp(buffer, "get ", 4) == 0){ //downlaod file from server
if(strlen(buffer) < 5){ // no file was entered
printf("%s\n", "ERROR...missing filename: get <filename>");
}
else{
uint32_t fileSize;
uint32_t commandSize = strlen(buffer);
//convert to network form
commandSize = htonl(commandSize);
n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
if(n < 0) syserr("can't send to server");
n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
if(n < 0) syserr("can't send to server");
n = recv(sockfd, &fileSize, sizeof(fileSize), 0); // get size of file
if(n < 0) syserr("can't receive from server");
fileSize = ntohl(fileSize);
if(fileSize == -1){
printf("%s\n", "File does not exist");
}
else{ // file exists
int totalBytes = 0;
int bytesWritten = 0;
// get file name
char *fileName = strtok(buffer, " ");
fileName = strtok(NULL, " ");
memcpy(temp, fileName, strlen(fileName)); //copy filename into temp
temp[strlen(fileName)] = '\0';
//create new file with given name
FILE *fpNew = fopen(fileName, "w");
if(fpNew){
while(totalBytes < fileSize){
//receieve the bytes
n = recv(sockfd, buffer, sizeof(buffer), 0);
if(n < 0) syserr("can't receive from server");
//write the bytes
int b = fwrite(buffer, 1, n, fpNew);
if (b < 0)
syserr("error writing file");
if(n == 0){ // error reading on server side
break;
}
totalBytes = n + totalBytes;
bytesWritten = b + bytesWritten;
}
fclose(fpNew);
if(bytesWritten == fileSize) // all bytes read/written to file successfully
printf("Retrieval of file %s: successful.\n", temp);
else
printf("Retrieval of file %s: unsuccessful.\n", temp);
}
else{
syserr("couldnt open file for writing.");
}
}
}
}
else if(strncmp(buffer, "put ", 4) == 0){ // upload file to server
if(strlen(buffer) < 5){
printf("%s\n", "ERROR...missing filename: get <filename>");
}
else{
uint32_t commandSize = strlen(buffer);
uint32_t status;
memcpy(temp, buffer, strlen(buffer)); //copy buffer into temp
temp[strlen(buffer)] = '\0';
// get name of file
char *fileName = strtok(temp, " ");
fileName = strtok(NULL, " ");
int bytes;
FILE *fp = fopen(fileName, "r"); // open the file
if(fp){ // file exists and opened
int totalBytes = 0;
//convert to network form
commandSize = htonl(commandSize);
n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
if(n < 0) syserr("can't send to server");
n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
if(n < 0) syserr("can't send to server");
// get file size
fseek(fp, 0L, SEEK_END);
int fileSize = ftell(fp);
// send the file size
uint32_t size = htonl(fileSize);
n = send(sockfd, &size, sizeof(size), 0);
if(n < 0) syserr("can't send to server");
//go back to beginning of file
fseek(fp, 0, SEEK_SET);
while(totalBytes < fileSize){ // while there are more bytes...
bytes = fread(buffer, 1, sizeof(buffer), fp); // read bytes fromt he file
if(bytes < 0){
syserr("Error reading the file.");
}
totalBytes = totalBytes + bytes;
//send the bytes
n = send(sockfd, buffer, bytes, 0);
if(n < 0) syserr("can't send to server");
if(bytes == 0){ //error reading
break;
}
}
fclose(fp);
//recieve the final status
n = recv(sockfd, &status, sizeof(status), 0);
if(n < 0) syserr("can't receive from server");
status = ntohl(status);
if(totalBytes == fileSize && status == 1){ // successful on both ends
printf("Upload of file %s: successful.\n", fileName);
}
else{
printf("Upload of file %s: unsuccessful.\n", fileName);
}
}
else{
printf("%s\n", "File does not exist");
}
}
}else if(strcmp(buffer, "exit") == 0){
uint32_t commandSize = strlen(buffer);
//convert to network form
commandSize = htonl(commandSize);
n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
if(n < 0) syserr("can't send to server");
n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
if(n < 0) syserr("can't send to server");
}
else{
if(strcmp(buffer, "exit") != 0)
printf("Error...invalid command.\nValid commands: ls-remote, ls-local, get <filename>, put <filename>, exit\n");
}
}while (strcmp(buffer, "exit") != 0);
printf("Connection to server %s:%s terminated, BYE now!\n", argv[1], argv[2]);
close(sockfd);
return 0;
}
void readDirectory(){
DIR *d = opendir(".");
struct dirent *dir;
if (d)
{
while((dir = readdir(d))!= NULL)
{
printf("%s\n", dir->d_name);
}
closedir(d);
}
else{
syserr("Error...could not get files from directory.");
}
}
my server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>
void syserr(char *msg){
perror(msg); exit(-1);
}
void errorHandling(char *msg, int newsockfd){
close(newsockfd);
perror(msg); exit(-1);
}
uint32_t directorySize();
void handle_client(int newsockfd);
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
struct sockaddr_in serv_addr, clt_addr;
socklen_t addrlen;
if(argc < 1) {
fprintf(stderr,"Usage: %s <port>\n", argv[0]);
return 1;
}
if(argc == 1){
argv[1] = "5555";
}
portno = atoi(argv[1]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) syserr("can't open socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
syserr("can't bind");
printf("bind socket to port %d...\n", portno);
listen(sockfd, 5);
for(;;){
printf("wait on port %d...\n", portno);
addrlen = sizeof(clt_addr);
newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
if(newsockfd < 0) syserr("can't accept");
pid_t pid = fork();
if(pid < 0){
syserr("Error can't fork");
}
else if(pid == 0){ // child process
close(sockfd);
handle_client(newsockfd);
close(newsockfd);
break;
}
else{ // parent
close(newsockfd);
continue;
}
}
return 0;
}
uint32_t directorySize(int newsockfd){
DIR *d = opendir(".");
struct dirent *dir;
uint32_t count = 0;
if (d)
{
while((dir = readdir(d))!= NULL)
{
//count++;
count = strlen(dir->d_name) + count + 1;
}
closedir(d);
}
else{
errorHandling("Error...could not get files from directory.", newsockfd);
}
return count;
}
void handle_client(int newsockfd){
// receieve command size from client
uint32_t commandSize;
int n;
char buffer[256];
while(1){
n = recv(newsockfd, &commandSize, sizeof(commandSize), 0); // receive size of command
if(n < 0) errorHandling("can't receive from client", newsockfd);
commandSize = ntohl(commandSize);
//recieve command
n = recv(newsockfd, buffer, commandSize, 0);
if(n < 0) errorHandling("can't receive from client", newsockfd);
else buffer[n] = '\0';
if(strcmp(buffer, "ls-remote") == 0){ //display files from server directory
// get the size of the directory
uint32_t size = htonl(directorySize(newsockfd));
n = send(newsockfd, &size, sizeof(size), 0); // send size of directory
if(n < 0) errorHandling("can't send to client", newsockfd);
DIR *d = opendir(".");
struct dirent *dir;
if(d){
while((dir = readdir(d))!= NULL){
memset(&buffer[0], 0, sizeof(buffer));
strcpy(buffer, dir->d_name);
buffer[strlen(buffer)] = '\n';
// send file/folder names
n = send(newsockfd, buffer, strlen(buffer), 0);
if(n < 0)
errorHandling("can't receive from client", newsockfd);
}
closedir(d);
}
else{
errorHandling("Error...could not get files from directory.", newsockfd);
}
}
else if (strncmp(buffer, "get", 3) == 0){ // if command is get
char *fileName = strtok(buffer, " "); // "get"
fileName = strtok(NULL, " "); // the name of the file
FILE *fp = fopen(fileName, "r");
if(fp){ // if file exists
int totalBytes = 0;
int bytes;
// get the size of the file
fseek(fp, 0L, SEEK_END);
int fileSize = ftell(fp);
// send the file size
uint32_t size = htonl(fileSize);
n = send(newsockfd, &size, sizeof(size), 0);
if(n < 0) errorHandling("can't send to client", newsockfd);
//go back to beginning of file
fseek(fp, 0, SEEK_SET);
while(totalBytes < fileSize){ // while there are more bytes to read...
// read the bytes into the buffer
bytes = fread(buffer, 1, sizeof(buffer), fp);
if(bytes < 0){
errorHandling("Eorror reading bytes on server side", newsockfd);
}
//send the bytes
n = send(newsockfd, buffer, bytes, 0);
if(n < 0) errorHandling("can't send to client", newsockfd);
if(bytes == 0) // error reading file; bytes should have been > 0
break;
totalBytes = totalBytes + bytes;
}
fclose(fp);
}
else{
// tell client file doesnt exist by sending -1
uint32_t dne = htonl(-1);
n = send(newsockfd, &dne, sizeof(dne), 0);
if(n < 0) errorHandling("can't send to client", newsockfd);
}
}
else if (strncmp(buffer, "put", 3) == 0){ // upload a file
int totalBytes = 0;
int bytesWritten = 0;
int b = 0;
uint32_t fileSize, status;
n = recv(newsockfd, &fileSize, sizeof(fileSize), 0);// receive the size of file
if(n < 0) errorHandling("can't receive from client", newsockfd);
fileSize = ntohl(fileSize);
// get file name
char *fileName = strtok(buffer, " ");
fileName = strtok(NULL, " ");
//create new file with given name
FILE *fpNew = fopen(fileName, "w");
if(fpNew){
while(totalBytes < fileSize){
n = recv(newsockfd, buffer, sizeof(buffer), 0);
if(n < 0) errorHandling("can't receive from client", newsockfd);
if(n == 0){ //bad file transfer on client side
break;
}
//write the bytes
b = fwrite(buffer, 1, n, fpNew);
if(b < n){ // error writing to file
break;
}
totalBytes = totalBytes + n; // bytes recived
bytesWritten = bytesWritten + b; //bytes written
}
fclose(fpNew);
if(bytesWritten != fileSize){ // not all bytes written
status = htonl(-1);
}
else{
status = htonl(1);
}
// send the status
n = send(newsockfd, &status, sizeof(status), 0);
if(n < 0) errorHandling("can't send client", newsockfd);
}
else{
errorHandling("could not open file for writing.", newsockfd);
}
}
else{ // command is exit
printf("%s\n", "closing connection");
close(newsockfd); // close the connection
break;
}
}
}
n = recv(sockfd, &size, sizeof(size), 0); // recieve the size of the directory
if(n < 0) syserr("can't receive from server");
That's not sufficient. If n == 0 the peer has closed the connection: you must do likewise and exit the read loop, or indeed possibly the entire process in your case.
I want to send file through socket and after that i want to send three messages. This code below works, but i have to send first sentence two times. My question is why? Without one sentence1, receiver display sentence2, sentence3 and sentence3. What is wrong with this code?
I'm using tcp protocol.
Sender.c
char file_size[256];
struct stat file_stat;
int sent_bytes = 0;
int fd;
int offset;
int remain_data;
fd = open(FILE_TO_SEND, O_RDONLY);
if (fd == -1)
{
fprintf(stderr, "Error opening file --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Get file stats */
if (fstat(fd, &file_stat) < 0)
{
fprintf(stderr, "Error fstat --> %s", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);
sprintf(file_size, "%d", file_stat.st_size);
write(sck, file_size, 256);
char buffer[1024] = "";
while (1) {
int bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read == 0)
break;
void *p = buffer;
while (bytes_read > 0) {
int bytes_written = write(sck, p, bytes_read);
if (bytes_written <= 0) {
// handle errors
}
bytes_read -= bytes_written;
p += bytes_written;
}
}
char sentence[1024];
write(sck, sentence1, 1024);
write(sck, sentence1, 1024);
write(sck, sentence2, 1024);
write(sck, sentence3, 1024);
Receiver.c
char buffer[1024] = "";
int sck = *((int*) arg);
int file_size;
read(sck, buffer, 256);
file_size = atoi(buffer);
ssize_t len;
FILE *received_file;
int remain_data = 0;
received_file = fopen("plik.pdf", "w");
if (received_file == NULL)
{
fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
remain_data = file_size;
while (((len = recv(sck, buffer, 1024, 0)) > 0) && (remain_data > 0))
{
fwrite(buffer, sizeof(char), len, received_file);
remain_data -= len;
}
fclose(received_file);
read (sck, buffer, 1024);
printf("%s 1\n", buffer);
read (sck, buffer, 1024);
printf("%s 2\n", buffer);
read (sck, buffer, 1024);
printf("%s 3\n", buffer);
Nothing to do with TCP protocol. Your logic is flawed. You are receiving the data and then checking for remain_data. So your sentence1 is received and discarded in the while loop.
Move your recv() into the body of while to fix this or change the order in while.
while ((remain_data > 0) && ((len = recv(sck, buffer, 1024, 0)) > 0))
In the modified while, you call recv() only if remain_data > 0. recv() is not called if remain_data == 0 (lazy evaluation). So your while loop ends immediately after receiving the file and ready to receive your sentences. In your code, the while loop read the first sentence, then checked remain_data > 0 effectively discarding sentence1