libuv 中文编程指南(四)网络

网络

libuv 的网络接口与 BSD 套接字接口存在很大的不同, 某些事情在 libuv 下变得更简单了, 并且所有接口都是都是非阻塞的, 但是原则上还是一致的. 另外 libuv 也提供了一些工具类的函数抽象了一些让人生厌的, 重复而底层的任务,比如使用 BSD 套接字结构来建立套接字, DNS 查询, 或者其他各种参数的设置.

libuv 中在网络 I/O 中使用了 uv_tcp_tuv_udp_t 两个结构体.

TCP

TCP 是一种面向连接的流式协议, 因此是基于 libuv 的流式基础架构上的.

服务器(Server)

服务器端的 sockets 处理流程如下:

  1. uv_tcp_init 初始化 TCP 监视器.
  2. uv_tcp_bind 绑定.
  3. 在指定的监视器上调用 uv_listen 来设置回调函数, 当有新的客户端连接到来时, libuv 就会调用设置的回调函数.
  4. uv_accept 接受连接.
  5. 使用 stream operations 与客户端进行通信.

以下是一个简单的 echo 服务器的例子:

int main() {loop = uv_default_loop();uv_tcp_t server;uv_tcp_init(loop, &server);struct sockaddr_in bind_addr = uv_ip4_addr("0.0.0.0", 7000);uv_tcp_bind(&server, bind_addr);int r = uv_listen((uv_stream_t*) &server, 128, on_new_connection);if (r) {fprintf(stderr, "Listen error %s\n", uv_err_name(uv_last_error(loop)));return 1;}return uv_run(loop, UV_RUN_DEFAULT);
}

你可以看到辅助函数 uv_ip4_addr 用来将人为可读的字符串类型的 IP 地址和端口号转换成 BSD 套接字 API 所需要的 struct sockaddr_in 类型的结构. 逆变换可以使用 uv_ip4_name 来完成.

对于 IPv6 来说应该使用 uv_ip6_* 形式的函数.

大部分的设置(setup)函数都是普通函数, 因为他们都是 计算密集型(CPU-bound), 直到调用了 uv_listen 我们才回到 libuv 中回调函数风格. uv_listen 的第二个参数 backlog 队列长度 – 即连接队列最大长度.

当客户端发起了新的连接时, 回调函数需要为客户端套接字设置一个监视器, 并调用 uv_accept 函数将客户端套接字与新的监视器在关联一起. 在例子中我们将从流中读取数据.

void on_new_connection(uv_stream_t *server, int status) {if (status == -1) {// error!return;}uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));uv_tcp_init(loop, client);if (uv_accept(server, (uv_stream_t*) client) == 0) {uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read);}else {uv_close((uv_handle_t*) client, NULL);}
}

剩余部分的函数与上一节流式例子中的代码相似, 你可以在例子程序中找到具体代码, 如果套接字不再使用记得调用 uv_close 关闭该套接字. 如果你不再接受连接, 你可以在 uv_listen 的回调函数中关闭套接字.

客户端(Client)

在服务器端你需要调用 bind/listen/accept, 而在客户端你只需要调用 uv_tcp_connect. uv_tcp_connect 使用了与 uv_listen 风格相似的回调函数 uv_connect_cb 如下:

uv_tcp_t socket;
uv_tcp_init(loop, &socket);uv_connect_t connect;struct sockaddr_in dest = uv_ip4_addr("127.0.0.1", 80);uv_tcp_connect(&connect, &socket, dest, on_connect);

建立连接后会调用 on_connect.

UDP

User Datagram Protocol 提供了无连接, 不可靠网络通信协议, 因此 libuv 并不提供流式 UDP 服务, 而是通过 uv_udp_t 结构体(用于接收)和 uv_udp_send_t 结构体(用于发送)以及相关的函数给开发人员提供了非阻塞的 UDP 服务. 所以, 真正读写 UDP 的函数与普通的流式读写非常相似.为了示范如何使用 UDP, 下面提供了一个简单的例子用来从 DHCP 获取 IP 地址. – DHCP 发现.

Note

你应该以 root 用户运行 udp-dhcp, 因为该程序使用了端口号低于 1024 的端口.

uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;int main() {loop = uv_default_loop();uv_udp_init(loop, &recv_socket);struct sockaddr_in recv_addr = uv_ip4_addr("0.0.0.0", 68);uv_udp_bind(&recv_socket, recv_addr, 0);uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);uv_udp_init(loop, &send_socket);uv_udp_bind(&send_socket, uv_ip4_addr("0.0.0.0", 0), 0);uv_udp_set_broadcast(&send_socket, 1);uv_udp_send_t send_req;uv_buf_t discover_msg = make_discover_msg(&send_req);struct sockaddr_in send_addr = uv_ip4_addr("255.255.255.255", 67);uv_udp_send(&send_req, &send_socket, &discover_msg, 1, send_addr, on_send);return uv_run(loop, UV_RUN_DEFAULT);
}

