[Erlang]hello world”与<<”hello world”>>详解(2)

来源:互联网 发布:疯狂java讲义在线阅读 编辑:程序博客网 时间:2024/06/06 20:14

所以我们在Eralng shell中找开一个socket:

1> {ok, Socket} = gen_udp:open(8789, [binary, {active,true}]).{ok,#Port<0.676>}2> gen_udp:open(8789, [binary, {active,true}]).{error,eaddrinuse}

这第一个命令,使用接收二进制,{active,true}的方式打开了8789端口,你可以看到一个如#Port<0.676>的返回值,这代表着我们刚打开的端口,你可以类似使用Pid一样使用这个端口,你甚至可以把它们与进程link起来,便于sockect 崩溃时,对应进程可以处理;

第二个命令还想再次打开同一个8789端口,因为第一个命令打开的端口没有释放掉,返回一个错误{error,eaddiinuse}该地址已被使用了。

不管怎样,我们再开一个Erlang shell终端,使用一个不同的端口找开第二个UDP socket:

1> {ok, Socket} = gen_udp:open(8790).{ok,#Port<0.587>}2> gen_udp:send(Socket, {127,0,0,1}, 8789, "hey there!").ok

哈哈,看到一个新的函数:gen_udp:send/4 ,从名称上就可以看出是用来发信息,参数:

gen_udp:send(OwnSocket, RemoteAddress, RemotePort, Message).

RemoteAddress 可以是一个字符串,原子结构(包括”example.org”这样的域名),一个IPv4(4元素数字的元组)或IPv6(8元素数字的元组);

RemotePort就是对应收信的端口,

Message就是信息本身,可以是字符串,二进制,或一个IO list.

刚发出去的信息,对方收到了么,你可以在你的第一个shell里面刷新看下:

3> flush().Shell got {udp,#Port<0.676>,{127,0,0,1},8790,<<"hey there!">>}ok

妙哉!二个shell就这样可以通讯啦… 打开socket的进程会收到一个格式如下的消息:

{udp, Socket, FromIp, FromPort, Message}.

这个消息包含了它从哪来,内容是什么,所以我们可以使用active 模块来收发信息。

那么什么是passive模式呢,为了说明passive 模式,我们需要关闭第一个shell里面的socket,然后再开一个新的socket端口:

4> gen_udp:close(Socket).ok5> f(Socket).ok6> {ok, Socket} = gen_udp:open(8789, [binary, {active,false}]).{ok,#Port<0.683>}

在上面,我们关闭了socket,解绑了Socket变量,然后再把Socket绑定为passive模式,然后再试试下面的命令:

7> gen_udp:recv(Socket, 0).

这时你的shell会被阻塞,recv/2是用来等待一个passive socket把信息发进来的函数,0表示我们希望接受消息的长度,有趣的是,这个长度对于gen_udp来说是被忽略的(没用);gen_tcp也有一个类似的函数,在那里面,这个参数才会生效。

不管怎么,如果我们不发消息给这个进程,recv/2就会永远等待不返回;

现在我们去第二个shell里面给第一个进程发一个新的消息看看:

3> gen_udp:send(Socket, {127,0,0,1}, 8789, "hey there!").ok

第二个shell里面就打印出

{ok,{{127,0,0,1},8790,<<"hey there!">>}}

如果你不想一直阻塞在这里面:你可以使用

8> gen_udp:recv(Socket, 0, 2000).{error,timeout}

以上几乎是大部分的UDP内容了,真的只有这么多,不骗你哦!


TCP Sockets

TCP sockets的大部分接口都和UDP sockets相似,但他们的工作原理是非常不一样的,其中最大的一点就是客户端和服务器是完全不同的2个东西,一个客户端行为如下:

A diagram similar to the UDP one: connection leads to send and receive, which both send to each other. More over, all states can then lead to the closed state

而服务器则是下面这种模式:

Diagram similar to the UDP one, although a listen state is added before the whole thing. That state can either move on to the 'accept' state (similar to 'open socket' for the possible branches) or to a close state.

huh?, 看上去很怪异么?客户端表现有点类似于gen_udp:你连接一个端口,收发信息,然后关闭socket;

对于服务器端,我们加入一个新的模式:listening。这是因为TCP是通过建立会话来工作的。

首先,我们要新开一个shell终端,使用 gen_tcp:listen(Port, Options),监听:

1> {ok, ListenSocket} = gen_tcp:listen(8091, [{active,true}, binary]).{ok,#Port<0.661>}

这个监听的端口负责管理使用connection连接上来的请求,你可以看到我们使用了类似于gen_udp上面的参数,其实大部分的选项对于IP sockets都是相似的,TCP有一些特殊的选项:

a connection backlog ({backlog, N}), keepalive sockets ({keepalive, true | false}), packet packaging ({packet, N},N指每一个packet的头在解析时会被自动去掉……

一旦监听socket打开,任何进程都可以使用这个端口,进入到一个accepting状态,阻塞在这里面直到某客户端上来和它说点什么。

2> {ok, AcceptSocket} = gen_tcp:accept(ListenSocket, 2000).** exception error: no match of right hand side value {error,timeout}3> {ok, AcceptSocket} = gen_tcp:accept(ListenSocket).** exception error: no match of right hand side value {error,closed}

哇,我们timeout后崩溃掉了,这个监听的socket被关闭掉了,那么我们得重新来过!这次把超时设置为2s(2000ms)

4> f().ok5> {ok, ListenSocket} = gen_tcp:listen(8091, [{active, true}, binary]).{ok,#Port<0.728>}6> {ok, AcceptSocket} = gen_tcp:accept(ListenSocket).

这个进程已阻塞在等待交流。非常好!那么我们再开一个shell终端:

1> {ok, Socket} = gen_tcp:connect({127,0,0,1}, 8091, [binary, {active,true}]).{ok,#Port<0.596>}

这个和上面gen_udp的选项相同,在最后面加了一上Timeout选项,如果你不加,就会一直等待,回头看看第一个Shell里面,他返回的是{ok,SocketNumber}.从这以后,这个accept socket和客户端的socket就可以一对一地通讯了,类似于gen_udp.使用第二个Shell给第一个Shell发信息:

3> gen_tcp:send(Socket, "Hey there first shell!").ok

你可以在第一个shell终端上看到:

7> flush().Shell got {tcp,#Port<0.729>,<<"Hey there first shell!">>}ok

两边的sockets都可以用同样的方式发消息,也可以使用gen_tcp:close(Socket)来关闭socket.不过要注意:关闭一个accept socket只会关闭本身的socket;关闭一个listen socket就会关闭自身和与之建立连接的accept sockets,如果关闭后再去连接就会返回{error,closed}.

这就是Erlang中TCP sockets的大部分内容,你信么?

虽然以上是大部分内容,但是还是有很多要注意的,如果你已亲自体验了上面这些sockets,或许会发现里面有关于sockets的所属权问题。

By this, I mean that UDP sockets, TCP client sockets and TCP accept sockets can all have messages sent through them from any process in existence, but messages received can only be read by the process that started the socket:

我还想说明:UDP sockets ,TCP 客户端和TCP accept sockets都可以被任何进程用于发消息,但是收消息只能被启动的它的进程所收到:

image

这一点都不实用?这意味着我们必须要保持启动sockets的进程一直存在,可不可以搞得更加灵活一点呢?

   
     1.  Process A starts a socket     2.  Process A sends a request     3.  Process A spawns process B        with a socket     4a. Gives ownership of the      4b. Process B handles the request        socket to Process B     5a. Process A sends a request   5b. Process B Keeps handling                                        the request     6a. Process A spawns process C  6b. ...         with a socket         ...

 

进程A负责管理(running a bunch of queries),但是每个新启动的进程会负责等待回应,处理对应的消息。A把任务分派给新的进程是非常明智!关键在于怎么把socket的所有权(ownership)也转移给新的进程:

gen_tcp,gen_udp都可以使用controlling_process(Socket,Pid)来转移sokcet的所有权。调用这个函数会告诉Erlang:”我不想再管这个socket了,把这socket给Pid来管理吧,我退出!”

从这以后,Pid就可以使用此socket来收发信息啦!That's it.


接下来,还讲了inet,什么的,
看到这里,总觉得原英文写得有趣生动,翻译过来,又生硬,又渣,完全没有勇气再糟蹋下去了。所以强烈推荐你还是看原文吧:http://learnyousomeerlang.com/buckets-of-sockets
0 0
原创粉丝点击