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

相关文章

Spring创建Bean的八种主要方式详解

《Spring创建Bean的八种主要方式详解》Spring(尤其是SpringBoot)提供了多种方式来让容器创建和管理Bean,@Component、@Configuration+@Bean、@En... 目录引言一、Spring 创建 Bean 的 8 种主要方式1. @Component 及其衍生注解

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

MySQL 临时表创建与使用详细说明

《MySQL临时表创建与使用详细说明》MySQL临时表是存储在内存或磁盘的临时数据表,会话结束时自动销毁,适合存储中间计算结果或临时数据集,其名称以#开头(如#TempTable),本文给大家介绍M... 目录mysql 临时表详细说明1.定义2.核心特性3.创建与使用4.典型应用场景5.生命周期管理6.注

MySQL的触发器全解析(创建、查看触发器)

《MySQL的触发器全解析(创建、查看触发器)》MySQL触发器是与表关联的存储程序,当INSERT/UPDATE/DELETE事件发生时自动执行,用于维护数据一致性、日志记录和校验,优点包括自动执行... 目录触发器的概念:创建触www.chinasem.cn发器:查看触发器:查看当前数据库的所有触发器的定

创建springBoot模块没有目录结构的解决方案

《创建springBoot模块没有目录结构的解决方案》2023版IntelliJIDEA创建模块时可能出现目录结构识别错误,导致文件显示异常,解决方法为选择模块后点击确认,重新校准项目结构设置,确保源... 目录创建spChina编程ringBoot模块没有目录结构解决方案总结创建springBoot模块没有目录

Linux之UDP和TCP报头管理方式

《Linux之UDP和TCP报头管理方式》文章系统讲解了传输层协议UDP与TCP的核心区别:UDP无连接、不可靠,适合实时传输(如视频),通过端口号标识应用;TCP有连接、可靠,通过确认应答、序号、窗... 目录一、关于端口号1.1 端口号的理解1.2 端口号范围的划分1.3 认识知名端口号1.4 一个进程

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.