好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

socket编程过程中

socket编程过程中

 

好多天前就想写的,可是拖延症暂时没药医。。。

 

socket编程过程中,有几个前提(在UNP的前言以及第一章中有提到)。1.需要一定的编程语言基础,例如C语言;2.需要有一定的TCP/IP网络协议栈知识,至少知道TCP和UDP的连接和释放(话说UDP不需要连接);3.如果可能,最好对操作系统(例如Linux)有一定了解。然后,就可以开搞了。

 

一般而言,socket编程分为客户端和服务端编程,socket提供在这两者之间进行信息交互。基本的流程如下:

 

 

其中,read和write属于Linux/Unix下的系统调用,通常还可以用recv和send函数代替。这张图引用自UNP第四章,使用系统调用的原因主要是为了表明,socket函数返回的socket描述符可以和Linux系统的本地文件描述符同样对待,因此也能够使用close函数来关闭一个套接字。

 

TCP属于全双工通信,因此一个套接字描述符拥有两个缓冲区,一个发送,一个接收,即tcp socket可以同时进行数据的收发而相互不影响。

 

对于客户端而言,socket函数产生套接字描述符,connect函数连接到服务端,服务端信息(例如ip地址,端口号等)放在一个socket_in结构中。通常而言,客户端不需要使用bind函数,但如果非要使用,也可以把客户端绑定到一个特定的ip或端口号(这两个可以任意组合)。其中,端口号小于1024(系统服务使用)需要root权限分配,ip地址则必须是本地网络接口的ip地址,即bind函数可以将客户端绑定到本机的一个特定的网络接口上,这样往外发数据就都使用这个特定的接口,否则根据目的地址的不同会通过不同的接口发送。例如,如果不绑定ip地址,发送数据到本地环回地址(127.0.0.1)将同样使用本地接口,而到本机的局域网地址则使用有线网络接口(例如eth0),具体连接可以通过netstat查看。因此,如果要限制系统中只能开一个客户端,可以将客户端的ip和端口全部绑死,这样开第二个客户端的时候connect函数就会返回“Address already in use”错误。

 

关于文件描述符(套接字描述符),在看UNP谈及最简单的并发时(使用fork),循环里是这样写的:

 

?

for (;;){

  connfd=accept(listen_fd,NULL,NULL);

   if ((pid=fork())==0){

    close(listen_fd);

    do_something();

    close(connfd);

     exit (0);

  }

close(connfd);

}

 

这里涉及到fork函数的调用。 fork函数创建了一个新的进程,然后把旧进程的一切都复制了一遍(实际上只有两者产生不同时才复制),所以实际上就存在了两个监听套接字和连接套接字描述符,在Linux中就是对套接字描述符的链接计数器+1,所以在子进程中关闭监听套接字只是计数器-1的操作,不会真的关闭连接。

 

 

初学socket编程时,容易遇到许多的坑,例如,TCP的状态转换,见下图:

 

 

TCP连接的状态并不是《计算机网络》(谢希仁版,教材)那本书中提到那么简单,而且连接终止也更加细化了一些。如果不能理解(记住)这些状态,时不时就要被坑一下。

 

又例如,socket使用网络进行数据传输,所以各种网络中的突发状况都会在通信过程中发生,而服务端和客户端也可能发生许多不可预知的错误。例如,服务器或客户端异常关闭,数据的字节序不同,客户端和服务端字大小不同(例如一个是32位另一个是64位),网络突然断开等等。

 

所以,简单的处理就是对各个函数调用过程中产生的错误信息(产生信号)进行相应的处理,例如添加信号处理函数等。

 

 

~EOF~

 

 

 

分类:  C/C++ ,  Network

标签:  Linux ,  Socket ,  C/C++

如何一步一步推导出Y Combinator

2013-04-13 22:24 by 崔鹏飞, 349 阅读,  1  评论,  收藏 ,  编辑

粘贴过来的代码高亮有问题,可以到我的另一个博客阅读: http://cuipengfei.me/blog/2013/04/09/make-y/  

本文讲什么?

本文用Scheme(Racket)代码为例,一步一步的推出Y Combinator的实现。

本文不讲什么?

Y Combinator是什么,干什么用的,它为什么能够work,它的数学含义以及实际应用场景,这些话题由于篇幅所限(咳咳,楼主的无知)不在本文论述范围之内。

如果有兴趣,请参考维基:  http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator

鸣谢

感谢Jojo同学的  这段JavaScript代码 的启发,我写了 对应的Scheme实现 。然后才有了本文。

正文开始

我们知道Y Combinator可以帮匿名函数实现递归。那就从一个广为人知的递归函数-阶乘开始吧。

 1 
 2 
 3 
   (  define   (  fac1   n  ) 
     (  if   (  <   n   2  )   1 
         (  *   n   (  fac1   (  -   n   1  ))))) 
  

如果n小于2,则返回1,否则开始递归,简单明了。如果像这样调用它一下:

 1 
  (  fac1   5  ) 
 

会返回120,结果无误。

上面是阶乘的递归实现,它有一个名字叫做fac1,但是如果用匿名函数实现阶乘呢?

 1 
 2 
 3 
 4 
   (  lambda   (  f  ) 
     (  lambda   (  n  ) 
       (  if   (  <   n   2  )   1 
           (  *   n   (  f   (  -   n   1  )))))) 
  

