58-基于 UDP 协议的回射服务器

1. UDP 协议概述

UDP 协议比 TCP 协议要简单的多,它是一种无连接的协议。无连接,意味着各个报文没有顺序性,你可能先发了一个数据包 A,后发一个数据包 B,结果对方却先收到 B,后收到 A.

UDP 协议首部也非常简单,如图 1.


这里写图片描述
图1 UDP 首部

UDP 协议不提供可靠性,这意味着不能保证对端一定能收到数据。

2. 本文实验所用的程序路径

代码托管在 gitos 上,请使用下面的命令获取:

git clone https://git.oschina.net/ivan_allen/unp.git

如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/echo_udp/basic

3. 基于 UDP 协议的回射服务器

  • 编写 UDP 程序时,并没有像 TCP 那样建立连接的过程,只要知道对方绑定的套接字地址,就可以直接将数据发送过去。
  • 在创建套接字时,第二个参数需要指定 SOCK_DGRAM,第三个参数是 0.
  • 无论是服务器还是客户端,都需要绑定套接字地址。对于服务器来说,需要的显式的绑定套接字地址。

下面是回射服务器的伪代码,我们可以看见,它比基于 TCP 的回射服务器要简洁的多,但是,它也更加不可靠。谁也不能保证客户端发送的 udp 报文一定会被服务器收到,也不能保证服务器处理完的数据一定会被客户端收到。

具体的代码请参考 unp/program/echo_udp/basic/echo.cc.

3.1 服务器程序伪代码

void server() {
  int sockfd, len, nr, nw;
  char buf[4096];
  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  servaddr = resolve(ip, port);
  bind(sockfd, &servaddr, sizeof(servaddr));
  while(1) {
    // 接收客户端发来的数据
    len = sizeof(cliaddr);
    nr = recvfrom(sockfd, buf, 4096, 0, &cliaddr, &len);
    // process(buf, nr); 处理数据
    // 发送回去
    nw = sendto(sockfd, buf, nr, 0, &cliaddr, len);
  }
}

3.2 客户端伪代码

void server() {
  int sockfd, len, nr, nw;
  char buf[4096];
  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  servaddr = resolve(ip, port);

  while(1) {
    // 从标准输入接收数据
    fgets(buf, 4096, stdin);
    // 发送给服务器
    nw = sendto(sockfd, buf, strlen(buf), 0, &servaddr, sizeof(servaddr));
    // 接收数据
    len = sizeof(cliaddr);
    nr = recvfrom(sockfd, buf, 4096, 0, NULL, NULL);
    // 将数据写到标准输出
    puts(buf);    
  }
}

4. recvfrom 和 sendto

  • 函数原型
// recvfrom 接收任何发给自己的数据报
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

// sendto 发送数据报给指定的套接字地址。如果当前 sockfd 还未绑定套接字地址,sendto 还会默认为其分配一个 ip:port 进行绑定
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);                        
  • 参数

这两个函数和 read 和 write 非常像,不过它们多了三个参数。前三个参数和 read、write 是一样的,不同的在于后三个参数。

参数解释
flags现在我们不介绍,以后我们会将再解释,当前我们写 UDP 程序,这个参数写 0 即可
dest_addr指向一个含有数据报接收者的套接字地址,大小由 addrlen 指定
src_addr指向一个数据报发送者的套接字地址,这个地址在 recvfrom 函数返回时自动填写

特别注意的是 recvfrom 的 addrlen 参数是一个指针,它既是传入参数,也是传出参数。

  • 返回值

sendto 返回值表示实际写入的字节数据

recvfrom 返回值表示实际接收到的字节数。

两个函数返回 -1 表示出错。

  • 要注意的地方

1) sendto 可以发送长度为 0 的数据报,这意味着 UDP 只会发送 8 字节的 UDP 首部,而没有数据。

2) recvfrom 也可以返回 0,它不像 TCP,read 返回 0 表示对端发送了 FIN。UDP 是无连接的,因此也没有关闭 UDP 连接之类的事情。

5. 程序运行结果

很开心新增了一台新主机 mars!今天就用它来当服务器,试试水。

  • 在 mars 上启动服务器
./echo -s -h mars
  • 在 sun 上启动客户端
// 表示连接主机 mars
./echo -h mars


这里写图片描述
图2 运行结果

左右是客户端,右侧是服务器。服务器打点是为了提示我们它收到数据了,并没有其它目的。


这里写图片描述
图3 tcpdump 输出

从图 3 中我们看到,客户端与服务器并没有任何建立连接的过程。

6. 总结

  • 掌握基本的 UDP 编程方法
  • 掌握 recvfrom 和 sendto 基本的用法

思考:客户端是在什么时候绑定套接字地址的?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值