<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                #Networking 在libuv中使用網絡編程接口不會像在BSD上使用socket接口那么的麻煩,因為libuv上所有的都是非阻塞的,但是原理都是一樣的。可以這么說,libuv提供了覆蓋了惱人的,啰嗦的和底層的任務的抽象函數,比如使用BSD的socket結構的來設置socket,還有DNS查找,libuv還調整了一些socket的參數。 在網絡I/O中會使用到```uv_tcp_t```和```uv_udp_t```。 ##TCP TCP是面向連接的,字節流協議,因此基于libuv的stream實現。 ####server 服務器端的建立流程如下: >1.```uv_tcp_init```建立tcp句柄。 >2.```uv_tcp_bind```綁定。 >3.```uv_listen```建立監聽,當有新的連接到來時,激活調用回調函數。 >4.```uv_accept```接收鏈接。 >5.使用stream處理來和客戶端通信。 ####tcp-echo-server/main.c - The listen socket ```c int main() { loop = uv_default_loop(); uv_tcp_t server; uv_tcp_init(loop, &server); uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection); if (r) { fprintf(stderr, "Listen error %s\n", uv_strerror(r)); return 1; } return uv_run(loop, UV_RUN_DEFAULT); } ``` 你可以調用```uv_ip4_addr()```函數來將ip地址和端口號轉換為sockaddr_in結構,這樣就可以被BSD的socket使用了。要想完成逆轉換的話可以調用```uv_ip4_name()```。 #####note >對應ipv6有類似的uv_ip6_* 大多數的設置函數是同步的,因為它們不會消耗太多cpu資源。到了```uv_listen```這句,我們再次回到回調函數的風格上來。第二個參數是待處理的連接請求隊列-最大長度的請求連接隊列。 當客戶端開始建立連接的時候,回調函數```on_new_connection```需要使用```uv_accept```去建立一個與客戶端socket通信的句柄。同時,我們也要開始從流中讀取數據。 ####tcp-echo-server/main.c - Accepting the client ```c void on_new_connection(uv_stream_t *server, int status) { if (status < 0) { fprintf(stderr, "New connection error %s\n", uv_strerror(status)); // 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); } } ``` 上述的函數集和stream的例子類似,在code文件夾中可以找到更多的例子。記得在socket不需要后,調用uv_close。如果你不需要接受連接,你甚至可以在uv_listen的回調函數中調用uv_close。 ####client 當你在服務器端完成綁定/監聽/接收的操作后,在客戶端只要簡單地調用```uv_tcp_connect```,它的回調函數和上面類似,具體例子如下: ```c uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); uv_tcp_init(loop, socket); uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t)); struct sockaddr_in dest; uv_ip4_addr("127.0.0.1", 80, &dest); uv_tcp_connect(connect, socket, dest, on_connect); ``` 當建立連接后,回調函數```on_connect```會被調用。回調函數會接收到一個uv_connect_t結構的數據,它的```handle```指向通信的socket。 ##UDP 用戶數據報協議(User Datagram Protocol)提供無連接的,不可靠的網絡通信。因此,libuv不會提供一個stream實現的形式,而是提供了一個```uv_udp_t```句柄(接收端),和一個```uv_udp_send_t```句柄(發送端),還有相關的函數。也就是說,實際的讀寫api與正常的流讀取類似。下面的例子展示了一個從DCHP服務器獲取ip的例子。 #####note >你必須以管理員的權限運行udp-dhcp,因為它的端口號低于1024 ####udp-dhcp/main.c - Setup and send UDP packets ```c 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, &recv_addr); uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR); uv_udp_recv_start(&recv_socket, alloc_buffer, on_read); uv_udp_init(loop, &send_socket); struct sockaddr_in broadcast_addr; uv_ip4_addr("0.0.0.0", 0, &broadcast_addr); uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0); uv_udp_set_broadcast(&send_socket, 1); uv_udp_send_t send_req; uv_buf_t discover_msg = make_discover_msg(); struct sockaddr_in send_addr; uv_ip4_addr("255.255.255.255", 67, &send_addr); uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send); return uv_run(loop, UV_RUN_DEFAULT); } ``` #####note >ip地址為0.0.0.0,用來綁定所有的接口。255.255.255.255是一個廣播地址,這也意味著數據報將往所有的子網接口中發送。端口號為0代表著由操作系統隨機分配一個端口。 首先,我們設置了一個接收的socket,端口號為68,作為DHCP客戶端,然后開始從中讀取數據。它會接收所有來自DHCP服務器的返回數據。我們設置了```UV_UDP_REUSEADDR```標記,用來和其他共享端口的 DHCP客戶端和平共處。接著,我們設置了一個類似的發送socket,然后使用```uv_udp_send```向DHCP服務器(在67端口)發送廣播。 設置廣播發送是非常必要的,否則你會接收到`EACCES`[錯誤](http://beej.us/guide/bgnet/output/html/multipage/advanced.html#broadcast)。和此前一樣,如果在讀寫中出錯,返回碼<0。 因為UDP不會建立連接,因此回調函數會接收到關于發送者的額外的信息。 當沒有可讀數據后,nread等于0。如果`addr`是`null`,它代表了沒有可讀數據(回調函數不會做任何處理)。如果不為null,則說明了從addr中接收到一個空的數據報。如果flag為```UV_UDP_PARTIAL```,則代表了內存分配的空間不夠存放接收到的數據了,在這種情形下,操作系統會丟棄存不下的數據。 ####udp-dhcp/main.c - Reading packets ```c void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) { if (nread < 0) { fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t*) req, NULL); free(buf->base); return; } char sender[17] = { 0 }; uv_ip4_name((const struct sockaddr_in*) addr, sender, 16); fprintf(stderr, "Recv from %s\n", sender); // ... DHCP specific code unsigned int *as_integer = (unsigned int*)buf->base; unsigned int ipbin = ntohl(as_integer[4]); unsigned char ip[4] = {0}; int i; for (i = 0; i < 4; i++) ip[i] = (ipbin >> i*8) & 0xff; fprintf(stderr, "Offered IP %d.%d.%d.%d\n", ip[3], ip[2], ip[1], ip[0]); free(buf->base); uv_udp_recv_stop(req); } ``` ####UDP Options 生存時間(Time-to-live) >可以通過`uv_udp_set_ttl`更改生存時間。 只允許IPV6協議棧 >在調用`uv_udp_bind`時,設置`UV_UDP_IPV6ONLY`標示,可以強制只使用ipv6。 組播 >socket也支持組播,可以這么使用: ```c 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_GROUP`和`UV_LEAVE_GROUP`。 這里有一篇很好的關于組播的[文章](http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html)。 可以使用`uv_udp_set_multicast_loop`修改本地的組播。 同樣可以使用`uv_udp_set_multicast_ttl`修改組播數據報的生存時間。(設定生存時間可以防止數據報由于環路的原因,會出現無限循環的問題)。 ##Querying DNS libuv提供了一個異步的DNS解決方案。它提供了自己的`getaddrinfo`。在回調函數中你可以像使用正常的socket操作一樣。讓我們來看一下例子: ####dns/main.c ```c 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(r)); return 1; } return uv_run(loop, UV_RUN_DEFAULT); } ``` 如果`uv_getaddrinfo`返回非零值,說明設置錯誤了,因此也不會激發回調函數。在函數返回后,所有的參數將會被回收和釋放。主機地址,請求服務器地址,還有hints的結構都可以在[這里](http://nikhilm.github.io/uvbook/getaddrinfo)找到詳細的說明。如果想使用同步請求,可以將回調函數設置為NULL。 在回調函數on_resolved中,你可以從`struct addrinfo(s)`鏈表中獲取返回的IP,最后需要調用`uv_freeaddrinfo`回收掉鏈表。下面的例子演示了回調函數的內容。 ####dns/main.c ```c void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { if (status < 0) { fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(status)); 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); uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect); uv_freeaddrinfo(res); } ``` libuv同樣提供了DNS逆解析的函數[uv_getnameinfo](http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo])。 ##Network interfaces 可以調用`uv_interface_addresses`獲得系統的網絡接口信息。下面這個簡單的例子打印出所有可以獲取的信息。這在服務器開始準備綁定IP地址的時候很有用。 ####interfaces/main.c ```c #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`可以用來表示是否是內部的IP。由于一個物理接口會有多個IP地址,所以每一次while循環的時候都會打印一次。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看