赞
踩
/* * tcp echo server * * @build make examples * @server bin/tcp_echo_server 1234 * @client bin/nc 127.0.0.1 1234 * nc 127.0.0.1 1234 * telnet 127.0.0.1 1234 */ #include "hloop.h" #include "hsocket.h" // hloop_create_tcp_server -> on_accept -> hio_read -> on_recv -> hio_write static void on_close(hio_t* io) { printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); } static void on_recv(hio_t* io, void* buf, int readbytes) { printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes); char localaddrstr[SOCKADDR_STRLEN] = {0}; char peeraddrstr[SOCKADDR_STRLEN] = {0}; printf("[%s] <=> [%s]\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); printf("< %.*s", readbytes, (char*)buf); // 回显: 将读到的数据原封不动的发给对方 printf("> %.*s", readbytes, (char*)buf); hio_write(io, buf, readbytes); } static void on_accept(hio_t* io) { printf("on_accept connfd=%d\n", hio_fd(io)); char localaddrstr[SOCKADDR_STRLEN] = {0}; char peeraddrstr[SOCKADDR_STRLEN] = {0}; printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io), SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); // 设置回调 hio_setcb_close(io, on_close); hio_setcb_read(io, on_recv); // 监听读事件 hio_read(io); } int main(int argc, char** argv) { if (argc < 2) { printf("Usage: %s port\n", argv[0]); return -10; } int port = atoi(argv[1]); // 创建事件循环 hloop_t* loop = hloop_new(0); // 创建TCP服务,设置accept回调 hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept); if (listenio == NULL) { return -20; } printf("listenfd=%d\n", hio_fd(listenio)); // 运行事件循环 hloop_run(loop); // 释放事件循环 hloop_free(&loop); return 0; }
编译步骤:
git clone https://github.com/ithewei/libhv.git
cd libhv
./configure
make
注:windows平台编译可使用cmake
git clone https://github.com/ithewei/libhv.git
cd libhv
mkdir win64
cd win64
cmake .. -G "Visual Studio 15 2017 Win64"
cmake --build .
测试:
注:windows平台可使用telnet
代替nc
作为测试客户端
/* * tcp chat server * * @build make examples * @server bin/tcp_chat_server 1234 * @clients bin/nc 127.0.0.1 1234 * nc 127.0.0.1 1234 * telnet 127.0.0.1 1234 */ #include "hloop.h" #include "hsocket.h" #include "hbase.h" #include "list.h" // libhv没有暴露出list.h、heap.h、rbtree.h等c版本基础数据结构,如需要请自行从base/目录下拷贝 // hloop_create_tcp_server // on_accept => join // on_recv => broadcast // on_close => leave typedef struct chatroom_s { // 事件循环结构体指针 hloop_t* loop; // 监听IO结构体指针 hio_t* listenio; // 房间号 int roomid; // 使用链表存储连接,插入与删除复杂度皆为O(1) struct list_head conns; } chatroom_t; typedef struct connection_s { // 连接IO结构体指针 hio_t* connio; // 套接字地址 char addr[SOCKADDR_STRLEN]; // 链表结点 struct list_node node; } connection_t; static chatroom_t s_chatroom; static void join(chatroom_t* room, connection_t* conn); static void leave(chatroom_t* room, connection_t* conn); static void broadcast(chatroom_t* room, const char* msg, int msglen); // 加入聊天室 void join(chatroom_t* room, connection_t* conn) { // 添加到链表 list_add(&conn->node, &room->conns); char msg[256] = {0}; int msglen = 0; // 向新来的成员发送当前聊天室成员列表 struct list_node* node; connection_t* cur; msglen = snprintf(msg, sizeof(msg), "room[%06d] clients:\r\n", room->roomid); hio_write(conn->connio, msg, msglen); list_for_each (node, &room->conns) { cur = list_entry(node, connection_t, node); msglen = snprintf(msg, sizeof(msg), "[%s]\r\n", cur->addr); hio_write(conn->connio, msg, msglen); } hio_write(conn->connio, "\r\n", 2); // 广播有新成员加入聊天室 msglen = snprintf(msg, sizeof(msg), "client[%s] join room[%06d]\r\n", conn->addr, room->roomid); broadcast(room, msg, msglen); } // 离开聊天室 void leave(chatroom_t* room, connection_t* conn) { // 从链表里删除 list_del(&conn->node); // 广播有成员离开聊天室 char msg[256] = {0}; int msglen = snprintf(msg, sizeof(msg), "client[%s] leave room[%d]\r\n", conn->addr, room->roomid); broadcast(room, msg, msglen); } // 广播消息 void broadcast(chatroom_t* room, const char* msg, int msglen) { printf("> %.*s", msglen, msg); struct list_node* node; connection_t* conn; // 循环向聊天室的每个连接发送消息 list_for_each (node, &room->conns) { conn = list_entry(node, connection_t, node); hio_write(conn->connio, msg, msglen); } } static void on_close(hio_t* io) { printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); connection_t* conn = (connection_t*)hevent_userdata(io); if (conn) { hevent_set_userdata(io, NULL); // 断连离开聊天室 leave(&s_chatroom, conn); HV_FREE(conn); } } static void on_recv(hio_t* io, void* buf, int readbytes) { printf("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes); char localaddrstr[SOCKADDR_STRLEN] = {0}; char peeraddrstr[SOCKADDR_STRLEN] = {0}; printf("[%s] <=> [%s]\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); printf("< %.*s", readbytes, (char*)buf); // 广播消息 connection_t* conn = (connection_t*)hevent_userdata(io); assert(conn != NULL); char msg[256] = {0}; int msglen = snprintf(msg, sizeof(msg), "client[%s] say: %.*s", conn->addr, readbytes, (char*)buf); broadcast(&s_chatroom, msg, msglen); } static void on_accept(hio_t* io) { printf("on_accept connfd=%d\n", hio_fd(io)); char localaddrstr[SOCKADDR_STRLEN] = {0}; char peeraddrstr[SOCKADDR_STRLEN] = {0}; printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io), SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); // 设置回调 hio_setcb_close(io, on_close); hio_setcb_read(io, on_recv); // 监听读事件 hio_read(io); // 新的连接加入聊天室 connection_t* conn = NULL; HV_ALLOC_SIZEOF(conn); conn->connio = io; strcpy(conn->addr, peeraddrstr); hevent_set_userdata(io, conn); join(&s_chatroom, conn); } int main(int argc, char** argv) { if (argc < 2) { printf("Usage: %s port\n", argv[0]); return -10; } int port = atoi(argv[1]); // 创建事件循环 hloop_t* loop = hloop_new(0); // 创建TCP服务,设置accept回调 hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept); if (listenio == NULL) { return -20; } printf("listenfd=%d\n", hio_fd(listenio)); // 初始化聊天室结构体 s_chatroom.loop = loop; s_chatroom.listenio = listenio; // 随机生成一个房间号 srand(time(NULL)); s_chatroom.roomid = rand() % 1000000; list_init(&s_chatroom.conns); // 运行事件循环 hloop_run(loop); // 释放事件循环 hloop_free(&loop); return 0; }
测试:
/* * tcp proxy server * * @build: make clean && make examples WITH_OPENSSL=yes * @http_server: bin/httpd -s restart -d * @proxy_server: bin/tcp_proxy_server 8888 127.0.0.1:8080 * bin/tcp_proxy_server 8888 127.0.0.1:8443 * bin/tcp_proxy_server 8888 www.baidu.com * bin/tcp_proxy_server 8888 www.baidu.com:443 * @client: bin/curl -v 127.0.0.1:8888 * bin/nc 127.0.0.1 8888 * > GET / HTTP/1.1 * > Connection: close * > [Enter] * > GET / HTTP/1.1 * > Connection: keep-alive * > [Enter] */ #include "hloop.h" #include "hsocket.h" static char proxy_host[64] = "127.0.0.1"; static int proxy_port = 80; static int proxy_ssl = 0; // hloop_create_tcp_server -> on_accept -> hio_setup_tcp_upstream static void on_accept(hio_t* io) { /* printf("on_accept connfd=%d\n", hio_fd(io)); char localaddrstr[SOCKADDR_STRLEN] = {0}; char peeraddrstr[SOCKADDR_STRLEN] = {0}; printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io), SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); */ if (proxy_port % 1000 == 443) proxy_ssl = 1; // 建立TCP转发 hio_setup_tcp_upstream(io, proxy_host, proxy_port, proxy_ssl); } int main(int argc, char** argv) { if (argc < 3) { printf("Usage: %s port proxy_host:proxy_port\n", argv[0]); return -10; } int port = atoi(argv[1]); char* pos = strchr(argv[2], ':'); if (pos) { int len = pos - argv[2]; if (len > 0) { memcpy(proxy_host, argv[2], len); proxy_host[len] = '\0'; } proxy_port = atoi(pos + 1); } else { strncpy(proxy_host, argv[2], sizeof(proxy_host)); } if (proxy_port == 0) proxy_port = 80; printf("proxy: [%s:%d]\n", proxy_host, proxy_port); // 创建事件循环 hloop_t* loop = hloop_new(0); // 创建TCP服务,设置accept回调 hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept); if (listenio == NULL) { return -20; } printf("listenfd=%d\n", hio_fd(listenio)); // 运行事件循环 hloop_run(loop); // 释放事件循环 hloop_free(&loop); return 0; }
测试:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。