原文:http://coolshell.cn/?p=1532 (酷壳

如果你想知道Unix的一些故事,你可以查看下面这些文章:

  • Unix40年:昨天,今天和明天
  • Unix传奇上篇下篇
  • Unix的现状与未来

一说起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. int readline(int fd, char *buf, int maxlen); // forward declaration
034.   
035. int
036. main(int argc, char** argv)
037. {
038.     int i, n, sockfd, clientfd;
039.     int yes = 1; // used in setsockopt(2)
040.     struct addrinfo *ai;
041.     struct sockaddr_in *client;
042.     socklen_t client_t;
043.     pid_t cpid; // child pid
044.     char line[MAXLEN];
045.     char cpid_s[32];
046.     char welcome[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, (const void *)&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 = sizeof client;
088.   
089.         /* Blocks! */
090.         clientfd = accept(sockfd, (struct sockaddr *)&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.     return 0;
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(int fd, char *buf, int maxlen)
132. {
133.     int n, rc;
134.     char c;
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.     } else if (rc == 0) {
142.         if (n == 1)
143.         return 0; // 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.     return n;
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.times do
28.   fork do
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. import os
06. import sys
07. import socket
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. for i in range(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.     if pid == 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.             while 1:
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.         except KeyboardInterrupt:
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. except KeyboardInterrupt:
65.     print "/nbailing"
66.     sys.exit()

Perl

01. #!/usr/bin/perl
02. use 5.010;
03. use strict;
04.   
05. # simple preforking echo server in Perl
06. use Proc::Fork;
07. use IO::Socket::INET;
08.   
09. sub strip { s//A/s+//, s//s+/z// for my @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. ) or die "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. 1 while 0 < 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, and start
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. function shutdown($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 we exit 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 process and the child's
34.     # process id in the parent. So if $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 a new connection 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, and exit 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. import Network
02. import Prelude hiding ((-))
03. import Control.Monad
04. import System.IO
05. import Control.Applicative
06. import System.Posix
07. import System.Exit
08. import System.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.         let write   = 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 -

如果你知道更多的,请你告诉我们。(全文完)

到处都是Unix的胎记相关推荐

  1. 宜信创始人唐宁:技术到处都是,关键看怎么用

     宜信创始人唐宁:技术到处都是,关键看怎么用 极客公园 2017-06-02 16:15 阅读:4443 摘要:作者:「痛点是什么?」5月26日下午的北大斯坦福中心,这是宜信创始人唐宁口中出现频率 ...

  2. 最近大街上,到处都是X团的地推

    小文说,最近大街上,到处都是X团的地推,成群结队的,拉着每一个路人推销他们的软件,忽悠着你下载他们的APP. 小文昨天中午出去吃饭,五.六个X团的地推,把一个漂亮女孩围在中间,各个都是轻佻的语气,拜托 ...

  3. 不要到处都用@Autowired啦

    相信大部分人项目只要用了spring框架,肯定到处都是@Autowired. 注意: spring4.3以后就可以省略@Autowired了,全部更新内容: 地址:其中跟新内容如下 这是什么意思呢,翻 ...

  4. 我对NHibernate的感受(2):何必到处都virtual

    上一篇主要是在夸NHibernate实现的好,而这篇就完全是来抱怨的了.NHiberante有个毛病,就是如果是和数据库产生映射的类,就要求所有的public成员和protected成员必须是virt ...

  5. NHibernate :何必到处都virtual

    上一篇文章主要是在夸NHibernate实现的好,而这篇就完全是来抱怨的了.NHiberante有个毛病,就是如果是和数据库产生映射的类,就要求所有的public成员和protected成员必须是vi ...

  6. 说一说ffmpeg到处都在使用的ff_thread_once函数

    从名字就能知道ff_thread_once函数的作用,就是保证在多线程调用的时候,函数只执行一次 FFmpeg中是使用经典的double check来保证函数只执行一次的,我们来跟踪下函数看下具体的实 ...

  7. shields 徽标_到处都有平面徽标

    shields 徽标 重点 (Top highlight) Companies invest a lot of time, money and energy trying to make audien ...

  8. openai-gpt_为什么到处都看到GPT-3?

    openai-gpt Disclaimer: My opinions are informed by my experience maintaining Cortex, an open source ...

  9. 为什么到处都在推python_Python为什么能取得越来越超然的地位

    展开全部 Python在人工智能领域的e69da5e887aa62616964757a686964616f31333365643632应用 2016年开发语言使用情况统计 机器学习与数据科学领域各语言 ...

最新文章

  1. 艾伟:ASP.NET MVC,深入浅出IModelBinder,在Post方式下慎用HtmlHelper
  2. php可以独立运行,【判断题】PHP不能独立运行,只能和Apache服务器一同使用( )。...
  3. altium designer无法创建工程_前端工程化之开发脚手架及封装自动化构建
  4. JavaScript实现CountingSort计数排序算法(附完整源码)
  5. poj 2299 Ultra-QuickSort
  6. DMA(2) S3C2410 DMA详解(其它的其实类似)
  7. Keep Network启动下一阶段流动性挖矿计划
  8. 自动化测试-selenium启动浏览器
  9. 传递function_JS中!function(){}()的理解
  10. 软件测试基础——理论知识
  11. matlab中交点坐标,matlab交点坐标
  12. SpringMVC学习(一)SpringMVC入门 、HelloSpringMVC程序、SpringMVC执行原理
  13. php表单验证插件下载,强力推荐10款Javascript表单验证插件
  14. 打卡项目php,Thinkphp框架早起打卡项目(深蓝引擎Z)趣步模式+完整数据+全开源源码...
  15. 网页前端(Html)video播放m3u8(HLS)Vue使用video.js播放m3u8
  16. matlab求最大公倍数_matlab求最大公约数和最小公倍数
  17. 8.1 结构体的定义和使用
  18. 雷电模拟器导入burp证书
  19. MC9S12G128模块化分层化软件架构之二——IO驱动
  20. DOM节点操作----节点层级

热门文章

  1. Halo博客的谷歌收录自动提交
  2. macOS npm -g 安装路径
  3. 源程序管理软件与项目管理软件
  4. 旺店通·企业奇门与金蝶云星空对接集成查询销售出库单连通销售出库新增(分销销售出库)
  5. 【淘宝SEO技巧】解读淘宝新规及SEO技术实战指南
  6. 陆金所服务器维护,陆金所如何在线更换金融核心场景的 Oracle 数据库
  7. HCIE北京考场体验报告
  8. 安装python包时报‘HTMLParser‘ object has no attribute ‘unescape‘
  9. HC32F460(华大单片机)学习开发
  10. win7下rndis/ethernet gadget驱动安装