我是靠谱客的博主 开放小兔子,最近开发中收集的这篇文章主要介绍websocket服务使用epoll非阻塞模式下接收数据含掩码粘包解包demo前言一、c语言websocket服务使用epoll非阻塞模式下接收数据含掩码粘包问题?二、代码实例总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

通过对epoll的实战应用以及官方说明,对该模式下的网络编程钦佩不已,下面是关于websocke通信下服务接收客户端的实例demo以及前期开发中遇到的问题


提示:紧供参考

一、c语言websocket服务使用epoll非阻塞模式下接收数据含掩码粘包问题?

注意:该demo只针对数据的读入丢包粘包问题,若加入心跳和数据发送需对该demo进行进一步改进,非常抱歉不能将完整的项目代码发与大家分享还请理解

二、代码实例

server_main主函数如下

代码如下(示例):

#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


int nread;
int length;
int hmi_fd;
typedef void (*a)(void);
typedef struct hmievent
{
    a p;
    void *ptr;
    int sockfd;
} HMI_EVENT_T;
void sock_handler(void);
//数据解包
char *de_package(const char *buffer, const int buf_len)
{
    int i = 0;
    char fin;
    char mask_flag;
    char masks[4];
    char *payload_data = NULL;
    char temp[8];
    unsigned int n;
    unsigned int payloadLen = 0, dataStart = 2;
    if (buf_len < 2)
    {
        DLOGD("buf_len less than 2.n");
        return NULL;
    }

    fin = (buffer[0] & 0x80) == 0x80; // 1bit,1表示最后一帧
    if (!fin)
    {
        DLOGD("fin is error.n");
        return NULL; // 超过一帧暂不处理
    }

    mask_flag = (buffer[1] & 0x80) == 0x80; // 是否包含掩码
    if (!mask_flag)
    {
        DLOGD("no mask.n");
        return NULL; // 不包含掩码的暂不处理
    }

    payloadLen = buffer[1] & 0x7F; // 数据长度
    DLOGD("PAYLOADLEN length=%lu", payloadLen);
    if (payloadLen == 126)
    {
        memcpy(masks, buffer + 4, 4);
        payloadLen = (buffer[2] & 0xFF) << 8 | (buffer[3] & 0xFF);
        payload_data = (char *)malloc(payloadLen);
        memset(payload_data, 0, payloadLen);
        memcpy(payload_data, buffer + 8, payloadLen);
        dataStart = 8;
    }
    else if (payloadLen == 127)
    {
        memcpy(masks, buffer + 10, 4);
        for (i = 0; i < 8; i++)
            temp[i] = buffer[9 - i];

        memcpy(&n, temp, 8);
        payload_data = (char *)malloc(n);
        memset(payload_data, 0, n);
        memcpy(payload_data, buffer + 14, n); //toggle error(core dumped) if data is too long.
        payloadLen = n;
        dataStart = 14;
    }
    else
    {
        memcpy(masks, buffer + 2, 4);
        payload_data = (char *)malloc(payloadLen);
        memset(payload_data, 0, payloadLen);
        memcpy(payload_data, buffer + 6, payloadLen);
        dataStart = 6;
    }
    length += (int)payloadLen + (int)dataStart;//每次数据解析的长度
    DLOGD("data length:%dn", length);
    for (i = 0; i < (int)payloadLen; i++)
        payload_data[i] = (char)(payload_data[i] ^ masks[i % 4]);
    DLOGD("data(%ld):%sn", payloadLen, payload_data);
    return payload_data;
} 
void server_main(void)
{
    int listen_fd;
    int conn_fd = -1;
    char buf[REQUEST_LEN_MAX];
    char *data = NULL;
    char str[INET_ADDRSTRLEN];
    char *sec_websocket_key = NULL;
    int port = DEFEULT_SERVER_PORT;
    struct sockaddr_in servaddr;
    struct sockaddr_in cliaddr;
    HMI_EVENT_T ll;
    ll.p = sock_handler;
    ll.ptr = NULL;
    socklen_t cliaddr_len;
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1)
    {
        DLOGD("创建套接字失败");
        return;
    }
    setnonblocking(listen_fd);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);

    if (bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        DLOGD("bind socked failed");
    };

    listen(listen_fd, 20);

    DLOGD("Listen %dnAccepting connections ...n", port);
    cliaddr_len = sizeof(cliaddr);

    int epoll_fd = epoll_create(MAX_EVENTS); //创建一个epoll句柄
    if (epoll_fd == -1)                  //判断句柄是否创建成功
    {
        DLOGD("epoll_create failedn");
    }

    struct epoll_event ev;                 //epoll事件结构体
    struct epoll_event events[MAX_EVENTS]; //事件监听队列
    ev.events = EPOLLIN | EPOLLET;         //表示对应的文件描述符可读(包括对端SOCKET正常关闭)
    ll.sockfd = listen_fd;
    ev.data.ptr = (void *)&ll;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) //注册新的listen_fd到epoll_fd中
    {
        DLOGD("epll_ctl:servaddr register failedn");
    }
    int nfds; //epoll监听事件发生的个数

    while (1) //循环接受客户端请求
    {
        DLOGD("***print client status: %dn", connected);
        nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        //等待事件发生
        //if (nfds == -1 || nfds == 0)
        if (nfds == -1)
        {
            DLOGD("start epoll_wait count=%d failed status: %dn", clientCount, nfds);
            continue; //跳过当次循环
        }
        int i;
        DLOGD("nfds count size is:%dn", nfds);
        for (i = 0; i < nfds; i++)
        {
            if (events[i].events & EPOLLIN)
            { //客户端有数据发送过来

                HMI_EVENT_T *cc = (HMI_EVENT_T *)events[i].data.ptr;
                DLOGD("sockd is:%dn", cc->sockfd);
                if (cc->sockfd == listen_fd)
                {
                    if ((conn_fd = accept(listen_fd, (struct sockaddr *)&cliaddr, &cliaddr_len)) > 0)
                    {
                        DLOGD("From %s at PORT %dn",
                              inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), //将二进制IP地址输出为127.0.0.1,AF_INET6为ipv6
                            ntohs(cliaddr.sin_port));
                        struct epoll_event event = {0};
                        setnonblocking(listen_fd);
                        event.events = EPOLLIN | EPOLLET;
                        HMI_EVENT_T llevent;
                        llevent.p = sock_handler;
                        llevent.sockfd = conn_fd;
                        hmi_fd = conn_fd;
                        event.data.ptr = (void *)&llevent;
                        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event);
                    }
                }
                else
                {
                    memset(buf, 0, sizeof(buf));

                    while ((nread = read(cc->sockfd, buf, REQUEST_LEN_MAX)) > 0)
                    {
                        DLOGD("read buf=%s;jinzhi=%x;buflength=%d", buf, buf, nread);//打印数据长度和结果发现含掩码

                        if (strncmp(buf, "GET", 3) != 0)//若不是连接请求消息则处理解析数据
                        { 
							//数据粘包则循环处理 如果此处直接进行解包处理,会导致再客户端连续数据发送时只收到一条数据的情况大概率就是粘包了
                            while ((nread - length) > 0)
                            {
                                DLOGD("buf_len more than %dn", nread);
                                data = de_package(buf + length, nread);
                                if (data == NULL)
                                {
                                    DLOGD("data is null!!");
                                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, cc->sockfd, &events[i]); //判断data是否为NULL
                                    close(cc->sockfd);                                          //删除文件描述符关闭套接字
                                    break;                                                      //退出当前循环
                                }
                                else
                                {

                                    hmi_handle_data(data, cc->sockfd);
                                }
                            }
                            length = 0;
                        }
                        else
                        {

                            sec_websocket_key = calculate_accept_key(buf);   //传入客户握手buf数据,计算服务器发送的握手key
                            websocket_shakehand(conn_fd, sec_websocket_key); //发送服务端握手信息
                            if (sec_websocket_key != NULL)
                            {
                                DLOGD("websocket shakehand success!!");
                                system("httpd -p 192.168.0.1:9176"); //如果sec_websocket_key(服务端握手key)不为NULL
                                free(sec_websocket_key);             //释放sec_websocket_key(服务端握手key)内存
                                sec_websocket_key = NULL;            //sec_websocket_key(服务端握手key)赋值为NULL
                                connected = 1;
                                //send_network_status(cc->sockfd);注意:若服务端需要发送心跳此处应单独开线程发心跳数据
                            }
                        }
                    }
                    if (nread <= 0)
                    {
                        if (errno != EAGAIN)
                        {
                            DLOGD("read error=%d", nread);
                            epoll_ctl(epoll_fd, EPOLL_CTL_DEL, cc->sockfd, &events[i]);
                            //epoll_del(epoll_fd, events[i].data.fd);
                            close(cc->sockfd); //删除文件描述符关闭套接字
                            break;
                        }
                    }
                }
            }
        }
    }
} /* ----- End of main() ----- */
static void setnonblocking(int fd)
{
    int opts;
    opts = fcntl(fd, F_GETFL);
    if (opts < 0)
    {
        DLOGD("fcntl F_GETFL:");
        return;
    }
    opts = (opts | O_NONBLOCK);
    if (fcntl(fd, F_SETFL, opts) < 0)
    {
        DLOGD("fcntl F_SETFL:");
    }
}


总结

epoll一般建议使用非阻塞模式至于具体原因根据大家项目需求来定,以上关于解决的粘包紧限于个人项目的出现情况做出的方案,由于根据测试发现每次接收的粘包都是完整的没有出现一个半或半个包的这种情况。所以给大家提供了一个方案思路。

链接: link.

最后

以上就是开放小兔子为你收集整理的websocket服务使用epoll非阻塞模式下接收数据含掩码粘包解包demo前言一、c语言websocket服务使用epoll非阻塞模式下接收数据含掩码粘包问题?二、代码实例总结的全部内容,希望文章能够帮你解决websocket服务使用epoll非阻塞模式下接收数据含掩码粘包解包demo前言一、c语言websocket服务使用epoll非阻塞模式下接收数据含掩码粘包问题?二、代码实例总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部