这个匿名函数“梦想着”其调用者会把该函数自己的实现作为参数传递进去。

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
   (((  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))) 
     (  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))))   1  ) 
  

我们把匿名函数重复写一遍,就可以计算1或者是0的阶乘,但是要计算3的阶乘呢?那就得这么写:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
   (((  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))) 
     ((  lambda   (  f  ) 
        (  lambda   (  n  ) 
          (  if   (  <   n   2  )   1 
              (  *   n   (  f   (  -   n   1  )))))) 
      ((  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))) 
       (  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))))))   3  ) 
  

想要计算一个大于2的n的阶乘,就得把这个匿名函数重复写n+1次。这么多的重复代码,这么多的括号。。。

所以我们需要一个神奇的函数,Y,它可以接受一个匿名的伪递归函数作为参数,产出一个真递归的函数。 这个神奇的Y作用在上面的匿名函数上之后产出的结果就可以用来计算任何n的阶乘。下面的代码会输出120(如果Y已经实现了的话)。

 1 
 2 
 3 
 4 
   ((  Y   (  lambda   (  f  ) 
          (  lambda   (  n  ) 
            (  if   (  <   n   2  )   1 
                (  *   n   (  f   (  -   n   1  )))))))   5  ) 
  

下面就开始一步步的构造这个神奇的Y吧。

为了便于推导,暂时给这个匿名函数一个名字,叫做fake_fac。

 1 
 2 
 3 
 4 
 5 
   (  define   fake_fac 
     (  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  ))))))) 
  

有了这个名字之后,再要计算3的阶乘就容易了一些。

 1 
  ((  fake_fac   (  fake_fac   (  fake_fac   fake_fac  )))   3  ) 
 

观察上面的代码,我们把fake_fac传递给它自己,得到一个返回值,把这个返回的值再次传递给fake_fac,再得到一个新的返回值,又把新的返回值传递给fake_fac,得到最终的返回值,最后把3传递给这个返回值。

可以看到,我们在不停的把fake_rec传给它自己,所以定义一个helper吧:

 1 
  (  define   (  callself   f  )   (  f   f  )) 
 

这个helper一会儿会派上用场。

现在看看fake_fac中的f是什么呢?对于((fake_fac (fake_fac (fake_fac fake_fac))) 3)这行代码中的最右侧的fake_fac来说,f没有用,因为这个fake_fac自己都没有被调到,它只是起个占位符的作用,实际上这行代码((fake_fac (fake_fac (fake_fac 1))) 3)和上面的那行是等价的。

对于右侧第二个fake_fac来说,f就是fake_fac。对于左侧第二个fake_fac来说,f是(fake_fac fake_fac)的返回值。

对于左侧第一个fake_fac来说,f是(fake_fac (fake_fac fake_fac))的返回值。

由此可见,f是fake_fac对自己反复调用的返回值。而且从fake_fac的定义可见,我们总是给f传递一个数字n,这样的话,我们再写一个helper:

 1 
  (  lambda   (  n  )   ((  f   f  )   n  )) 
 

再把这个helper传递给fake_fac。

 1 
  (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  ))) 
 

但是上面这两段代码是有问题的,因为f的值无法确定。

有句话说的好: if you don’t know exactly what you want to put somewhere in a piece of code, just abstract it out and make it a parameter of a function. 所以我们就把f抽成参数吧。

 1 
 2 
   (  define   (  callselfWithN   f  ) 
     (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  )))) 
  

我们希望这个helper可以帮fake_fac无限次的调用自己。

现在,我们该怎么调用callselfWithN呢?不能把fake_fac传给它,因为那样的话(f f)就只是fake_fac对自己的调用,它只能计算0或者1的阶乘。所以要把callselfWithN这个我们希望可以帮fake_fac实现无限次自调用的函数传给callselfWithN它自己。

 1 
  ((  callselfWithN   callselfWithN  )   5  ) 
 

这行代码可以返回120,结果正确了!

记得前面定义的第一个helper吗?现在用的上了:

 1 
  ((  callself   callselfWithN  )   5  ) 
 

现在把callselfWithN带入:

 1 
 2 
   ((  callself    (  lambda   (  f  ) 
     (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  )))))   5  ) 
  

可以看出,这段代码和fake_fac是紧耦合的,把它抽到参数上去:

 1 
 2 
 3 
   (  define   (  Y3   fake_recur  ) 
     (  callself    (  lambda   (  f  ) 
                  (  fake_recur   (  lambda   (  n  )   ((  f   f  )   n  )))))) 
  

然后再把callself也带入:

 1 
 2 
 3 
 4 
 5 
   (  define   Y   (  lambda   (  fake_recur  ) 
               ((  lambda   (  f  )   (  f   f  )) 
                (  lambda   (  f  ) 
                  (  fake_recur 
                   (  lambda   (  n  )   ((  f   f  )   n  ))))))) 
  

现在Y不依赖于任何其他函数了,测试一下Y,把前面的计算阶乘的匿名函数传给它:

 1 
 2 
 3 
 4 
   ((  Y   (  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))))   5  ) 
  

能够返回120,正确!Y Combinator构造完成!

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于socket编程过程中的详细内容...

  阅读:39次