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

来源:互联网 发布:汉服淘宝店平价学生党 编辑:程序博客网 时间:2024/05/19 15:42


参见 :http://learnyousomeerlang.com/buckets-of-sockets

为了加深理解,自译如下,若理解有误或更好的建议,请帮忙指出, :)

Buckets of Sockets

目前为止,我们做了一些关于Erlang本身很有趣的事,但是却极少与外面的世界交互,最多也就是从某个地方读写文件。

尽管和自己的联系越多越有趣,但是,现在是时候走出舒适区,来一起谈谈剩下的世界了。

这一章会包含三个使用sockets部件:IO lists,UDP socketsTCP sockets. IO lists并不算严格意义的一个主题,IO lists只是

使发送字符串到其它sockets或其它Erlang drivers更加效率。


IO Lists

我在前面部分提到过:我们可以使用字符串(整数的列表)或二进制(使用二进制数据结构来保存数据)来处理文本, 使用”Hello World”或<<”Hello World”>>发送信息,会得到相类似的符号,类似的结果。

不同在于:你是如何组织信息:一个字符串就是一个整数的列表,每一个字母都映射到列表中的一个元素,如果你想在列表中间或结尾增加一个元素,你不得不遍历到你要增加的元素位置,这并不是你想要的,但是:

A = [a]B = [b|A] = [b,a]C = [c|B] = [c,b,a]

在上面这个例子中,A,B,C都是不需要重写(复制)的,C等价于[c,b,a],[c|B],或[c,|[b|[a]]],在最后,[c,|[b|[a]]],里面的[a]等价于A,[C|B]里面的B就是第二行的B.那么我们来看看下面这需要复制的有什么不同?

A = [a]B = A ++ [b] = [a] ++ [b] = [a|[b]]C = B ++ [c] = [a|[b]] ++ [c] = [a|[b|[c]]]

你看出来上面赋值时发生的重写(rewrite)了么?

当我们创建B时,必须要去rewrite A;当创建C时,必须要rewrite B(包括[a]),如果我们再在用类似的方法创建D,我们不得不rewrite C…

所以操作很长的字符串是非常没有效率的做法,同时还会创建大量的垃圾数据给Erlang VM处理.

但是使用二进制就不会这么糟糕:

A = <<"a">>B = <<A/binary, "b">> = <<"ab">>C = <<B/binary, "c">> = <<"abc">>

在上面这个情况下:binaries知道自己数据的长度,所以增加一个元素消耗是固定的,这比lists好多了,同时binaries非常紧密(compact,节省空间),由于这2个原因,我们以后会坚持使用binaries来处理文本(text).

当然也有一些缺点,binaries意味着处理数据只有这一种方式,但操作binaries(修改或分离)还是会有一些消耗,因此我们会在代码里面个别的使用string与binaries的转换。但非常不推荐频繁转换这2个类型。

在上面的情况里,IO lists是我们的救世主,但IO lists也是数据结构里面非常奇怪的一种类型,他们是字节,(整数0~255),binaries,或其它IO lists 组成的一个”列表“。这就意味着函数可以接受Io lists 也可以

接受如[H, e, [$l, <<"lo">>, " "], [[["W","o"], <<"rl">>]] | [<<"d">>]]的格式,当真的遇到这种情况时,Erlang VM 会把list 扁平化成<<”Hello World”>> .

什么样的函数可以处理IO lists? 可以查看

1. 相关模块 io, file;2. TCP 和UDP sockets也可以处理;3. 一些库函数也可,比如一些使用unicode 或者 正则表达式 re 的,都可以处理他们。

你可以使用下面语句来做试试:

IoList = <<"Hello World">>, io:format("~s~n", [IoList]).

总的来说:使用binaries来替换string,可以避免数据结构改变时产生大量地动态创建垃圾。


TCP and UDP: Bro-tocols

 UDP protocol 是我们要谈到的第一种socket,基于UDP协议,UDP是基于协议IP层,提供类似于端口少量抽象接口。UDP被认为是一个缺少维护状态的协议。从UDP端口得到的数据会被分割成从多小块,没有标记,没有会话标识,不能保证你收到的数据和发送过来的数据是一致的。

实际中,别人发一个packet给接收者,接收者有可能收不到这个packet,由于以上原因,人们倾向于在packets很小,丢失一小部分packets也没关系,且没有太多的复杂的数据交换的场景使用UDP(最典型就是视频下载啦)

