libuv学习,创建tcp服务端试例

2024-06-21 07:08

本文主要是介绍libuv学习,创建tcp服务端试例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

    使用libuv,可以非常方便的创建tcp服务端,基本上除了初始化,其他所有的处理都是在回调函数中处理的。可以非常轻松的实现异步读写。其中需要注意的是,uv_read_start的第二个参数,uv_alloc_cb回调函数,在每次接收到数据触发uv_read_cb回调之前都会被调用一次,用来给接收缓存做初始化,如果是每次通过malloc申请的内存,那么就要自己手动free掉,试例中就是使用的这种方式,只不过做了处理,接收的时候没有free掉,而是通过uv_buf_init函数把它又赋值给了uv_write所需的发送缓存,最后在发送完成回调uv_write_cb中一并free了。通过这种方式,实现了简单的tcp echo服务。

/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.** Permission is hereby granted, free of charge, to any person obtaining a copy* of this software and associated documentation files (the "Software"), to* deal in the Software without restriction, including without limitation the* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or* sell copies of the Software, and to permit persons to whom the Software is* furnished to do so, subject to the following conditions:** The above copyright notice and this permission notice shall be included in* all copies or substantial portions of the Software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS* IN THE SOFTWARE.*/#ifndef TASK_H_
#define TASK_H_#include "uv.h"#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>#if defined(_MSC_VER) && _MSC_VER < 1600
#include "stdint-msvc2008.h"
#else
#include <stdint.h>
#endif#if !defined(_WIN32)
#include <sys/time.h>
#include <sys/resource.h>  /* setrlimit() */
#endif#ifdef __clang__
#pragma clang diagnostic ignored "-Wvariadic-macros"
#pragma clang diagnostic ignored "-Wc99-extensions"
#endif#define TEST_PORT 9123
#define TEST_PORT_2 9124#ifdef _WIN32
#define TEST_PIPENAME "\\\\?\\pipe\\uv-test"
#define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2"
#else
#define TEST_PIPENAME "/tmp/uv-test-sock"
#define TEST_PIPENAME_2 "/tmp/uv-test-sock2"
#endif#ifdef _WIN32
#include <io.h>
#ifndef S_IRUSR
#define S_IRUSR _S_IREAD
#endif
#ifndef S_IWUSR
#define S_IWUSR _S_IWRITE
#endif
#endif#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))#define container_of(ptr, type, member) \((type *) ((char *) (ptr) - offsetof(type, member)))typedef enum {TCP = 0,UDP,PIPE
} stream_type;/* Die with fatal error. */
#define FATAL(msg)                                        \do {                                                    \fprintf(stderr,                                       \"Fatal error in %s on line %d: %s\n",         \__FILE__,                                     \__LINE__,                                     \msg);                                         \fflush(stderr);                                       \abort();                                              \} while (0)/* Have our own assert, so we are sure it does not get optimized away in* a release build.*/
#define ASSERT(expr)                                      \do {                                                     \if (!(expr)) {                                          \fprintf(stderr,                                       \"Assertion failed in %s on line %d: %s\n",    \__FILE__,                                     \__LINE__,                                     \#expr);                                       \abort();                                              \}                                                       \} while (0)/* This macro cleans up the main loop. This is used to avoid valgrind* warnings about memory being "leaked" by the main event loop.*/
#define MAKE_VALGRIND_HAPPY()           \do {                                  \close_loop(uv_default_loop());      \uv_loop_delete(uv_default_loop());  \} while (0)/* Just sugar for wrapping the main() for a task or helper. */
#define TEST_IMPL(name)                                                       \int run_test_##name(void);                                                  \int run_test_##name(void)#define BENCHMARK_IMPL(name)                                                  \int run_benchmark_##name(void);                                             \int run_benchmark_##name(void)#define HELPER_IMPL(name)                                                     \int run_helper_##name(void);                                                \int run_helper_##name(void)/* Pause the calling thread for a number of milliseconds. */
void uv_sleep(int msec);/* Format big numbers nicely. WARNING: leaks memory. */
const char* fmt(double d);/* Reserved test exit codes. */
enum test_status {TEST_OK = 0,TEST_TODO,TEST_SKIP
};#define RETURN_OK()                                                           \do {                                                                        \return TEST_OK;                                                           \} while (0)#define RETURN_TODO(explanation)                                              \do {                                                                        \fprintf(stderr, "%s\n", explanation);                                     \fflush(stderr);                                                           \return TEST_TODO;                                                         \} while (0)#define RETURN_SKIP(explanation)                                              \do {                                                                        \fprintf(stderr, "%s\n", explanation);                                     \fflush(stderr);                                                           \return TEST_SKIP;                                                         \} while (0)#if !defined(_WIN32)#define TEST_FILE_LIMIT(num)                                                 \do {                                                                      \struct rlimit lim;                                                      \lim.rlim_cur = (num);                                                   \lim.rlim_max = lim.rlim_cur;                                            \if (setrlimit(RLIMIT_NOFILE, &lim))                                     \RETURN_SKIP("File descriptor limit too low.");                        \} while (0)#else  /* defined(_WIN32) */#define TEST_FILE_LIMIT(num) do {} while (0)#endif#if defined _WIN32 && ! defined __GNUC__#include <stdarg.h>/* Define inline for MSVC<2015 */
#if defined(_MSC_VER) && _MSC_VER < 1900
#define inline __inline
#endif#if defined(_MSC_VER) && _MSC_VER < 1900/* Emulate snprintf() on MSVC<2015, _snprintf() doesn't zero-terminate the buffer* on overflow...*/
inline int snprintf(char* buf, size_t len, const char* fmt, ...) {va_list ap;int n;va_start(ap, fmt);n = _vsprintf_p(buf, len, fmt, ap);va_end(ap);/* It's a sad fact of life that no one ever checks the return value of* snprintf(). Zero-terminating the buffer hopefully reduces the risk* of gaping security holes.*/if (n < 0)if (len > 0)buf[0] = '\0';return n;
}
#endif#endif#if defined(__clang__) ||                                \defined(__GNUC__) ||                                 \defined(__INTEL_COMPILER) ||                         \defined(__SUNPRO_C)
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif/* Fully close a loop */
static void close_walk_cb(uv_handle_t* handle, void* arg) {if (!uv_is_closing(handle))uv_close(handle, NULL);
}UNUSED static void close_loop(uv_loop_t* loop) {uv_walk(loop, close_walk_cb, NULL);uv_run(loop, UV_RUN_DEFAULT);
}UNUSED static int can_ipv6(void) {uv_interface_address_t* addr;int supported;int count;int i;if (uv_interface_addresses(&addr, &count))return 1; /* Assume IPv6 support on failure. */supported = 0;for (i = 0; supported == 0 && i < count; i += 1)supported = (AF_INET6 == addr[i].address.address6.sin6_family);uv_free_interface_addresses(addr, count);return supported;
}#endif /* TASK_H_ */
#include <stdio.h>
#include <uv.h>
#include <stdlib.h>
#include "task.h"typedef struct
{int nm;
} conn;typedef struct
{uv_write_t req;uv_buf_t buf;
} write_req_t;
static uv_loop_t* loop;static int server_closed;
static stream_type serverType;
static uv_tcp_t tcpServer;
static uv_udp_t udpServer;
static uv_pipe_t pipeServer;
static uv_handle_t* server;static void after_write(uv_write_t* req, int status);
static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf);
static void on_close(uv_handle_t* peer);
static void on_server_close(uv_handle_t* handle);
static void on_connection(uv_stream_t*, int status);static void on_server_close(uv_handle_t* handle)
{ASSERT(handle == server);
}static void after_write(uv_write_t* req, int status)
{write_req_t* wr;/* Free the read/write buffer and the request */wr = (write_req_t*) req;printf("wr->buf.base=%p\n",wr->buf.base);free(wr->buf.base);free(wr);if (status == 0)return;fprintf(stderr,"uv_write error: %s - %s\n",uv_err_name(status),uv_strerror(status));
}static void after_shutdown(uv_shutdown_t* req, int status)
{uv_close((uv_handle_t*) req->handle, on_close);free(req);
}static void after_read(uv_stream_t* handle,ssize_t nread,const uv_buf_t* buf)
{int i;write_req_t *wr;uv_shutdown_t* sreq;if (nread < 0){/* Error or EOF */ASSERT(nread == UV_EOF);free(buf->base);sreq = malloc(sizeof* sreq);ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown));return;}if (nread == 0){/* Everything OK, but nothing read. */free(buf->base);return;}/** Scan for the letter Q which signals that we should quit the server.* If we get QS it means close the stream.*/for (i = 0; i < nread; i++){printf("0x%02x ", buf->base[i]);}printf("\n");if (!server_closed){for (i = 0; i < nread; i++){if (buf->base[i] == 'Q'){if (i + 1 < nread && buf->base[i + 1] == 'S'){free(buf->base);uv_close((uv_handle_t*) handle, on_close);return;}else{uv_close(server, on_server_close);server_closed = 1;}}}}wr = (write_req_t*) malloc(sizeof *wr);ASSERT(wr != NULL);wr->buf = uv_buf_init(buf->base, nread);if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)){FATAL("uv_write failed");}
}static void on_close(uv_handle_t* peer)
{free(peer);
}static void echo_alloc(uv_handle_t* handle,size_t suggested_size,uv_buf_t* buf)
{buf->base = malloc(suggested_size);buf->len = suggested_size;printf("suggested_size =%d %p\n", suggested_size,buf->base);
}static void on_connection(uv_stream_t* server, int status)
{uv_stream_t* stream;int r;if (status != 0){fprintf(stderr, "Connect error %s\n", uv_err_name(status));}ASSERT(status == 0);switch (serverType){case TCP:stream = malloc(sizeof (uv_tcp_t));ASSERT(stream != NULL);r = uv_tcp_init(loop, (uv_tcp_t*) stream);ASSERT(r == 0);break;case PIPE:stream = malloc(sizeof (uv_pipe_t));ASSERT(stream != NULL);r = uv_pipe_init(loop, (uv_pipe_t*) stream, 0);ASSERT(r == 0);break;default:ASSERT(0 && "Bad serverType");abort();}/* associate server with stream */stream->data = server;r = uv_accept(server, stream);ASSERT(r == 0);r = uv_read_start(stream, echo_alloc, after_read);ASSERT(r == 0);
}static int tcp4_echo_start(int port)
{struct sockaddr_in addr;int r;ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr));server = (uv_handle_t*) & tcpServer;serverType = TCP;r = uv_tcp_init(loop, &tcpServer);if (r){/* TODO: Error codes */fprintf(stderr, "Socket creation error\n");return 1;}r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0);if (r){/* TODO: Error codes */fprintf(stderr, "Bind error\n");return 1;}r = uv_listen((uv_stream_t*) & tcpServer, SOMAXCONN, on_connection);if (r){/* TODO: Error codes */fprintf(stderr, "Listen error %s\n", uv_err_name(r));return 1;}return 0;
}int main()
{loop = uv_default_loop();tcp4_echo_start(30001);uv_run(loop, UV_RUN_DEFAULT);return 0;
}

 

这篇关于libuv学习,创建tcp服务端试例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080531

相关文章

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

mysql外键创建不成功/失效如何处理

《mysql外键创建不成功/失效如何处理》文章介绍了在MySQL5.5.40版本中,创建带有外键约束的`stu`和`grade`表时遇到的问题,发现`grade`表的`id`字段没有随着`studen... 当前mysql版本:SELECT VERSION();结果为:5.5.40。在复习mysql外键约

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用SpringBoot创建一个RESTful API的详细步骤

《使用SpringBoot创建一个RESTfulAPI的详细步骤》使用Java的SpringBoot创建RESTfulAPI可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤... 目录一、创建 Spring Boot 项目二、创建控制器类(Controller Class)三、运行

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

SpringBoot实现websocket服务端及客户端的详细过程

《SpringBoot实现websocket服务端及客户端的详细过程》文章介绍了WebSocket通信过程、服务端和客户端的实现,以及可能遇到的问题及解决方案,感兴趣的朋友一起看看吧... 目录一、WebSocket通信过程二、服务端实现1.pom文件添加依赖2.启用Springboot对WebSocket