Unix经典编程

来源:互联网 发布:有没有京东秒杀软件 编辑:程序博客网 时间:2024/04/28 11:45

一说起Unix编程,不必多说,最著名的系统调用就是fork,pipe,exec,kill或是socket了(fork(2),execve(2),pipe(2),socketpair(2), select(2), kill(2), sigaction(2))这些系统调用都像是Unix编程的胎记或签名一样,表明着它来自于Unix。

下面这篇文章,将向大家展示Unix下最经典的socket的编程例子——使用fork + socket来创建一个TCP/IP的服务程序。这个编程模式很简单,首先是创建Socket,然后把其绑定在某个IP和Port上上侦听连接,接下来的一般做法是使用一个fork创建一个client服务进程再加上一个死循环用于处理和client的交互。这个模式是Unix下最经典的Socket编程例子。

下面,让我们看看用C,Ruby,Python,Perl,PHP和Haskell来实现这一例子,你会发现这些例子中的Unix的胎记。如果你想知道这些例子中的技术细节,那么,向你推荐两本经典书——《Unix高级环境编程》和《Unix网络编程》。

 

C语言

我们先来看一下经典的C是怎么实现的。

001./**
002. * A simple preforking echo server in C.
003. *
004. * Building:
005. *
006. * $ gcc -Wall -o echo echo.c
007. *
008. * Usage:
009. *
010. * $ ./echo
011. *
012. *   ~ then in another terminal ... ~
013. *
014. * $ echo 'Hello, world!' | nc localhost 4242
015. *
016. */
017.  
018.#include <unistd.h> /* fork, close */
019.#include <stdlib.h> /* exit */
020.#include <string.h> /* strlen */
021.#include <stdio.h> /* perror, fdopen, fgets */
022.#include <sys/socket.h>
023.#include <sys/wait.h> /* waitpid */
024.#include <netdb.h> /* getaddrinfo */
025.  
026.#define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
027.  
028.#define PORT "4242"
029.#define NUM_CHILDREN 3
030.  
031.#define MAXLEN 1024
032.  
033.intreadline(intfd, char*buf, intmaxlen); // forward declaration
034.  
035.int
036.main(intargc, char** argv)
037.{
038.    inti, n, sockfd, clientfd;
039.    intyes = 1; // used in setsockopt(2)
040.    structaddrinfo *ai;
041.    structsockaddr_in *client;
042.    socklen_t client_t;
043.    pid_t cpid;// child pid
044.    charline[MAXLEN];
045.    charcpid_s[32];
046.    charwelcome[32];
047.  
048.    /* Create a socket and get its file descriptor -- socket(2) */
049.    sockfd = socket(AF_INET, SOCK_STREAM, 0);
050.    if(sockfd == -1) {
051.    die("Couldn't create a socket");
052.    }
053.  
054.    /* Prevents those dreaded "Address already in use" errors */
055.    if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (constvoid*)&yes, sizeof(int)) == -1) {
056.    die("Couldn't setsockopt");
057.    }
058.  
059.    /* Fill the address info struct (host + port) -- getaddrinfo(3) */
060.    if(getaddrinfo(NULL, PORT, NULL, &ai) != 0) {
061.    die("Couldn't get address");
062.    }
063.  
064.    /* Assign address to this socket's fd */
065.    if(bind(sockfd, ai->ai_addr, ai->ai_addrlen) != 0) {
066.    die("Couldn't bind socket to address");
067.    }
068.  
069.    /* Free the memory used by our address info struct */
070.    freeaddrinfo(ai);
071.  
072.    /* Mark this socket as able to accept incoming connections */
073.    if(listen(sockfd, 10) == -1) {
074.    die("Couldn't make socket listen");
075.    }
076.  
077.    /* Fork you some child processes. */
078.    for(i = 0; i < NUM_CHILDREN; i++) {
079.    cpid = fork();
080.    if(cpid == -1) {
081.        die("Couldn't fork");
082.    }
083.  
084.    if(cpid == 0) { // We're in the child ...
085.        for(;;) { // Run forever ...
086.        /* Necessary initialization for accept(2) */
087.        client_t =sizeofclient;
088.  
089.        /* Blocks! */
090.        clientfd = accept(sockfd, (structsockaddr *)&client, &client_t);
091.        if(clientfd == -1) {
092.            die("Couldn't accept a connection");
093.        }
094.  
095.        /* Send a welcome message/prompt */
096.        bzero(cpid_s, 32);
097.        bzero(welcome, 32);
098.        sprintf(cpid_s,"%d", getpid());
099.        sprintf(welcome,"Child %s echo> ", cpid_s);
100.        send(clientfd, welcome,strlen(welcome), 0);
101.  
102.        /* Read a line from the client socket ... */
103.        n = readline(clientfd, line, MAXLEN);
104.        if(n == -1) {
105.            die("Couldn't read line from connection");
106.        }
107.  
108.        /* ... and echo it back */
109.        send(clientfd, line, n, 0);
110.  
111.        /* Clean up the client socket */
112.        close(clientfd);
113.        }
114.    }
115.    }
116.  
117.    /* Sit back and wait for all child processes to exit */
118.    while(waitpid(-1, NULL, 0) > 0);
119.  
120.    /* Close up our socket */
121.    close(sockfd);
122.  
123.    return0;
124.}
125.  
126./**
127. * Simple utility function that reads a line from a file descriptor fd,
128. * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.
129. */
130.int
131.readline(intfd, char*buf, intmaxlen)
132.{
133.    intn, rc;
134.    charc;
135.  
136.    for(n = 1; n < maxlen; n++) {
137.    if((rc = read(fd, &c, 1)) == 1) {
138.        *buf++ = c;
139.        if(c == '/n')
140.        break;
141.    }elseif(rc == 0) {
142.        if(n == 1)
143.        return0; // EOF, no data read
144.        else
145.        break;// EOF, read some data
146.    }else
147.        return-1; // error
148.    }
149.  
150.    *buf ='/0';// null-terminate
151.    returnn;
152.}