与UDP相对的还有保证送达机制的TCP协议,TCP会负责处理那些丢弃的packets,重新发送它们,使用独立的会话来保证多对发送和接收者。

TCP为保证可靠的信息传递,不得不牺牲了效率,变得比UDP传输慢且数据冗余大;UDP快,便是不可靠;所以请根据你的具体场景选择使用哪一个。

无论如何,在Erlang使用UDP非常简单,我们只需要指定端口并设定一个socket,那么这个socket就可以收发数据;

image

For a bad analogy, this is like having a bunch of mailboxes on your house (each mailbox being a port) and receiving tiny slips of paper in each of them with small messages. They can have any content, from "I like how you look in these pants" down to "The slip is coming from inside the house!". When some messages are too large for a slip of paper, then many of them are dropped in the mailbox. It's your job to reassemble them in a way that makes sense, then drive up to some house, and drop slips after that as a reply. If the messages are purely informative ("hey there, your door is unlocked") or very tiny ("What are you wearing? -Ron"), it should be fine and you could use one mailbox for all of the queries. If they were to be complex, though, we might want to use one port per session, right? Ugh, no! Use TCP!

一个不怎么恰当的类比:这就像你的房子边上有一大堆的信箱(相当于端口),每个端口可以用于接受很多小容量信件,这些信件可以携带任何内容,比如:“我喜欢你看裤子的样子”,“这纸片是来自房子内部的!”… 如果一些消息太大,会被切割为多个信件放到信箱中,你的职责就是使用有意义的规则分配它们,并把它们放到对应的房子里面作为一个回应。如果一个信息非常有用(hey,你的门没锁)或者非常小(Ron,你今天穿了什么)时,你可以使用一个信箱来处理所有的信息,但是如果信息非常复杂,我们可能需要对每组会话都使用专门的一个端口,这样显然非常丑陋!这时你应该用TCP.

TCP协议是有状态的(stateful),基于连接(connection-based)的一种协议.在发出信息之前,你必须要先握手,这意味着:某人想发信件到对应的信箱时,必须要先打个招呼“hey,我是IP:94.25.12.37,可以聊会么?”然后你应答”当然,把你的消息用数字N打上一个标签,每发一句就递增这个数字N”.从这以后,只要你想和IP92.25.12.37互相交流的内容,都有可能把检查到对方没有收的内容,对些进行重新发送。

这种方式,我们就可以使用一个信箱(端口)来保持交流,这对于TCP来说是一件非常优雅的事,虽然会增加一些负担,便是保证了所有的信息都按顺序被合更的分配到对应的地方。

如果你对以上的分析不感兴趣,不要太失望,我们马上就会讨论怎么在Erlang里使用TCP和UDP scokets,这些都很简单。


UDP Sockets

以下有一些基本的UDP操作:

复制代码
1. 建立socket(setting up a socket);2. 发信息(sending mesasge);3. 收信息(receiving message );4. 关闭连接(closeing a connection);
复制代码

A graph showing that Opening a socket can lead to 3 options: sending data, receiving data, or closing a socket. Sending can lead to receiving data or closing a socket, receiving data can lead to sending data or closing a socket. Finally, closing a socket does nothing

第一个操作,不管你要干什么,首先你要使用 gen_udp:open/1-2打开一个socket,最简单的示例如下:

{ok, Socket} = gen_udp:open(PortNumber).

PortNumer取值范围1~65535:

%%0~1023被认为是系统端口,大多数情况下,你的操作系统会不让你监听系统端口,除非你给了相应的权限;%%1024~49151是注册端口,通常这些端口都没有被限制,可以自由使用;(有时不通常是因为 registered to well known services);%%49152~65535就是动态或私有(dynamic or private)端口,他们常用于短暂使用( ephemeral ports);

下面我们的实验使用的是一些安全未被使用过的端口,比如:8789.

但是在此这前要说明一点:如果你使用 gen_udp:open/2,这里的第二个参数是一个选项的列表,比如会指定我们要接收的数据是列表还是二进制;我们接收方式{active, true}还是{active, false};

还有一些选项指定连接网络类型是IPv4或IPv6;可不可以使用UDP的socekt进行广播({broadcast, true | false}), 缓冲区的大小等等;

还有非常多有用的选项可以使用,但是我们此次实际只用一个简单的连接来做,其它部分你要去学习TCP,UDP协议后再自己折腾吧。

0 0
原创粉丝点击