libhv学习路线 之 TCP服务器

        对于libhv TCP端的服务器实现,基本上也已经被libhv封装完成了,所以我们只需要研究以下libhv内部是如何实现TCP连接建立,以及收发数据的.

一 基于libhv的TCP服务器内部实现原理

        A..我们需要建立一个事件回环loop变量,具体代码如下所示:

hloop_t* loop = hloop_new(0);

        B.调用hloop_create_tcp_server()函数建立TCP连接,具体代码如下所示,IP地址设置的是0.0.0.0的主要原因是,我们希望服务器监听所有该服务器有的IP地址(服务器的IP可能不知有一个):

hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);

        对于hloop_create_tcp_server()函数是怎么实现建立TCP连接的,我们如下进行讲解:

        我们先看一下hloop_create_tcp_server()函数内部代码:

hio_t* hloop_create_tcp_server (hloop_t* loop, const char* host, int port, haccept_cb accept_cb) {
    hio_t* io = hio_create_socket(loop, host, port, HIO_TYPE_TCP, HIO_SERVER_SIDE);
    if (io == NULL) return NULL;
    hio_setcb_accept(io, accept_cb);
    if (hio_accept(io) != 0) return NULL;
    return io;
}

         对于hloop_create_tcp_server()内部代码:

        first.调用hio_create_socket()函数,建立监听socket,并且基于该监听socket建立I/O事件,并且注册accept事件.hio_create_socket()函数内部程序如下所示:

        1.建立socket地址(sockaddr类型结构体变量),调用sockaddr_set_ipport()函数对该结构体变量的ip地址和端口地址进行赋值;

        2.然后创建socket,判断要建立的socket是属于服务器还是客户端的,本专题主要讨论服务器,则先调用setsockopt()函数使得该socket的选项为可重用地址选项(因为服务器使用的端口都是惟一的,比如HTTP服务器端口为80,如果上次连接关闭,则该端口再次建立TCP连接时需要等待一会,如果设置为可重用地址选项,则该端口可以立即投入下次使用),最后再将该socket绑定socket地址(只有服务器才需要,毕竟服务器的端口号是为唯一的);

        3.建立I/O事件(hio_t变量),该I/O事件绑定对应的socket地址;

        4.返回该I/O事件.

hio_t* hio_create_socket(hloop_t* loop, const char* host, int port, hio_type_e type, hio_side_e side) {
    int sock_type = type & HIO_TYPE_SOCK_STREAM ? SOCK_STREAM :
                    type & HIO_TYPE_SOCK_DGRAM  ? SOCK_DGRAM :
                    type & HIO_TYPE_SOCK_RAW    ? SOCK_RAW : -1;
    if (sock_type == -1) return NULL;
    sockaddr_u addr;
    memset(&addr, 0, sizeof(addr));
    int ret = -1;
#ifdef ENABLE_UDS
    if (port < 0) {
        sockaddr_set_path(&addr, host);
        ret = 0;
    }
#endif
    if (port >= 0) {
        ret = sockaddr_set_ipport(&addr, host, port);
    }
    if (ret != 0) {
        // fprintf(stderr, "unknown host: %s\n", host);
        return NULL;
    }
    int sockfd = socket(addr.sa.sa_family, sock_type, 0);
    if (sockfd < 0) {
        perror("socket");
        return NULL;
    }
    hio_t* io = NULL;
    if (side == HIO_SERVER_SIDE) {
#ifdef SO_REUSEADDR
        // NOTE: SO_REUSEADDR allow to reuse sockaddr of TIME_WAIT status
        int reuseaddr = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) {
            perror("setsockopt");
            closesocket(sockfd);
            return NULL;
        }
#endif
        if (bind(sockfd, &addr.sa, sockaddr_len(&addr)) < 0) {
            perror("bind");
            closesocket(sockfd);
            return NULL;
        }
        if (sock_type == SOCK_STREAM) {
            if (listen(sockfd, SOMAXCONN) < 0) {
                perror("listen");
                closesocket(sockfd);
                return NULL;
            }
        }
    }
    io = hio_get(loop, sockfd);
    assert(io != NULL);
    io->io_type = type;
    if (side == HIO_SERVER_SIDE) {
        hio_set_localaddr(io, &addr.sa, sockaddr_len(&addr));
        io->priority = HEVENT_HIGH_PRIORITY;
    } else {
        hio_set_peeraddr(io, &addr.sa, sockaddr_len(&addr));
    }
    return io;
}

        second.调用hio_setcb_accept()函数绑定用户定义的回调函数;

        third.调用hio_accept()函数注册该I/O事件为accept事件.

        C.运行事件循环hloop_run(),该函数内部会调用hloop_process_events()函数,进而会阻塞等待I/O事件.当监听描述符上面出现连接请求时,触发accept事件,将该事件加入待处理事件集合中,然后再调用hio_handle_events()函数(之前关于I/O事件的专题讲过),里面会再次调用nio_accept()函数(nio_accept()函数主要是调用accept()函数从监听socket上与客户端建立连接,返回连接socket,然后建立I/O事件(hio_t类型变量),绑定该连接描述符,但是该I/O事件变量暂时并未绑定任何事件,然后再调用用户定义的回调函数(传入参数就是该监听连接socket所对应的I/O事件),可以从这里面对连接socket绑定相应事件和相应事件对应的用户回调函数);

        对于用户定义的accept回调函数可以根据需要对连接socket所对应的I/O事件绑定读写关闭事件以及对应的回调函数.

 hloop_run(loop);

        D.调用hloop_free()函释放事件回环.