Ruby

下面是Ruby,你可以看到其中的fork

01.   
02.  
03.# simple preforking echo server in Ruby
04.require'socket'
05.  
06.# Create a socket, bind it to localhost:4242, and start listening.
07.# Runs once in the parent; all forked children inherit the socket's
08.# file descriptor.
09.acceptor = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM,0)
10.address = Socket.pack_sockaddr_in(4242,'localhost')
11.acceptor.bind(address)
12.acceptor.listen(10)
13.  
14.# Close the socket when we exit the parent or any child process. This
15.# only closes the file descriptor in the calling process, it does not
16.# take the socket out of the listening state (until the last fd is
17.# closed).
18.#
19.# The trap is guaranteed to happen, and guaranteed to happen only
20.# once, right before the process exits for any reason (unless
21.# it's terminated with a SIGKILL).
22.trap('EXIT') { acceptor.close }
23.  
24.# Fork you some child processes. In the parent, the call to fork
25.# returns immediately with the pid of the child process; fork never
26.# returns in the child because we exit at the end of the block.
27.3.timesdo
28.  forkdo
29.    # now we're in the child process; trap (Ctrl-C) interrupts and
30.    # exit immediately instead of dumping stack to stderr.
31.    trap('INT') { exit }
32.  
33.    puts"child #$$ accepting on shared socket (localhost:4242)"
34.    loop {
35.      # This is where the magic happens. accept(2) blocks until a
36.      # new connection is ready to be dequeued.
37.      socket, addr = acceptor.accept
38.      socket.write"child #$$ echo> "
39.      socket.flush
40.      message = socket.gets
41.      socket.write message
42.      socket.close
43.      puts"child #$$ echo'd: '#{message.strip}'"
44.    }
45.    exit
46.  end
47.end
48.  
49.# Trap (Ctrl-C) interrupts, write a note, and exit immediately
50.# in parent. This trap is not inherited by the forks because it
51.# runs after forking has commenced.
52.trap('INT') { puts"/nbailing"; exit }
53.  
54.# Sit back and wait for all child processes to exit.
55.Process.waitall

