网络编程-003-UDP-socket

UDP 协议的特点

  • UDP 不要求保持一个连接
  • UDP 没有因接收方检查数据包 (或当数据包没有正确抵达而自动重传) 而带来的开销
  • 设计UDP的目的是用于短应用和控制消息
  • UDP要求的网络带宽比 TCP 更小

使用 UDP 协议的场合

  • 传送的数据对顺序和可靠性要求不高
  • 简单的 发送 — 接收 一次通信形式
  • 传输的数据量较少
  • 发送广播或多播信息

UDP 和 TCP 的区别

  • TCP 面向连接, 完整的 “流”
  • UDP 无连接, 独立的报文

UDP-socket 编程模型1

UDP-socket 编程模型1

特点
  • 应用程序双方是对等的, 通信时都经过 6 个阶段
  • 双方必须确切地知道对方的网络地址, 且将约定好的自己的网络地址绑定到自己的 socket 上
  • 每次发送或接受数据报时, 所使用的 sendto( 和 recvfrom( 中要包括对方的网络地址信息
  • recvfrom( 在没有收到数据时, 默认情况下会阻塞, 程序不向下执行

UDP-socket 编程模型2

UDP-socket 编程模型2

特点
  • 应用程序程序双方不对等, 服务器要先行启动, 处于被动地等待访问状态, 客户端则可随时主动请求访问服务器
  • 客户端不需要绑定 socket
  • 服务器将 socket 绑定到周知的端口或指定的端口, 且客户端必须确切地知道服务器 socket 使用的网络地址
  • 客户端 socket 使用动态分配的自由端口, 不需要进行绑定, 服务器事先不必知道客户端 socket 的网络地址
  • 客户端必须先发送数据报, 服务器收到后才能知道客户端的地址, 才能给客户端会送数据报

sendto(

int
WSAAPI
sendto(
    _In_ SOCKET s,
    _In_reads_bytes_(len) const char FAR * buf,
    _In_ int len,
    _In_ int flags,
    _In_reads_bytes_(tolen) const struct sockaddr FAR * to,
    _In_ int tolen
);
  • to 指定对端的地址结构
  • tolen 指定对端的地址结构长度
  • 返回值
    • 成功: 返回实际发送的字节数
    • 失败: 返回 SOCKET_ERROR

recvfrom(

int
WSAAPI
recvfrom(
    _In_ SOCKET s,
    _Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
    _In_ int len,
    _In_ int flags,
    _Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from,
    _Inout_opt_ int FAR * fromlen
    );
  • from 接收发过来的地址结构
  • 返回值
    • 成功: 返回实际接收的字节数
    • 连接已终止: 0
    • 失败: SOCK_ERROR

发送和接收 0 字节

  • TCP
    • 可以发送 0 字节报文
    • 若接收到 0 字节, 意味着连接被优雅关闭
  • UDP
    • 可以发送 0 字节报文
    • 若接收到 0 字节, 意味着报文长度为 0 并关闭连接
  • recvfrom( 和 sendto( 也可以用在 TCP 中
    • 此时, 接收到 0 字节意味着连接被优雅关闭
    • 一般不这样用

示例

sendto(

    SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in localaddr;
    memset((void *)&localaddr, 0, sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    localaddr.sin_port = htons(0);

    bind(s, (struct sockaddr *)&localaddr, sizeof(localaddr));

    struct sockaddr_in peeraddr;
    memset((void *)&peeraddr, 0, sizeof(peeraddr));
    peeraddr.sin_family = AF_INET;
    peeraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    peeraddr.sin_port = htons(8888);

    sendto(s, "Hello", 6, 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr));

recvfrom(

    s = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in localaddr;
    memset((void *)&localaddr, 0, sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    localaddr.sin_port = htons(8888);

    bind(s, (struct sockaddr *)&localaddr, sizeof(localaddr));

    char buf[32];
    recvfrom(s, buf, 32, sizeof(buf), NULL, 0);

setsockopt( 设置 socket 选项

int
WSAAPI
setsockopt(
    _In_ SOCKET s,
    _In_ int level,
    _In_ int optname,
    _In_reads_bytes_opt_(optlen) const char FAR * optval,
    _In_ int optlen
    );
  • level 是被设置的选项的级别, 可选 SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP, IPPROTO_IPv6
  • optname 是准备设定的选项, 其取值取决于 level
  • optval 指向存放选项值的缓冲区
  • optlen optval 缓冲区长度
  • 在 SOL_SOCKET level 上 optname 取值
    • SO_DEBUG, 打开或关闭调试信息
    • SO_REUSEADDR, 打开或关闭地址复用功能
    • SO_KEEPALIVE, socket 保活
    • SO_DONTROUTE, 打开或关闭路由查找功能
    • SO_BROADCAST, 允许或禁止发送广播数据
    • SO_LINGER, 如果选择此选项, close 或 shutdown 将 等到所有 socket 队列里的消息成功发送或到达延时时间后才会返回, 否则, 调用将立刻返回
    • SO_DONTLINGER
    • SO_OOBINLINE, 紧急数据放入普通数据流

利用 socket 实现 UDP协议的广播通信

  • 创建UDP- socket, 只有 UDP-socket支持广播通信
  • 绑定 socket 于指定的地址和端口
  • 通过 socket 选项设置广播属性
  • 通过 sendto( 发送广播信息, 发送地址为 INADDR_BROADCASE
  • 通过 recvfrom( 接收广播消息

让 UDP 广播数据

BOOL optval = true;
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&optval, sizeof(BOOL));

利用 socket 实现 UDP协议的组播通信

  • 创建 UDP-socket
  • 绑定 socket 于指定的地址和端口
  • 通过 socket 选项设置组播属性
  • 通过 sendto( 发送组播信息
  • 通过 recvfrom( 接收组播消息
  • 组播地址
    • 224.0.0.0224.0.0.255 224.0.0.0 — 224.0.0.255 被 IANA 保留为网络协议使用
    • 244.0.0.1 244.0.0.1 全主机组 244.0.0.2 244.0.0.2 全多播路由器组 244.0.0.5 244.0.0.5 全OSPF路由器组
    • 224.0.1.0238.255.255.255 224.0.1.0 — 238.255.255.255 是公用的组播地址, 可以用于Internet上
    • 239.0.0.0239.255.255.255 239.0.0.0 — 239.255.255.255 是私有地址, 供各个内部网在内部使用, 这个地址的组播不能上公网
  • IP 多播组的加入和离开用 setsockopt( 完成

    • IP_ADD_MEMBERSHIP 加入组
    • IP_DROP_MEMBERSHIP 脱离组
    • 使用时要传递一个 ip_mreq 结构

      struct ip_mreq {
      struct in_addr imr_multiaddr;
      struct in_addr imr_interface;
      }
      setsocket(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&ipmr, &len);
      setsocket(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&ipmr, &len);

      多播的 TTL (生存时间) 设置

int optval = 3;
setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)optval, sizeof(int)); 

多播的 禁止回环设置

int optval = 0;
setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,   (char *)optval, sizeof(int));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值