现在我们看看UDP协议(User Datagram Protocol,用户数据报协议)。使用UDP,互联网上的机器之间可以互相发送小段的数据,叫做数据报。UDP数据报是不可靠的,这意味着如果客户端发送一系列的UDP数据报到服务器,收到的数据报顺序可能是错误的。不过收到的数据报肯定是正确的。大的数据报会被分为多个小的分片,IP协议负责重新组装这些分片,并最终交付给应用。

UDP是无连接的协议,这意味着客户端无需连接服务器即可发送消息。这也意味着程序更加适于大量客户端收发小的消息报文。

在Erlang中编写UDP客户端和服务器比TCP时更简单,因为我们无需管理连接。

1   简单的UDP服务器和客户端

首先,我们看看服务器,一个通用的服务器样式如下:

  1. server(Port) ->
  2. {ok,Socket} = gen_udp:open(Port,[binary]),
  3. loop(Socket).
  4.  
  5. loop(Socket) ->
  6. receive
  7. {udp,Socket,Host,Port,Bin} ->
  8. BinReply = ... ,
  9. gen_udp:send(Socket,Host,Port,BinReply),
  10. loop(Socket)
  11. end.

这里比TCP协议的例子更简单,因为我们至少不需要关心连接关闭的消息。注意我们以二进制方式打开socket,驱动也会以二进制数据的形式将报文发送到应用。

注意客户端。这里有个简单的客户端。它仅仅打开UDP socket,发送消息到服务器,等待响应(或超时),然后关闭socket并返回从服务器接收到的值。

  1. client(Request) ->
  2. {ok,Socket} = gen_udp:open(0,[binary]),
  3. ok = gen_udp:send(Socket,"localhost",4000,Request),
  4. Value = receive
  5. {udp,Socket,_,_,Bin} ->
  6. {ok,Bin}
  7. after 2000 ->
  8. error
  9. end,
  10. gen_udp:close(Socket),
  11. Value

我们必须拥有一个超时,否则UDP的不可靠会让我们永远得不到响应。

2   一个UDP阶乘服务器

我们可以很容易的构造一个UDP的阶乘服务器。代码模仿前一节。

  1. -module(upd_test).
  2. -export([start_server/0,client/1]).
  3.  
  4. start_server() ->
  5. spawn(fun() -> server(4000) end).
  1. %% 服务器
  1. server(Port) ->
  2. {ok,Socket}=gen_udp:open(Port,,[binary]),
  3. io:format("server opened socket:~p~n",[Socket]),
  4. loop(Socket).
  5.  
  6. loop(Socket) ->
  7. receive
  8. {udp,Socket,Host,Port,Bin} =Msg ->
  9. io:format("server received:~p~n",[Msg]),
  10. N=binary_to_term(Bin),
  11. Fac=fac(N),
  12. gen_udp:send(Socket,Host,Port,term_to_binary(Fac)),
  13. loop(Socket)
  14. end.
  15.  
  16. fac(0) -> 1;
  17. fac(N) -> N*fac(N-1).
  1. %% 客户端
  1. client(N) ->
  2. {ok,Socket} = gen_upd:open(0,[binary]),
  3. io:format("client opened socket=~p~n",[Socket]),
  4. ok=gen_udp:send(Socket,"localhost",4000,term_to_binary(N)),
  5. Value=receive
  6. {udp,Socket,_,_,Bin}=Msg ->
  7. io:format("client received:~p~n",[Msg]),
  8. binary_to_term(Bin)
  9. after 2000 ->
  10. 0
  11. end,
  12. gen_udp:close(Socket),
  13. Value

注意我增加了一些打印语句,所以我们可以看到程序执行的过程。我一般是开发阶段加很多打印语句,而在工作正常后就注释掉了。

现在让我们运行例子,首先启动服务器:

  1. 1> udp_test:start_server().
  2. server opened socket:#Port<0.106>
  3. <0.34.0>