0.0.0.0 地址可以绑定本机所有网口. 255.255.255.255 是广播地址, 意味着网络包可以发送给子网中所有网口, 端口 0 说明操作系统可以任意指定端口进行绑定.

首先我们在 68 号端口上设置了绑定本机所有网口的接收套接字(DHCP 客户端), 并且设置了读监视器. 然后我们利用相同的方法设置了一个用于发送消息的套接字. 并使用 uv_udp_send 在 67 号端口上(DHCP 服务器)发送 广播消息.

设置广播标志也是 必要 的, 不然你会得到 EACCES 错误 [1]. 发送的具体消息与本书无关, 如果你对此感兴趣, 可以参考源码. 若出错, 则读写回调函数会收到 -1 状态码.

由于 UDP 套接字并不和特定的对等方保持连接, 所以 read 回调函数中将会收到用于标识发送者的额外信息. 如果缓冲区是由你自己的分配的, 并且不够容纳接收的数据, 则``flags`` 标志位可能是 UV_UDP_PARTIAL. 在这种情况下, 操作系统会丢弃不能容纳的数据. (这也是 UDP 为你提供的特性).

void on_read(uv_udp_t *req, ssize_t nread, uv_buf_t buf, struct sockaddr *addr, unsigned flags) {if (nread == -1) {fprintf(stderr, "Read error %s\n", uv_err_name(uv_last_error(loop)));uv_close((uv_handle_t*) req, NULL);free(buf.base);return;}char sender[17] = { 0 };uv_ip4_name((struct sockaddr_in*) addr, sender, 16);fprintf(stderr, "Recv from %s\n", sender);// ... DHCP specific code
free(buf.base);uv_udp_recv_stop(req);
}

UDP 选项(UDP Options)

生存时间TTL(Time-to-live)

可以通过 uv_udp_set_ttl 来设置网络数据包的生存时间(TTL).

仅使用 IPv6 协议

IPv6 套接字可以同时在 IPv4 和 IPv6 协议下进行通信. 如果你只想使用 IPv6 套接字, 在调用 uv_udp_bind6 [2] 时请传递 UV_UDP_IPV6ONLY 参数.

多播(Multicast)

套接字可以使用如下函数订阅(取消订阅)一个多播组:

UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,const char* multicast_addr, const char* interface_addr,uv_membership membership);

membership 取值可以是 UV_JOIN_GROUPUV_LEAVE_GROUP.

多播包的本地回路是默认开启的 [3], 可以使用 uv_udp_set_multicast_loop 来开启/关闭该特性.

多播包的生存时间可以使用 uv_udp_set_multicast_ttl 来设置.

DNS 查询(Querying DNS)

libuv 提供了异步解析 DNS 的功能, 用于替代 getaddrinfo [4]. 在回调函数中, 你可以在获得的 IP 地址上执行普通的套接字操作. 让我们通过一个简单的 DNS 解析的例子来看看怎么连接 Freenode 吧:

int main() {loop = uv_default_loop();struct addrinfo hints;hints.ai_family = PF_INET;hints.ai_socktype = SOCK_STREAM;hints.ai_protocol = IPPROTO_TCP;hints.ai_flags = 0;uv_getaddrinfo_t resolver;fprintf(stderr, "irc.freenode.net is... ");int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.freenode.net", "6667", &hints);if (r) {fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(uv_last_error(loop)));return 1;}return uv_run(loop, UV_RUN_DEFAULT);
}

如果 uv_getaddrinfo 返回非零, 表示在建立连接时出错, 你设置的回调函数不会被调用, 所有的参数将会在 uv_getaddrinfo 返回后被立即释放. 有关 hostname, servnamehints 结构体的文档可以在 getaddrinfo 帮助页面中找到.

在解析回调函数中, 你可以在 struct addrinfo(s) 结构的链表中任取一个 IP. 这个例子也演示了如何使用 uv_tcp_connect. 你在回调函数中有必要调用 uv_freeaddrinfo.

void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {if (status == -1) {fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(uv_last_error(loop)));return;}char addr[17] = {'\0'};uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16);fprintf(stderr, "%s\n", addr);uv_connect_t *connect_req = (uv_connect_t*) malloc(sizeof(uv_connect_t));uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));uv_tcp_init(loop, socket);connect_req->data = (void*) socket;uv_tcp_connect(connect_req, socket, *(struct sockaddr_in*) res->ai_addr, on_connect);uv_freeaddrinfo(res);
}

