I am reading a socket client example from APUE at https://github.com/hayatoito/apue-2e/blob/master/sockets/ruptime.c. I don't find it close or shutdown its socket. Is it true in general that a client doesn't have to close its socket file descriptor? When does a client need to close its socket file descriptor, and when not?
For comparison, its server closes the socket at the end: https://github.com/hayatoito/apue-2e/blob/master/sockets/ruptimed.c
Client:
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#define MAXADDRLEN 256
#define BUFLEN 128
extern int connect_retry(int, const struct sockaddr *, socklen_t);
void
print_uptime(int sockfd)
{
int n;
char buf[BUFLEN];
while ((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
write(STDOUT_FILENO, buf, n);
if (n < 0)
err_sys("recv error");
}
int
main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err;
if (argc != 2)
err_quit("usage: ruptime hostname");
hint.ai_flags = 0;
hint.ai_family = 0;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
err_quit("getaddrinfo error: %s", gai_strerror(err));
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
if ((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0)
err = errno;
if (connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen) < 0) {
err = errno;
} else {
print_uptime(sockfd);
exit(0);
}
}
fprintf(stderr, "can't connect to %s: %s\n", argv[1], strerror(err));
exit(1);
}
Server:
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#define BUFLEN 128
#define QLEN 10
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
extern int initserver(int, struct sockaddr *, socklen_t, int);
void
serve(int sockfd)
{
int clfd;
FILE *fp;
char buf[BUFLEN];
for (;;) {
clfd = accept(sockfd, NULL, NULL);
if (clfd < 0) {
syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));
exit(1);
}
if ((fp = popen("/usr/bin/uptime", "r")) == NULL) {
sprintf(buf, "error: %s\n", strerror(errno));
send(clfd, buf, strlen(buf), 0);
} else {
while (fgets(buf, BUFLEN, fp) != NULL)
send(clfd, buf, strlen(buf), 0);
pclose(fp);
}
close(clfd);
}
}
int
main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err, n;
char *host;
if (argc != 1)
err_quit("usage: ruptimed");
#ifdef _SC_HOST_NAME_MAX
n = sysconf(_SC_HOST_NAME_MAX);
if (n < 0) /* best guess */
#endif
n = HOST_NAME_MAX;
host = malloc(n);
if (host == NULL)
err_sys("malloc error");
if (gethostname(host, n) < 0)
err_sys("gethostname error");
daemonize("ruptimed");
hint.ai_flags = AI_CANONNAME;
hint.ai_family = 0;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {
syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",
gai_strerror(err));
exit(1);
}
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr,
aip->ai_addrlen, QLEN)) >= 0) {
serve(sockfd);
exit(0);
}
}
exit(1);
}
Is it true in general that a client doesn't have to close its socket file descriptor?
No, it is not true.
A variant of that belief resulted in a number of keepalive issues in early Microsoft Internet Explorer browsers (versions 1 through 5), that had to be worked around on the server end. (Essentially, the OS did not ensure a proper, full TCP connection termination.)
However, if the process is about to exit, it is not a bug to not close all sockets, because POSIX.1 (the standard that defines this functionality and the C interface used here) says explicitly (in e.g. exit()) that all open streams are closed when the process exits. In theory, it is a similar situation as dynamic memory allocation: it is not necessary for the process to free() all dynamically allocated memory when it exits, because all (non-shared) dynamically allocated memory is automatically freed when the process exits.
In practice, it is much more robust to explicitly close all socket descriptors. This is especially true for TCP connections, because the connection termination involves a FIN and an ACK packet exchange. While one could trust the OS to always get it right, the MSIE example shows reality is much less trustworthy, and being thorough makes for a better user experience.
When does a client need to close its socket file descriptor, and when not?
There are two cases in practice:
When the connection is terminated.
Descriptors are a finite resource, and closing them as soon as they are no longer needed ensures resources are not wasted. There really is no good reason for keeping a socket connection open for any longer than necessary. Certain things, like traversing a filesystem hierarchy using nftw(), are much more efficient when they can use a potentially large number of descriptors, so taking care a process does not run out of them due to programmer laziness is a good idea.
When creating a child process via fork(), that should not have access to that socket connection.
Current Linux, MacOS, FreeBSD, and OpenBSD at least support a close-on-exec flag (often via fcntl(sfd, F_SETFD, FD_CLOEXEC)). In Linux, you can create close-on-exec socket descriptors using socket(domain, type | SOCK_CLOEXEC, protocol) and socket pairs using socketpair(domain, type | SOCK_CLOEXEC, protocol, sfd).
Close-on-exec descriptors are closed when an exec call is successful, replacing that process with whatever else is being executed. Thus, if the fork is followed by an exec or _Exit, and all socket descriptors are close-on-exec, the duplicate sockets are closed "automatically", and you don't need to worry about it.
Note that if your code uses popen(), you better have your socket descriptors close-on-exec, or the command you run may have access to the connections. Pity it is completely nonstandard at this point in time (early 2019).
Do also note that if the child process does not execute another binary, but for example drops privileges (rare for a client), close-on-exec won't do anything. So, closing (in the child process) unneeded duplicates of the socket descriptors, explicitly "by hand", is still important for proper privilege separation. But that is rarely an issue with client applications, more with services and such.
In other words, whenever you wish to terminate the socket connection, or when you have an extraneous duplicate of the socket connection, you close() them.
Allways have to close the socket, anyway it is leaked resource.
There is lot of way to close it, but before the close, be sure the socket data is empty. So you have to call the shutdown, anf after close it.
Related
This question already has an answer here:
Basic non blocking tcp connect example for C [closed]
(1 answer)
Closed 12 months ago.
I am trying to write a C code that connects using non-blocking TCP socket along with select(). When I read the man page about EINPROGRESS, I feel a little bit confused.
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to
select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates
writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine
whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one
of the usual error codes listed here, explaining the reason for the failure).
Is there any sample code I can refer to? Although it is a pretty old question, I don't see anyone post a complete working code. Some suggest to use connect twice but I don't know exactly how.
Sure, below is a little C program that uses a non-blocking TCP connect to connect to www.google.com's port 80, send it a nonsense string, and print out the response it gets back:
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
static void SendNonsenseCommand(int sock)
{
const char sendString[] = "Hello Google! How are you!\r\n\r\n";
if (send(sock, sendString, sizeof(sendString), 0) != sizeof(sendString)) perror("send()");
}
int main(int argc, char ** argv)
{
// Create a TCP socket
const int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {perror("socket"); return 10;}
// Set the TCP socket to non-blocking mode
const int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {perror("fcntl(F_GETFL)"); return 10;}
if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0) {perror("fcntl(F_SETFL)"); return 10;}
// Get the IP address of www.google.com
struct hostent * he = gethostbyname("www.google.com");
if (he == NULL) {printf("Couldn't get a hostent for www.google.com\n"); return 10;}
// Start a non-blocking/asynchronous TCP connetion to port 80
struct sockaddr_in saAddr;
memset(&saAddr, 0, sizeof(saAddr));
saAddr.sin_family = AF_INET;
saAddr.sin_addr = *(struct in_addr*)he->h_addr;
saAddr.sin_port = htons(80);
const int connectResult = connect(sock, (const struct sockaddr *) &saAddr, sizeof(saAddr));
int isTCPConnectInProgress = ((connectResult == -1)&&(errno == EINPROGRESS));
if ((connectResult == 0)||(isTCPConnectInProgress))
{
if (isTCPConnectInProgress == 0) SendNonsenseCommand(sock);
// TCP connection is happening in the background; our event-loop calls select() to block until it is ready
while(1)
{
fd_set socketsToWatchForReadReady, socketsToWatchForWriteReady;
FD_ZERO(&socketsToWatchForReadReady);
FD_ZERO(&socketsToWatchForWriteReady);
// While connecting, we'll watch the socket for ready-for-write as that will tell us when the
// TCP connection process has completed. After it's connected, we'll watch it for ready-for-read
// to see what Google's web server has to say to us.
if (isTCPConnectInProgress) FD_SET(sock, &socketsToWatchForWriteReady);
else FD_SET(sock, &socketsToWatchForReadReady);
int maxFD = sock; // if we were watching multiple sockets, we'd compute this to be the max value of all of them
const int selectResult = select(maxFD+1, &socketsToWatchForReadReady, &socketsToWatchForWriteReady, NULL, NULL);
if (selectResult >= 0)
{
if ((FD_ISSET(sock, &socketsToWatchForWriteReady))&&(isTCPConnectInProgress))
{
printf("Socket is ready for write! Let's find out if the connection succeeded or not...\n");
struct sockaddr_in junk;
socklen_t length = sizeof(junk);
memset(&junk, 0, sizeof(junk));
if (getpeername(sock, (struct sockaddr *)&junk, &length) == 0)
{
printf("TCP Connection succeeded, socket is ready for use!\n");
isTCPConnectInProgress = 0;
SendNonsenseCommand(sock);
}
else
{
printf("TCP Connection failed!\n");
break;
}
}
if (FD_ISSET(sock, &socketsToWatchForReadReady))
{
char buf[512];
const int numBytesReceived = recv(sock, buf, sizeof(buf)-1, 0);
if (numBytesReceived > 0)
{
buf[numBytesReceived] = '\0'; // ensure NUL-termination before we call printf()
printf("recv() returned %i: [%s]\n", numBytesReceived, buf);
}
else if (numBytesReceived == 0)
{
printf("TCP Connection severed!\n");
break;
}
else perror("recv()");
}
}
else {perror("select()"); return 10;}
}
}
else perror("connect()");
close(sock); // just to be tidy
return 0;
}
I am writing a server that should wait for client to connect. After accessing the main loop server should "bounce" so long accept() does return a different value then -1. The problem is that the accept() method is blocking the execution instead of returning any value. What could be a reason for this if there is no invalid argument flag raised?
Bellow is a minimum reproducible example of my code:
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/unistd.h> // unlink
#define MAX_LISTCLI 5
int main(void) {
uint server_id, len;
struct sockaddr_un server;
if ((server_id = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("Socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, "free_socket");
if (unlink(server.sun_path) != -1)
fprintf(stderr, "Server was NOT aboard correctly?\n");
len = (strlen(server.sun_path) + sizeof(server.sun_family));
if (bind(server_id, (struct sockaddr *)&server, len) == -1) {
perror("Bind");
exit(1);
}
if (listen(server_id, MAX_LISTCLI) == -1) {
perror("Listen");
exit(1);
}
_Bool done = 0;
while (!done) {
uint remote_id;
struct sockaddr_un remote;
len = sizeof(remote);
// Bounce if connection was not obtained and wait for the client to connect
printf("Connecting..\n");
if ((remote_id =
accept(server_id,
(struct sockaddr *)&remote ,&len)) == -1) {
fprintf(stderr, "Bounce..\n");
continue;
}
fprintf(stderr, "Connected.\n");
// Replay to the user..
done = 1;
}
close(remote_id);
close(server_id);
unlink(server.sun_path);
return 0;
}
The problem here is with the socket being server_id being blocking (refer here).
If no pending connections are present on the queue, and the
socket is not marked as nonblocking, accept() blocks the caller
until a connection is present. If the socket is marked
nonblocking and no pending connections are present on the queue,
accept() fails with the error EAGAIN or EWOULDBLOCK.
If you want the accept call to return immediately you would have to make the socket non blocking.
EDIT: I wouldn't recommend making it a non-blocking call as that would simply waste CPU cycles due to repeated execution of the while loop. The ideal way of dealing with this would block on the accept call and then use fork system call to spawn a new process.
As mentioned in other post, you need to make your server socket nonblocking. You can do that by using fcntl.
fcntl(server_id, F_SETFL, O_NONBLOCK);
Then all of the calls that would normally block your socket will return a flag.
I hope I can explain. I am new in c programming and trying to send and receive binary file using TCPIP. Server should receive multiple files at a time when client send it. I creaded a bat file for send to server. There is no problem if file is 2 or 3 but while trying to send about 5 file sometimes show error. Actually file is not receiving properly. I used
multithreading Synchronization of Semaphore method
The result of receiving side(server) is as follows while printing:
file name (5000.dat)
Invalid argumen(5000.dat)
completetfile name (5120.dat)
(5120.dat) complete
file name (8192.dat)
(8192.dat) complete
file name (10240.dat)
(10240.dat) complete
Some text is misplassed above and each time shows different result. Sometimes reveive and write file properly and sometimes some files can't read.
My code of receving side is as follows:
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <process.h>
#include <windows.h>
void fileReceive(void *param);
HANDLE semaphore;
HANDLE threadHandle;
int main(int argc, char *argv[]){
if (argc > 1) {
goto l_param_error;
}
WSADATA wsaData; // Contains information about the Windows Sockets implementation
SOCKET sock0; // creates a socket that is bound to a specific transport service provider.
struct sockaddr_in addr;
struct sockaddr_in client;
int len;
SOCKET sock; // creates a socket that is bound to a specific transport service provider
// Initiates use of the Winsock DLL by a process.
int error = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (error != 0) {
goto l_WSAIni_error;
}
addr.sin_family = AF_INET;// internetwork: UDP, TCP, etc.
addr.sin_port = htons(8080);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
sock0 = socket(AF_INET, SOCK_STREAM, 0);
if (sock0 == INVALID_SOCKET) {
goto l_socket_error;
}
// associates a local address with a socket
if (bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
goto l_bind_error;
}
while (1) {
// places a socket in a state in which it is listening for an incoming connection
if (listen(sock0, 1) != 0) {
goto l_socket_conn_setup_error;
}
len = sizeof(client);
// The accept function permits an incoming connection attempt on a socket.
sock = accept(sock0, (struct sockaddr *)&client, &len);
if (sock == INVALID_SOCKET) {
goto l_error_accpet;
}
semaphore = CreateSemaphore(0, 1, 1, 0);
threadHandle = (HANDLE)_beginthread(&fileReceive, 0, &sock);
if (threadHandle == 0) {
printf("Thread handle error");
return 1;
}
CloseHandle(semaphore);
}
WSACleanup();
return 0;
}
void fileReceive(void *param) {
int n = 0;
int sock = *((int *)param);
unsigned char buf[1];
unsigned char buff[256] = { 0 };
FILE *fp = NULL;
memset(buff, 0, sizeof(buff));
WaitForSingleObject(semaphore, INFINITE);
// Receive file name
int recvFile = recv(sock, buff, 255, 0);
ReleaseSemaphore(semaphore, 1, 0);
if ((recvFile == 0) || (recvFile == -1)) {
goto l_recv_error;
}
fp = fopen(buff, "wb+");
if (fp != NULL) {
printf("file name (%s)\n", buff);
while (n = recv(sock, &buf[0], 1, 0) > 0) {
size_t written = fwrite(&buf, sizeof(buf), 1, fp);
if (written != 1) {
goto l_write_error;
}
}
printf("(%s) complete\n", buff);
}
else {
goto l_fp_error;
}
fclose(fp);
closesocket(sock);
_endthread();
CloseHandle(threadHandle);
}
Unfortunately, you have a long list of problems. Bluntly, it seems that you don't understand TCP (it's a byte stream protocol) and it seems that you don't understand what problems thread synchronization solves and how to use it. Given this, you are attempting a task that's way beyond your capabilities and should attempt much simpler tasks first. Start with TCP code that doesn't use threads or threading code that doesn't use TCP so you don't have to get everything right at once.
Here are some of the problems:
You pass &sock to the thread. But then change the value of sock, possibly before the thread can read it.
You call recv on the TCP connection to get the filename and just assume you'll read all, and only, the filename. TCP has no way to "glue" bytes together into a message. If you want to send and receive messages, you must define a message protocol and implement it on top of TCP.
Your semaphore doesn't actually do anything. You don't use it to communicate or synchronize anything.
You write 256 bytes every time you read 1.
Is it possible that a TCP server program can listen on two different socket interface?
Problem Statement:
I've a problem statement where the TCP server will be having two interfaces:
Interface I: For accepting generic data from TCP client (IP address 192.168.5.10:2000)
Interface II: Management Interface for the server (IP address 192.168.5.11:2000)
Interface I: This interface shall receive data from TCP client, processes them & send it back to client.
Interface II: This interface shall receive commands (meant for Servers management purpose). This commands most probably would be sent through telnet.
Current Status:
I already have a thread based TCP server program where I've "Interface I" up & running(I'm able to receive data from multiple clients, process them & send it back)
Can anyone give me some pointers/prototype example on how to add "Interface II" to my TCP server program?
NOTE: TCP server program is written in C programming language for Linux OS
UPDATE
Below is the code fragment I've written so far for listening on one socket. I tried modifying it for listening over two sockets as you've directed but I'm facing trouble while trying to spawn a different thread for the other socket interface. Will it possible for you to modify this to listen on two sockets? It would be really helpful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
void *data_processing_thread(void *arg);
int main(int argc, char **argv)
{
int fdmax, listener, newfd, res;
int optval=1;
socklen_t addrlen;
int server_port = 4000;
/* master, temp file descriptor list */
fd_set *master, *read_fds;
/* client, server address */
struct sockaddr_in server_addr, client_addr;
pthread_t thread;
master = malloc(sizeof(fd_set));
read_fds = malloc(sizeof(fd_set));
FD_ZERO(master);
FD_ZERO(read_fds);
/* create endpoint for communication */
if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("failed to create listener\n");
return -1;
}
/* check if address is already in use? */
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(int)) == -1) {
perror("socket address already in use!\n");
return -1;
}
/* bind */
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(server_port);
memset(&(server_addr.sin_zero), '\0', 8);
if (bind(listener, (struct sockaddr*)&server_addr,
sizeof(server_addr)) == -1) {
perror("failed to do the bind\n");
return -1;
}
/* listen for connect on sockets */
if (listen(listener, 10) == -1) {
perror("failed to listen on socket\n");
return -1;
}
/* add the listener to the master set */
FD_SET(listener, master);
/* keep track of biggest file descriptor */
fdmax = listener;
while (1) {
read_fds = master;
/* wait till socket descriptor is ready for the operation */
if (select(fdmax+1, read_fds, NULL, NULL, NULL) == -1) {
perror("failed to do select() on socket\n");
return -1;
}
/* Run through existing data connections looking for data to be
* read */
int cnt;
int *accept_fd = 0;
for (cnt=0; cnt<=fdmax; cnt++) {
if (cnt == listener) {
if (FD_ISSET(cnt, read_fds)) {
addrlen = sizeof(client_addr);
if ((newfd = accept(listener, (struct sockaddr*)&client_addr, &addrlen)) == -1) {
perror("failed to accept incoming connection\n");
} else {
fprintf(stdout, "Server: Connection from client [%s] on socket [%d]\n",
inet_ntoa(client_addr.sin_addr), newfd);
accept_fd = malloc(sizeof(int));
*accept_fd = newfd;
if ((res = pthread_create(&thread, NULL, data_processing_thread, (void*)accept_fd)) != 0) {
perror("Thread creation failed\n");
free(accept_fd);
}
}
}
continue;
}
}
}
return 1;
}
void *data_processing_thread(void *arg)
{
int nbytes;
int *recv_fd = (int*)arg;
char *buffer = malloc(sizeof(char)*256);
while(1) {
fprintf(stdout, "Server: Waiting for data from socket fd %d\n", *recv_fd);
/* receive incoming data from comm client */
if ((nbytes = recv(*recv_fd, buffer, sizeof(buffer), 0)) <= 0) {
if (nbytes != 0) {
perror("failed to receive\n");
}
break;
} else {
fprintf(stdout, "Data received: %s\n", buffer);
}
}
close(*recv_fd);
free(recv_fd);
pthread_exit(0);
}
Create two listening sockets using socket().
Bind both to respective address/port using bind().
Make both listen using listen().
Add both listening sockets to a properly initialised fd_set typed variable using FD_SET().
Pass the fd_set to a call to select()
Upon select()'s return check the reason and perform the appropriate action, typically
either calling accept() on one of the both listening sockets and add the accepted socket (as returned by accept()) to the fd_set,
or if it's an accepted socket that had triggered select() to return, then call read(), write() or close() on it. If close()ing the socket also remove it from the fd_set using FD_CLR().
Start over with step 5.
Important note: The steps above are a rough scheme, not mentioning all possible all traps, so it is absolutly necessary to also read the related man-pages for each step carefully, to understand what is happening.
you can bind 0.0.0.0 which means binding all interfaces.
you can't bind two interfaces using only one socket.
you should create a new socket, and bind ti to interface II.
I am trying to make a file transfer between server and client, but is working very badly. Basically what needs to happen is:
1) The client send a txt file to the server (I called it "quotidiani.txt")
2) The server saves it in another txt file ("receive.txt")
3) The server runs a script on it that modifies it and saves it with another name ("output.txt")
4) The server send the file back to the client that saves it (on the same socket) with the name (final.txt)
The problem is that the first file (quotidiani.txt) is read just for a little part, and then there are some errors. I'd like someone to help me understand and correct my errors.
Here's my code:
client.c:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char *argv[])
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr);
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/aryan/Desktop/quotidiani.txt";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server... ", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//}
/* Receive File from Server */
printf("[Client] Receiveing file from Server and saving it as final.txt...");
char* fr_name = "/home/aryan/Desktop/progetto/final.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(sockfd, revbuf, LENGTH, 0)) > 0)
{
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed.\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512)
{
break;
}
}
if(fr_block_sz < 0)
{
if (errno == EAGAIN)
{
printf("recv() timed out.\n");
}
else
{
fprintf(stderr, "recv() failed due to errno = %d\n", errno);
}
}
printf("Ok received from server!\n");
fclose(fr);
}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
server.c
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define BACKLOG 5
#define LENGTH 512
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main ()
{
/* Defining Variables */
int sockfd;
int nsockfd;
int num;
int sin_size;
struct sockaddr_in addr_local; /* client addr */
struct sockaddr_in addr_remote; /* server addr */
char revbuf[LENGTH]; // Receiver buffer
/* Get the Socket file descriptor */
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor. (errno = %d)\n", errno);
exit(1);
}
else
printf("[Server] Obtaining socket descriptor successfully.\n");
/* Fill the client socket address struct */
addr_local.sin_family = AF_INET; // Protocol Family
addr_local.sin_port = htons(PORT); // Port number
addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address
bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct
/* Bind a special Port */
if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 )
{
fprintf(stderr, "ERROR: Failed to bind Port. (errno = %d)\n", errno);
exit(1);
}
else
printf("[Server] Binded tcp port %d in addr 127.0.0.1 sucessfully.\n",PORT);
/* Listen remote connect/calling */
if(listen(sockfd,BACKLOG) == -1)
{
fprintf(stderr, "ERROR: Failed to listen Port. (errno = %d)\n", errno);
exit(1);
}
else
printf ("[Server] Listening the port %d successfully.\n", PORT);
int success = 0;
while(success == 0)
{
sin_size = sizeof(struct sockaddr_in);
/* Wait a connection, and obtain a new socket file despriptor for single connection */
if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1)
{
fprintf(stderr, "ERROR: Obtaining new Socket Despcritor. (errno = %d)\n", errno);
exit(1);
}
else
printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr));
/*Receive File from Client */
char* fr_name = "/home/aryan/Desktop/receive.txt";
FILE *fr = fopen(fr_name, "a");
if(fr == NULL)
printf("File %s Cannot be opened file on server.\n", fr_name);
else
{
bzero(revbuf, LENGTH);
int fr_block_sz = 0;
while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) > 0)
{
int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
if(write_sz < fr_block_sz)
{
error("File write failed on server.\n");
}
bzero(revbuf, LENGTH);
if (fr_block_sz == 0 || fr_block_sz != 512)
{
break;
}
}
if(fr_block_sz < 0)
{
if (errno == EAGAIN)
{
printf("recv() timed out.\n");
}
else
{
fprintf(stderr, "recv() failed due to errno = %d\n", errno);
exit(1);
}
}
printf("Ok received from client!\n");
fclose(fr);
}
/* Call the Script */
system("cd ; chmod +x script.sh ; ./script.sh");
/* Send File to Client */
//if(!fork())
//{
char* fs_name = "/home/aryan/Desktop/output.txt";
char sdbuf[LENGTH]; // Send buffer
printf("[Server] Sending %s to the Client...", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
fprintf(stderr, "ERROR: File %s not found on server. (errno = %d)\n", fs_name, errno);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs))>0)
{
if(send(nsockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
exit(1);
}
bzero(sdbuf, LENGTH);
}
printf("Ok sent to client!\n");
success = 1;
close(nsockfd);
printf("[Server] Connection with Client closed. Server will wait now...\n");
while(waitpid(-1, NULL, WNOHANG) > 0);
//}
}
}
Some comments in no particular order:
You're passing up the opportunity to know exact errors too often:
if(listen(sockfd,BACKLOG) == -1)
{
printf("ERROR: Failed to listen Port %d.\n", PORT);
return (0);
}
This block should definitely include a perror("listen") or something similar. Always include perror() or strerror() in every error handling block when the error details will be reported via errno. Having exact failure reasons will save you hours when programming and will save you and your users hours when things don't work as expected in the future.
Your error handling needs some further standardizing:
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
printf("ERROR: Failed to obtain Socket Descriptor.\n");
return (0);
}
This should not return 0 because that will signal to the shell that the program ran to completion without error. You should return 1 (or use EXIT_SUCCESS and EXIT_FAILURE) to signal an abnormal exit.
else
printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr));
/*Receive File from Client */
In this preceding block you've gotten an error condition but continue executing anyway. That's a quick way to get very undesirable behavior. This should either re-start the main server loop or exit the child process or something similar. (Depends if you keep the multi-process server.)
if(!fork())
{
The preceding block forgot to account for fork() failing. fork() can, and does fail -- especially in shared hosting environments common at universities -- so you should be prepared for the full, complicated three possible return values from fork(): failure, child, parent.
It appears you're using fork() indiscriminately; your client and server are both very simple and the way they are designed to run means they cannot be used to service multiple clients simultaneously. You should probably stick to exactly one process for each, at least until the algorithm is perfectly debugged and you figure out some way to run multiple clients simultaneously. I expect this is the source of the problem you're encountering now.
You need to use functions to encapsulate details; write a function to connect to the server, a function to send the file, a function to write the file, etc. Write a function to handle the complicated partial writes. (I especially recommend stealing the writen function from the Advanced Programming in the Unix Environment book's source code. File lib/writen.c.) If you write the functions correctly you can re-use them in both the client and server. (Something like placing them in utils.c and compiling the programs like gcc -o server server.c utils.c.)
Having smaller functions that each do one thing will allow you to focus on smaller amounts of code at a time and write little tests for each that will help you narrow down which sections of code still need improvement.
One discussion point seems was missing here, So I thought to mention it here.
Let us very quicky understand TCP's data transfer. There are three steps
a)Connection Establishment, b)Data Transfer, c)Connection Termination
Now here a client is sending a file to a server, over a TCP socket.
The server does some processing on the file and sends it back to the client.
Now all the 3 steps need to done. Connection Establishment is done by calling connect.
Data reading/writing is done by recv/send here and connection termination is done by close.
The server here is reading data in a loop using recv.
Now when the loop will come to an end? When the recv returns 0 or may be less than 0 on error.
When recv returns 0? -> When the other side has closed the connection.
(When the TCP FIN segement has been recived by this side).
So in this code when the client has finished sending the file,I have used a shutdown function, which sends the FIN segement from the client side and the server's recv can now return 0 and the program continues.(Half way close, since the client also needs to read data subsequently).
(Just for understanding please note TCP's connection Establisment is a 3 way Handshake and
connection termination is a 4 way handshake.)
Similarly if you forget closing the connection socket on the server side, the client's recv will also block for ever.
I think that was the reason for using ctrl c to stop the client sometimes which you have mentioned.
You may pl. refer any standard Networking book or the rfc http://www.ietf.org/rfc/rfc793.txt for learning more about TCP
I have pasted the modified code and also little added some comments,
Hope this explanation will help.
Modified client code:
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
exit(1);
}
bzero(sdbuf, LENGTH);
}
/*Now we have sent the File's data, what about server's recv?
Recv is blocked and waiting for data to arrive or if the protocol
stack receives a TCP FIN segment ..then the recv will return 0 and
the server code can continue */
/*Sending the TCP FIN segment by shutdown and this is half way
close, since the client also needs to read data subsequently*/
shutdown(sockfd, SHUT_WR);
printf("Ok File %s from Client was Sent!\n", fs_name);