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

相关文章

Python中conda虚拟环境创建及使用小结

《Python中conda虚拟环境创建及使用小结》本文主要介绍了Python中conda虚拟环境创建及使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录0.前言1.Miniconda安装2.conda本地基本操作3.创建conda虚拟环境4.激活c

使用Python创建一个能够筛选文件的PDF合并工具

《使用Python创建一个能够筛选文件的PDF合并工具》这篇文章主要为大家详细介绍了如何使用Python创建一个能够筛选文件的PDF合并工具,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录背景主要功能全部代码代码解析1. 初始化 wx.Frame 窗口2. 创建工具栏3. 创建布局和界面控件4

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c

使用Python在Excel中创建和取消数据分组

《使用Python在Excel中创建和取消数据分组》Excel中的分组是一种通过添加层级结构将相邻行或列组织在一起的功能,当分组完成后,用户可以通过折叠或展开数据组来简化数据视图,这篇博客将介绍如何使... 目录引言使用工具python在Excel中创建行和列分组Python在Excel中创建嵌套分组Pyt

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

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

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

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

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