这会在后台运行,所以我们发出一个客户端请求:

  1. 2> udp_test:client(40).
  2. client opened socket=#Port<0.105>
  3. server received:{udp,#Port<0.106>,{127,0,0,1},32785,<<131,97,40>>}
  4. client received:{udp,#Port<0.105>,
  5. {127,0,0,1},4000,
  6. <<131,110,20,0,0,0,0,0,64,37,5,255,
  7. 100,222,15,8,126,242,199,132,27,
  8. 232,234,142>>}
  9. 815915283247897734345611269596115894272000000000

3   UDP的附加注释

我们必须注意的是UDP是无连接的协议,也就四海服务器无法拒绝客户端发送数据,甚至不知道客户端是谁。

大个的UDP报文会被切分成多个分片分别在网络上传输。分片发生在数据报长度大于最大传输单元(MTU)时,以确保通过路由器等网络设备以后仍然可以到达。一般的测量方法是开始于一个足够小的包(比如500字节),然后逐渐增加,直到发现MTU为止。如果在某一点发现数据报被丢弃了,那么,你就直到可以传输的最大报文长度了。

一个UDP数据报可以被传输两次,所以你必须小心的编码以防备这个事。因为他可能会对同一个请求的第二次出现而再做一次响应。想要防止,我们可以修改客户端代码来在每个请求中加一个唯一引用,并且检查响应中的这个唯一引用。想要生成一个唯一引用,我们可以用Erlang BIF的 make_ref ,就会生成一个全局唯一引用。远程过程调用现在可以这样写:

  1. client(Request) ->
  2. {ok,Socket} = gen_udp:open(0,[binary]),
  3. Ref=make_ref(),
  4. B1=term_to_binary({Ref,Request}),
  5. ok=gen_udp:send(Socket,"localhost",4000,B1),
  6. wait_for_ref(Socket,Ref).
  7.  
  8. wait_for_ref(Socket,Ref) ->
  9. receive
  10. {udp,Socket,_,_,Bin} ->
  11. case binary_to_term(Bin) of
  12. {Ref,Val} ->
  13. Val;
  14. {_SomeOtherRef,_} ->
  15. wait_for_ref(Socket,Ref)
  16. end;
  17. after 1000 ->
  18. ...
  19. end.

ps:这里它相当于加上了个ref的唯一值,去检查clinet返回响应的做校验.

Socket的UDP协议在erlang中的实现的更多相关文章

  1. socket之UDP协议,并发编程介绍,操作系统发展史

    socket之UDP协议 1.UDP协议 UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection 参考 ...

  2. C#的Socket实现UDP协议通信

    今天稍花化了一点时间,利用C#的Socket验证了UDP的通信,为接下来特地利用UDP做个分布式的通信仿真系统打下基础.众所周知,UDP 就是用户数据报协议,在互联网参考模型的第四层——传输层.与TC ...

  3. 网络Socket编程UDP协议例子

    服务端代码 public class UDPChatServer { //通讯端口 private Integer port=8000; //数据报文的通讯通道对象 private DatagramC ...

  4. Java的socket服务UDP协议

    练习1 接收类 package com.socket.demo; import java.io.IOException; import java.net.DatagramPacket; import ...

  5. 通过socket和Udp协议简单实现一个群体聊天工具(控制台)

    编写一个聊天程序.有收数据的部分 和 发数据的部分.这两个部分需要同时执行,这就用到多线程技术,一个线程负责收,一个现象负责发. 因为收和发动作是不一致的,所以要定义两个run方法而且这两个方法要封装 ...

  6. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

  7. socket、tcp/ip协议、udp协议

    socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Un ...

  8. Python进阶----UDP协议使用socket通信,socketserver模块实现并发

    Python进阶----UDP协议使用socket通信,socketserver模块实现并发 一丶基于UDP协议的socket 实现UDP协议传输数据 代码如下:

  9. 局域网内通过UDP协议进行传输接受数据——AsyncUdpSocket

    在相同的局域网内,可以通过Udp协议进行数据的传输和接收,Udp协议与Http协议不同,Udp更加方便快捷,省去了很多步骤,但是也有很多传输问题,在局域网内小范围传输数据时Udp还是非常能够胜任的. ...

随机推荐

  1. 利用canvas裁剪想要的图片

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. 从头实现一个koa框架

    koajs是最流行的nodejs后端框架之一,有很多网站都使用koa进行开发,同时社区也涌现出了一大批基于koa封装的企业级框架.然而,在这些亮眼的成绩背后,作为核心引擎的koa代码库本身,却非常的精 ...

  3. Scala不使用null 而使用Option,None,Some的好处

    刚接触Scala时就很奇怪, 为什么Java已经有null了,却偏偏还要弄出个None 后来依然我行我素在Scala里使用null, 结果就是经常被NullPointerException折磨得阴魂不 ...

  4. LeetCode OJ-- Generate Parentheses *

    https://oj.leetcode.com/problems/generate-parentheses/ 输入n产生n个( ,n个 )组成的所有合法的括号组合. 现在纸上画画,找到规律: 1.每一 ...

  5. python 编码问题之终极解决

    结合之前遇到的坑以及下面贴的这篇文章, 总结几种python乱码解决方案,如果遇到乱码,不妨尝试一下? 1,必备 #encoding=utf-8 2, python编程环境编码 import sys ...

  6. Codeforces 161D Distance in Tree(树型DP)

    题目链接 Distance in Tree $k <= 500$ 这个条件十分重要. 设$f[i][j]$为以$i$为子树,所有后代中相对深度为$j$的结点个数. 状态转移的时候,一个结点的信息 ...

  7. POJ 1239 Increasing Sequences [DP]

    题意:略. 思路:进行两次dp. 第一次dp从前向后,用dp[x]表示从第x位向前dp[x]位可构成一个数字,且与前面的数组符合题意要求.最后求的dp[n]即为最后一个数字的长度. 而题目还有要求,所 ...

  8. TiKV 源码解析系列——Placement Driver

    https://zhuanlan.zhihu.com/p/24809131?refer=newsql

  9. css :before 和 :after

    :before p:before 在每个 <p> 元素的内容之前插入内容. 2 :after p:after 在每个 <p> 元素的内容之后插入内容. 2 <!DOCTY ...

  10. http://blog.csdn.net/LANGXINLEN/article/details/50421988

    GitHub上史上最全的Android开源项目分类汇总 今天在看博客的时候,无意中发现了 @Trinea在GitHub上的一个项目 Android开源项目分类汇总, 由于类容太多了,我没有一个个完整地 ...