Python

01."""
02.Simple preforking echo server in Python.
03."""
04.  
05.importos
06.importsys
07.importsocket
08.  
09.# Create a socket, bind it to localhost:4242, and start
10.# listening. Runs once in the parent; all forked children
11.# inherit the socket's file descriptor.
12.acceptor=socket.socket()
13.acceptor.bind(('localhost',4242))
14.acceptor.listen(10)
15.  
16.# Ryan's Ruby code here traps EXIT and closes the socket. This
17.# isn't required in Python; the socket will be closed when the
18.# socket object gets garbage collected.
19.  
20.# Fork you some child processes. In the parent, the call to
21.# fork returns immediately with the pid of the child process;
22.# fork never returns in the child because we exit at the end
23.# of the block.
24.fori inrange(3):
25.    pid=os.fork()
26.  
27.    # os.fork() returns 0 in the child process and the child's
28.    # process id in the parent. So if pid == 0 then we're in
29.    # the child process.
30.    ifpid ==0:
31.        # now we're in the child process; trap (Ctrl-C)
32.        # interrupts by catching KeyboardInterrupt) and exit
33.        # immediately instead of dumping stack to stderr.
34.        childpid=os.getpid()
35.        print"Child %s listening on localhost:4242"%childpid
36.        try:
37.            while1:
38.                # This is where the magic happens. accept(2)
39.                # blocks until a new connection is ready to be
40.                # dequeued.
41.                conn, addr=acceptor.accept()
42.  
43.                # For easier use, turn the socket connection
44.                # into a file-like object.
45.                flo=conn.makefile()
46.                flo.write('Child %s echo> ' %childpid)
47.                flo.flush()
48.                message=flo.readline()
49.                flo.write(message)
50.                flo.close()
51.                conn.close()
52.                print"Child %s echo'd: %r"%/
53.                          (childpid, message.strip())
54.        exceptKeyboardInterrupt:
55.            sys.exit()
56.  
57.# Sit back and wait for all child processes to exit.
58.#
59.# Trap interrupts, write a note, and exit immediately in
60.# parent. This trap is not inherited by the forks because it
61.# runs after forking has commenced.
62.try:
63.    os.waitpid(-1,0)
64.exceptKeyboardInterrupt:
65.    print"/nbailing"
66.    sys.exit()

Perl

