The sample codes here demonstrate the following techniques:
- Concurrent FTP Server
- The use of standard I/O with POSIX sockets
- The handling of Unix signal SIGCHLD to prevent terminated children from becoming zombies
- The handling of system calls interrupted by caught signals
Codes
Code for the Server:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
/*----------------------------------------------------------------*/
/* Prevent Terminated Children from Becoming Zombies */
/*----------------------------------------------------------------*/
/* The method here is rather portable for systems that conform to POSIX.1-1990.
* In POSIX.1-2001, if
* - the disposition of SIGCHLD is set to SIG_IGN; or
* - the SA_NOCLDWAIT flag is set for SIGCHLD,
* then children that terminate do not become zombies. A call to wait()
* or waitpid() will block until all children have terminated, and then
* fail with errno set to ECHILD. Linux 2.6 conforms to this behavior, but
* Linux 2.4 does not.
*/
void handler_sigchld(int signum)
{
int status;
// Wait for all terminated children
// Loop is required as Unix signals are not queued by default.
while (waitpid(-1, &status, WNOHANG) > 0) {
}
}
int handle_terminated_children(void)
{
struct sigaction sigact;
sigact.sa_handler = handler_sigchld;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART; // Make certain system calls restartable across signals
if (sigaction(SIGCHLD, &sigact, NULL)) {
return -1;
}
return 0;
}
/*----------------------------------------------------------------*/
/* Server */
/*----------------------------------------------------------------*/
/* Port number of the server. Generally, avoid:
* - IANA well-known ports : 0 - 1023
* - BSD ephemeral ports : 1024 - 5000
* - IANA dynamic or private ports : 49152 - 65535
*
* To check the empherical port range of Linux, do
* \$ cat /proc/sys/net/ipv4/ip_local_port_range
* 32768 61000
* (The default range since Linux 2.4)
*
* Refer to
* http://www.iana.org/assignments/port-numbers
* for port number assignments from the IANA.
*/
#define SERVER_PORT 10060
/* The maximum length the queue of pending connections may grow to */
#define LISTEN_BACKLOG 16
int server(void)
{
/* Obtain a socket descriptor */
int listen_fd;
listen_fd = socket(AF_INET, SOCK_STREAM, 0); // TCP
if (listen_fd < 0) {
// errno is set appropriately
return -1;
}
/* Assign a local address to the socket */
struct sockaddr_in server_addr; // IPv4 socket address structure
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Wildcard address; Network byte order
server_addr.sin_port = htons(SERVER_PORT);
if (bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
// errno is set appropriately
return -2;
}
/* Convert the socket into a passive listening socket */
if (listen(listen_fd, LISTEN_BACKLOG) < 0) {
// errno is set appropriately
return -3;
}
/* Arrange to handle terminated children */
if (handle_terminated_children() < 0) {
return -4;
}
for (;;) {
/* Get the descriptor of the next completed connection for the accepted socket */
int connection_fd;
struct sockaddr_in client_addr;
socklen_t client_addr_len;
client_addr_len = sizeof(client_addr);
connection_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_addr_len);
if (connection_fd < 0) {
// errno is set appropriately
switch (errno) {
case EINTR:
// Interrupted by a signal. Restart the connection.
continue;
case EAGAIN:
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
// Linux accept() passes already-pending network errors on the new
// socket as an error code from accept(). The application should
// detect these errors in the case of TCP/IP and retry again for
// reliable operation.
continue;
default:
return -5;
}
}
pid_t child_pid;
child_pid = fork();
if (child_pid == 0) {
// Child process
close(listen_fd); // Close the listening socket
/* Interact with the client */
#define STR_MAX 100
FILE *fp;
char str[STR_MAX];
fp = fdopen(connection_fd, "r+");
if (!fp) {
// errno is set appropriately
return -6;
}
// Echo the client once
if (fgets(str, STR_MAX, fp)) {
fseek(fp, 0L, SEEK_CUR); // Synchronization; See man fdopen(3)
if (fputs(str, fp) == EOF) {
return -7;
}
}
if (fclose(fp) == EOF) {
// errno is set appropriately
return -8;
}
exit(0);
}
else if (child_pid > 0) {
// Parent process
close(connection_fd); // Close the connection socket
}
else { // child_pid < 0
// errno is set appropriately
return -9;
}
}
return 0;
}
int main(int argc, char *argv[])
{
int retval;
retval = server();
printf("retval => %d (%s)\n", retval, strerror(errno));
return 0;
}
Code for the Client:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
/*----------------------------------------------------------------*/
/* Useful Wrappers */
/*----------------------------------------------------------------*/
/* A wrapper for write() that
- tries again for if write() was interrupted by a caught signal
- invokes write() again for incomplete write() due to buffer limit
*/
ssize_t write_safe(int fd, const void *p, size_t n)
{
ssize_t num_written;
while (n > 0) {
num_written = write(fd, p, n);
if (num_written <= 0) {
if (errno == EINTR)
num_written = 0; // Try again
else
return -1;
}
n -= num_written;
p += num_written;
}
return num_written;
}
/* A wrapper for read() that
- tries again for if read() was interrupted by a caught singal
- invokes read() again for incomplete read() due to buffer limit
(NOTE: Sometimes you may not want this behavior as you will be
blocked here until the buffer is full or EOF is reached.)
*/
ssize_t read_safe(int fd, void *p, size_t n)
{
size_t num_left;
ssize_t num_read;
num_left = n;
while (num_left > 0) {
num_read = read(fd, p, num_left);
if (num_read < 0) {
if (errno == EINTR)
num_read = 0; // Try again
else
return -1;
}
else if (num_read == 0) { // EOF
break;
}
num_left -= num_read;
p += num_read;
}
return n - num_left;
}
/*----------------------------------------------------------------*/
/* Client */
/*----------------------------------------------------------------*/
int client(const char *server_ip, int server_port)
{
/* Obtain a socket descriptor */
int socket_fd;
socket_fd = socket(AF_INET, SOCK_STREAM, 0); // TCP
if (socket_fd < 0) {
// errno is set appropriately
return -1;
}
/* Specify the address of the server */
struct sockaddr_in server_addr; // IPv4 socket address structure
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
int status;
if ((status = inet_pton(AF_INET, server_ip, &server_addr.sin_addr)) < 0) {
// status < 0: Does not contain a valid address family
// status == 0: Source address is invalid in the specified address family
return -2;
}
server_addr.sin_port = htons(server_port);
/* Initiate a connection on the socket */
if (connect(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
// errno is set appropriately
return -3;
}
/* Interact with the server */
#define STR_MAX 100
// Get a string
char str[STR_MAX];
printf("Enter a few words: ");
if (fgets(str, STR_MAX, stdin)) {
// Send it to the server
if (write_safe(socket_fd, str, strlen(str)) < 0) {
// errno is set appropriately
return -4;
}
}
// Read from the server
ssize_t num_read;
if ((num_read = read_safe(socket_fd, str, STR_MAX)) < 0) {
// errno is set appropriately
return -5;
}
// Display the result returned
printf("Echo: [%*s]\n", num_read, str);
return 0;
}
#define SERVER_PORT 10060
int main(int argc, char *argv[])
{
int retval;
retval = client("127.0.0.1", SERVER_PORT);
printf("retval => %d (%s)\n", retval, strerror(errno));
return 0;
}
Miscellaneous
kill()- Send signal to a process
- Perform error checking (EPERM: No permission to send; ESRCH: The process or process group does not exist.) without sending any signal.
Bookmark/Search this post with:

Digg
Facebook
Google
Yahoo
Technorati
hi , we have a problem how
hi , we have a problem how does it work ?
The codes here only serve to
The codes here only serve to demonstrate the essential part of using the Unix system calls and networking APIs to implement such clients or servers from scratch. You should be able to compile them using the GCC compiler. Note that the codes are NOT for production use!!