网络接口(Network interfaces)

系统网络接口信息可以通过调用 uv_interface_addresses 来获得, 下面的示例程序将打印出机器上所有网络接口的细节信息, 因此你可以获知网口的哪些域的信息是可以得到的, 这在你的程序启动时绑定 IP 很方便.

#include <stdio.h>
#include <uv.h>int main() {char buf[512];uv_interface_address_t *info;int count, i;uv_interface_addresses(&info, &count);i = count;printf("Number of interfaces: %d\n", count);while (i--) {uv_interface_address_t interface = info[i];printf("Name: %s\n", interface.name);printf("Internal? %s\n", interface.is_internal ? "Yes" : "No");if (interface.address.address4.sin_family == AF_INET) {uv_ip4_name(&interface.address.address4, buf, sizeof(buf));printf("IPv4 address: %s\n", buf);}else if (interface.address.address4.sin_family == AF_INET6) {uv_ip6_name(&interface.address.address6, buf, sizeof(buf));printf("IPv6 address: %s\n", buf);}printf("\n");}uv_free_interface_addresses(info, count);return 0;
}

is_internal 对于回环接口来说为 true. 请注意如果物理网口使用了多个 IPv4/IPv6 地址, 那么它的名称将会被多次报告, 因为每个地址都会报告一次.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/293985.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

突破历史,21年C#将首获年度编程语言奖!

2021年渐入尾声&#xff0c;TIOBE今日发布了12月排行榜&#xff0c;Java(第3)持续下滑&#xff0c;PHP(第12)跌出前十&#xff0c;而C#(第5)继续稳步增长。据悉&#xff0c;TIOBE的CEO Paul Jansen表示&#xff0c;C#极有可能获取“TIOBE 年度编程语言奖”。该奖项一般颁发给被…

史上最接近上帝的方程!神秘的数字4.669,目前没有人能解开这个谜语......

全世界只有3.14 % 的人关注了爆炸吧知识上帝指纹统治世界&#xff1f;春节进入倒计时&#xff0c;实不相瞒&#xff0c;超模君想鸽的心蠢蠢欲动&#xff0c;费了好大劲才摁住&#xff01;为了不鸽&#xff0c;连夜翻了个墙&#xff0c;明明刚开始还在认认真真看论文&#xff0c…

html仿苹果浏览器,完美仿iPhone风格主题 领航浏览器体验

1仿iPhone的图标式导航页手机浏览器这个市场因其使用情况极为广泛和频繁因此吸引了无数厂商进入&#xff0c;不仅是传统的浏览器厂商也有许多新晋的手机软件厂商&#xff0c;其产品也从强调省流量、云概念、操作体验和自主核心、HTML 5等功能不一而足&#xff0c;今天带来的体验…

Android之MediaProjectionManager实现手机截屏总结

比较好的文章&#xff1a; Android中使用代码截图的各种方法总结 http://blog.csdn.net/woshinia/article/details/11520403 手机截屏&#xff1a; http://www.cnblogs.com/tgyf/p/4655507.html 转载的地方&#xff1a; http://www.cnblogs.com/tgyf/p/4851092.html 分享一…

网络游戏同步法则

转自&#xff1a;http://www.cppblog.com/keigoliye/archive/2009/09/12/95986.html网络游戏同步法则网路的硬件也有限&#xff0c;而人的创造也无限&#xff0c;在公网平均130ms的Latency下&#xff0c;是不存在“完全的”的同步情况。如何通过消除/隐藏延时&#xff0c;将用户…

【JavaScript】关于eval( )

为什么80%的码农都做不了架构师&#xff1f;>>> 一、eval() 动态执行时使用当前函数的闭包。 var i 100; function myFunc(ctx) {var i test;eval(var test "hello."); //test执行后为局部变量 } myFunc(); // 输出值100 alert(i); 二、eva l() 访问…

在ASP.Net Core和JAVA中,使用Azure配置密钥——Key Vault

思路浅析 在软件开发中&#xff0c;项目安全是重中之重&#xff0c;特别是在多部门或者开源项目中&#xff0c;如何保存我们的密钥&#xff0c;但又不影响本地的开发&#xff0c;更需要我们开发者需要考虑的问题&#xff0c;这里简单的列举了下平时开发中我们做的…

我怀疑对象做了什么对不起我的事......

