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中,用户管理和权限控制是数据库安全的重要组成部分,下面详细介绍如何在MySQL中创建用户并授予适当的权限,感兴趣的朋友跟随小编一起看看吧... 目录mysql 用户创建与授权详解一、MySQL用户管理基础1. 用户账户组成2. 查看现有用户二、创建用户1. 基

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Python FastMCP构建MCP服务端与客户端的详细步骤

《PythonFastMCP构建MCP服务端与客户端的详细步骤》MCP(Multi-ClientProtocol)是一种用于构建可扩展服务的通信协议框架,本文将使用FastMCP搭建一个支持St... 目录简介环境准备服务端实现(server.py)客户端实现(client.py)运行效果扩展方向常见问题结

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Macos创建python虚拟环境的详细步骤教学

《Macos创建python虚拟环境的详细步骤教学》在macOS上创建Python虚拟环境主要通过Python内置的venv模块实现,也可使用第三方工具如virtualenv,下面小编来和大家简单聊聊... 目录一、使用 python 内置 venv 模块(推荐)二、使用 virtualenv(兼容旧版 P

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创

C#使用MQTTnet实现服务端与客户端的通讯的示例

《C#使用MQTTnet实现服务端与客户端的通讯的示例》本文主要介绍了C#使用MQTTnet实现服务端与客户端的通讯的示例,包括协议特性、连接管理、QoS机制和安全策略,具有一定的参考价值,感兴趣的可... 目录一、MQTT 协议简介二、MQTT 协议核心特性三、MQTTNET 库的核心功能四、服务端(BR

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示