hloop_free(&loop);

二 编程实例

      代码实现:

        以下代码主要是监听0.0.0.0这个IP地址的1234号端口,如果有连接请求,则建立连接,然后客户有数据发过来则回显再发送数据给客户.

#include "hv/hloop.h"

void on_close(hio_t *io){}

void on_send(hio_t* io, const void* buf, int writebytes){
	printf("we send a data to client: %s\n", (char *)buf);
}

void on_recv(hio_t* io, void* buf, int readbytes) {
	hio_setcb_write(io, on_send);
        printf("we received a data from client: %s\n", (char *)buf);
    	hio_write(io, "we reveiced!", 12);//回显数据
}

void on_accept(hio_t *io)
{
	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("there is no port number\n");
		return -1;
	}
	int port = atoi(argv[1]);
	//创建事件循环
	hloop_t *loop = hloop_new(0);
	//创建TCP服务
	hio_t *listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);
	if(listenio == NULL)
	{
		printf("listen socket is created failed\n");
	}
	// 运行事件循环
   	hloop_run(loop);
    	// 释放事件循环
    	hloop_free(&loop);
    	return 0;
}

        下面是运行结果,左图是服务器运行结果,右图是客户端运行结果:

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
学习服务器开发的路线可以按照以下步骤进行: 1. 掌握基础知识:首先,需要学习计算机网络的基本概念和原理,了解网络协议、TCP/IP协议栈等。同时,学习一门编程语言,如Python、Java、C++等,以及相关的网络编程库和框架。 2. 学习Web开发:掌握HTTP协议和RESTful架构,学习常见的Web开发技术,如HTML、CSS、JavaScript等。了解常用的Web开发框架,如Django、Flask、Spring等。 3. 数据库和数据存储:学习数据库的基本概念和使用方法,如关系型数据库(如MySQL、PostgreSQL)和非关系型数据库(如MongoDB、Redis)。了解SQL语言和ORM(Object-Relational Mapping)框架。 4. 学习服务器端开发技术:掌握服务器端开发的相关技术和框架,如Node.js、ASP.NET、Ruby on Rails等。了解服务器端的架构设计、并发处理、负载均衡等概念。 5. 安全和性能优化:学习服务器端的安全防护措施,包括身份验证、数据加密、防止SQL注入等。同时,了解性能优化的方法,如缓存、异步处理等。 在学习过程中,可以选择一些练手项目来巩固所学知识。以下是一些适合的练手项目: 1. 构建简单的静态网站:使用HTML、CSS和JavaScript构建一个简单的静态网站,熟悉前端开发流程。 2. 实现一个简单的API服务:使用服务器端开发技术,构建一个提供数据接口的API服务,学习如何处理HTTP请求和响应。 3. 开发一个论坛或博客系统:使用Web框架和数据库,实现一个简单的论坛或博客系统,包括用户注册、登录、发布文章等功能。 4. 构建一个实时聊天应用:使用WebSocket技术,实现一个支持实时通信的聊天应用,可以学习到实时网络编程的知识。 5. 开发一个电子商务平台:使用服务器端开发技术和数据库,实现一个简单的电子商务平台,包括商品展示、购物车、订单管理等功能。 通过不断地练习和实践,可以逐渐提升自己的服务器开发能力,并深入了解实际项目开发中的各种挑战和解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值