Echo server: Difference between revisions

From Rosetta Code
Content added Content deleted
(C)
Line 5: Line 5:


The implementation must not stop responding to other clients if one client sends a partial line or stops reading responses.
The implementation must not stop responding to other clients if one client sends a partial line or stops reading responses.

=={{header|C}}==
{{works with|POSIX}}

This is a rather standard code (details apart); the reference guide for such a code is the [http://beej.us/guide/bgnet Beej's Guide to Network programming]. The dependency from POSIX is mainly in the use of the <tt>read</tt> and <tt>write</tt> functions, (using the socket as a file descriptor sometimes make things simpler).

<lang c>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

#define MAX_ENQUEUED 20
#define BUF_LEN 256
#define PORT_STR "12321"

void wait_for_zombie(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0) ;
}

int main()
{
struct addrinfo hints, *res;
struct sockaddr addr;
struct sigaction sa;
socklen_t addr_size;
int sock;

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

if ( getaddrinfo(NULL, PORT_STR, &hints, &res) != 0 ) {
perror("getaddrinfo");
exit(EXIT_FAILURE);
}

if ( (sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1 ) {
perror("socket");
exit(EXIT_FAILURE);
}
sa.sa_handler = wait_for_zombie;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if ( sigaction(SIGCHLD, &sa, NULL) == - 1 ) {
perror("sigaction");
exit(EXIT_FAILURE);
}

if ( bind(sock, res->ai_addr, res->ai_addrlen) == 0 ) {
freeaddrinfo(res);
if ( listen(sock, MAX_ENQUEUED) == 0 ) {
for(;;) {
addr_size = sizeof(addr);
int csock = accept(sock, &addr, &addr_size);
if ( csock == -1 ) {
perror("accept");
} else {
if ( fork() == 0 ) {
close(sock);
char buf[BUF_LEN];
int r;
while( (r = read(csock, buf, BUF_LEN)) > 0 ) {
(void)write(csock, buf, r);
}
exit(EXIT_SUCCESS);
}
close(csock);
}
}
} else {
perror("listen");
exit(EXIT_FAILURE);
}
} else {
perror("bind");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}</lang>


=={{header|Tcl}}==
=={{header|Tcl}}==

Revision as of 15:07, 21 May 2009

Task
Echo server
You are encouraged to solve this task according to the task description, using any language you may know.

Create a network service that sits on TCP port 12321, which accepts connections on that port, and which echoes complete lines (using a carriage-return/line-feed sequence as line separator) back to clients. No error handling is required. For the purposes of testing, it is only necessary to support connections from localhost (127.0.0.1). Logging of connection information to standard output is recommended.

The implementation must be able to handle simultaneous connections from multiple clients. A multi-threaded solution may be used.

The implementation must not stop responding to other clients if one client sends a partial line or stops reading responses.

C

Works with: POSIX

This is a rather standard code (details apart); the reference guide for such a code is the Beej's Guide to Network programming. The dependency from POSIX is mainly in the use of the read and write functions, (using the socket as a file descriptor sometimes make things simpler).

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>
  3. include <errno.h>
  4. include <sys/types.h>
  5. include <sys/socket.h>
  6. include <netdb.h>
  7. include <unistd.h>
  8. include <sys/wait.h>
  9. include <signal.h>
  1. define MAX_ENQUEUED 20
  2. define BUF_LEN 256
  3. define PORT_STR "12321"

void wait_for_zombie(int s) {

 while(waitpid(-1, NULL, WNOHANG) > 0) ;

}

int main() {

 struct addrinfo hints, *res;
 struct sockaddr addr;
 struct sigaction sa;
 socklen_t addr_size;
 int sock;
 memset(&hints, 0, sizeof(struct addrinfo));
 hints.ai_family = AF_UNSPEC;
 hints.ai_socktype = SOCK_STREAM;
 hints.ai_flags = AI_PASSIVE;
 if ( getaddrinfo(NULL, PORT_STR, &hints, &res) != 0 ) {
   perror("getaddrinfo");
   exit(EXIT_FAILURE);
 }
 if ( (sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1 ) {
   perror("socket");
   exit(EXIT_FAILURE);
 }
 
 sa.sa_handler = wait_for_zombie;
 sigemptyset(&sa.sa_mask);
 sa.sa_flags = SA_RESTART;
 if ( sigaction(SIGCHLD, &sa, NULL) == - 1 ) {
   perror("sigaction");
   exit(EXIT_FAILURE);
 }
 if ( bind(sock, res->ai_addr, res->ai_addrlen) == 0 ) {
   freeaddrinfo(res);
   if ( listen(sock, MAX_ENQUEUED) == 0 ) {
     for(;;) {

addr_size = sizeof(addr); int csock = accept(sock, &addr, &addr_size); if ( csock == -1 ) { perror("accept"); } else { if ( fork() == 0 ) { close(sock); char buf[BUF_LEN]; int r; while( (r = read(csock, buf, BUF_LEN)) > 0 ) { (void)write(csock, buf, r); } exit(EXIT_SUCCESS); } close(csock); }

     }
   } else {
     perror("listen");
     exit(EXIT_FAILURE);
   }
 } else {
   perror("bind");
   exit(EXIT_FAILURE);
 }
 return EXIT_SUCCESS;

}</lang>

Tcl

<lang tcl># How to handle an incoming new connection proc acceptEcho {chan host port} {

   puts "opened connection from $host:$port"
   fconfigure $chan -blocking 0 -buffering line -translation crlf
   fileevent $chan readable [list echo $chan $host $port]

}

  1. How to handle an incoming message on a connection

proc echo {chan host port} {

   if {[gets $chan line] >= 0} {
       puts $chan $line
   } elseif {[eof $chan]} {
       close $chan
       puts "closed connection from $host:$port"
   }
   # Other conditions causing a short read need no action

}

  1. Make the server socket and wait for connections

socket -server acceptEcho -myaddr localhost 12321 vwait forever</lang>

Alternative version

A more succinct version (though one harder to adapt to other kinds of services, but closer to the standard unix echo daemon since it has no line-buffering) is to use an asynchronous binary copy. <lang tcl># How to handle an incoming new connection proc acceptEcho {chan host port} {

   puts "opened connection from $host:$port"
   fconfigure $chan -translation binary -buffering none
   fcopy $chan $chan -command [list done $chan $host $port]

}

  1. Called to finalize the connection

proc done {chan host port args} {

   puts "closed connection from $host:$port"
   close $chan

}

  1. Make the server socket and wait for connections

socket -server acceptEcho -myaddr localhost 12321 vwait forever</lang>