1 狗狗已经这么明显提醒你了▼2 传销老总都怕的传销......▼3 和一只狗撞衫了▼4 你信吗&#xff1f;我跳水不会湿头发&#xff01;▼5 东北雪糕行业繁荣的原因还挺在理▼6 打了个平手&#xff1f;▼7 缅甸网红苏娜英腰围35公分她还想减▼8 哈哈哈哈▼你点的每个赞&…

jQuery 事件和动画

jQuery 事件和动画 上回说到jQuery的选择器&#xff0c;大家都应该知道了&#xff0c;jQuery的使用可以让我们少写很多的代码&#xff0c;达到一个轻量级的效果&#xff0c;那么既然都有选择器等等方便&#xff0c;那么事件的使用肯定也是不可能缺少的&#xff0c;另外还加入一…

Android之用SingleTask和TaskAffinity解决手机截取的项目启动页面问题

今天做的远程截屏功能,服务端发一个命令下来,然后客户端截屏,截屏的代码已经写好,因为是跨进程通信的,我最后采取的办法是启动activity来实现的,但是问题来了,如果用户没有登录的情况下,可以截屏到任何页面,但是登录了之后,不在本应用里面切换的话,会回到应用的页面…

html怎么用excel打开乱码,我的Excel表格打开就乱码了,请问该如何修复?

回答&#xff1a; 第一种方法&#xff1a;采取直接修复最新版本的Excel具有直接修复受损文件的功能&#xff0c;大家可以利用Excel新增的“打开并修复”命令&#xff0c;来直接检查并修复Excel文件中的错误&#xff0c;只要单击该命令&#xff0c;Excel就会打开一个修复对话框&…

汽车模型身上出现反射效果

博客列表: www.1111kp.info, www.163123.info, www.360111.info, www.360123.info, www.6699ysk.info, www.aaafaipiao.com, www.bbbkp123.info, www.fp1111.info, www.fp1234.info, www.fpfuzhou.com, 3dsmax导出的模型&#xff0c;默认材质是漫反射&#xff08;diffuse&…

记一次 .NET 某市附属医院 Web程序 偶发性CPU爆高分析

一&#xff1a;背景 1. 讲故事这个月初&#xff0c;一位朋友加微信求助他的程序出现了 CPU 偶发性爆高&#xff0c;希望能有偿解决一下。从描述看&#xff0c;这个问题应该困扰了很久&#xff0c;还是医院的朋友给力&#xff0c;开门就是 100块 红包 &#x1f923;&#x1f923…

执行CMD命令

可以执行多条命令&#xff0c;用“\r\n”分割 1 using System;2 using System.Diagnostics;3 4 namespace Tool5 {6 7 public class CMDHelper8 {9 public static string[] ExeCommand(string commandText) 10 { 11 12 Process p new Pr…

[iOS]应用内支付(内购)的个人开发过程及坑!

本文基于XcodeVersion 7.3 (7D175)版本&#xff0c;手机是iPhone 6&#xff0c;9.3系统。 一. 创建测试App 首先你需要登录 App的ItunesConnection&#xff0c;你会看到如下界面 简单的介绍一下这几个选项 1.我的App主要用于管理自己的App应用&#xff0c;例如编辑资料&…

Android之Intent 序列化反序列化

我们做截屏功能的时候&#xff0c;因为有2个进程&#xff0c;本来是把intent和MediaProjection放到Application里面&#xff0c;但是由于跨进程了&#xff0c;所以数据拿不到&#xff0c;就采用了Parcel 序列化出错,未找到出错的原因,找其它的解决方法: 查看Intent 的源代码, 发…

农商银行招聘计算机人员考什么,农商银行招聘考试题都考什么?

整理了农商农商一、行政职业能力测试类农商银行行测考试题型主要以选择题形式出现。主要包括言语理解、数量关系、判断推理、资料分析、常识五大部分。二、英语类农商银行考试英语部分&#xff1a;一般银行英语考试内容包括英语词汇与语法、英汉互译、改错、完型填空和阅读理解…

一步步学习微软InfoPath2010和SP2010--第八章节--使用InfoPath表单Web部件

本章中&#xff0c;你将学习到&#xff1a; 1. 配置Web部件设置 2. 创建Web部件连接 3. 创建表单参数 4. 使用其他浏览器表单参数 你可以使用InfoPath表单Web部件&#xff08;Microsoft SharePoint2010新引入的&#xff09;在SharePoint企业版或Microsoft…

讲一讲应用服务的新鲜事儿

微软中国MSDN 点击上方蓝字关注我们为了新功能的发布&#xff0c;以及 Linux 和 Windows 的改进&#xff0c;Azure App Service 团队付出了非常多的努力。很开心的是&#xff0c;我们看到了 Windows Containers 的正式版本&#xff0c;并可应用于 App Service 环境 v3 上。此外…