01.#!/usr/bin/perl
02.use5.010;
03.usestrict;
04.  
05.# simple preforking echo server in Perl
06.useProc::Fork;
07.useIO::Socket::INET;
08.  
09.substrip { s//A/s+//, s//s+/z// formy@r = @_;@r }
10.  
11.# Create a socket, bind it to localhost:4242, and start listening.
12.# Runs once in the parent; all forked children inherit the socket's
13.# file descriptor.
14.my$acceptor = IO::Socket::INET->new(
15.    LocalPort => 4242,
16.    Reuse     => 1,
17.    Listen   => 10,
18.) ordie"Couln't start server: $!/n";
19.  
20.# Close the socket when we exit the parent or any child process. This
21.# only closes the file descriptor in the calling process, it does not
22.# take the socket out of the listening state (until the last fd is
23.# closed).
24.END {$acceptor->close}
25.  
26.# Fork you some child processes. The code after the run_fork block runs
27.# in all process, but because the child block ends in an exit call, only
28.# the parent executes the rest of the program. If a parent block were
29.# specified here, it would be invoked in the parent only, and passed the
30.# PID of the child process.
31.for( 1 .. 3 ) {
32.    run_fork { child {
33.        while(1) {
34.            my$socket = $acceptor->accept;
35.            $socket->printflush("child $$ echo> ");
36.            my$message = $socket->getline;
37.            $socket->print($message );
38.            $socket->close;
39.            say"child $$ echo'd: '${/strip $message}'";
40.        }
41.        exit;
42.    } }
43.}
44.  
45.# Trap (Ctrl-C) interrupts, write a note, and exit immediately
46.# in parent. This trap is not inherited by the forks because it
47.# runs after forking has commenced.
48.$SIG{'INT' } = sub{ print"bailing/n";exit};
49.  
50.# Sit back and wait for all child processes to exit.
51.1while0 < waitpid-1, 0;
52.  

PHP

01.<?
02./*
03.Simple preforking echo server in PHP.
04.Russell Beattie (russellbeattie.com)
05.*/
06.  
07./* Allow the script to hang around waiting for connections. */
08.set_time_limit(0);
09.  
10.# Create a socket, bind it to localhost:4242,andstart
11.# listening. Runs once in the parent; all forked children
12.# inherit the socket's file descriptor.
13.$socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
14.socket_bind($socket,'localhost', 4242);
15.socket_listen($socket, 10);
16.  
17.pcntl_signal(SIGTERM,'shutdown');
18.pcntl_signal(SIGINT,'shutdown');
19.  
20.functionshutdown($signal){
21.    global$socket;
22.    socket_close($socket);
23.    exit();
24.}
25.# Fork you some child processes. In the parent, the call to
26.# fork returns immediately with the pid of the child process;
27.# fork never returns in the child because weexit at the end
28.# of the block.
29.for($x= 1; $x<= 3; $x++){
30.     
31.    $pid= pcntl_fork();
32.     
33.    # pcntl_fork() returns 0 in the child processandthe child's
34.    # process id in the parent. Soif$pid == 0 then we're in
35.    # the child process.
36.    if($pid== 0){
37.  
38.        $childpid= posix_getpid();
39.         
40.        echo"Child $childpid listening on localhost:4242 /n";
41.  
42.        while(true){
43.            # This is where the magic happens. accept(2)
44.            # blocks until anewconnection is ready to be
45.            # dequeued.
46.            $conn= socket_accept($socket);
47.  
48.            $message= socket_read($conn,1000,PHP_NORMAL_READ);
49.             
50.            socket_write($conn,"Child $childpid echo> $message");
51.         
52.            socket_close($conn);
53.         
54.            echo"Child $childpid echo'd: $message /n";
55.         
56.        }
57.  
58.    }
59.}
60.#
61.# Trap interrupts, write a note,andexit immediately in
62.# parent. This trap is not inherited by the forks because it
63.# runs after forking has commenced.
64.try{
65.  
66.    pcntl_waitpid(-1,$status);
67.  
68.} catch (Exception$e) {
69.  
70.    echo"bailing /n";
71.    exit();
72.  
73.}

Haskell

01.importNetwork
02.importPrelude hiding ((-))
03.importControl.Monad
04.importSystem.IO
05.importControl.Applicative
06.importSystem.Posix
07.importSystem.Exit
08.importSystem.Posix.Signals
09.  
10.main :: IO ()
11.main = with =<< (listenOn - PortNumber 4242) where
12.  
13.  with socket =do
14.    replicateM 3 - forkProcess work
15.    wait
16.  
17.    where
18.    work =do
19.      installHandler sigINT (Catch trap_int) Nothing
20.      pid <- show <$> getProcessID
21.      puts -"child " ++ pid ++ " accepting on shared socket (localhost:4242)"
22.       
23.      forever -do
24.        (h, _, _) <- accept socket
25.  
26.        letwrite   = hPutStr h
27.            flush   = hFlush h
28.            getline = hGetLine h
29.            close   = hClose h
30.  
31.        write -"child " ++ pid ++ " echo> "
32.        flush
33.        message <- getline
34.        write - message ++"/n"
35.        puts -"child " ++ pid ++ " echo'd: '"++ message ++ "'"
36.        close
37.  
38.    wait = forever -do
39.      ( const () <$> getAnyProcessStatus True True  ) `catch` const trap_exit
40.  
41.    trap_int = exitImmediately ExitSuccess
42.  
43.    trap_exit =do
44.      puts"/nbailing"
45.      sClose socket
46.      exitSuccess
47.  
48.    puts = putStrLn
49.  
50.  (-) = ($)
51.  infixr 0 -
原创粉丝点击