0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

根据WebSocket协议完全使用C++实现函数

C语言专家集中营 来源:未知 2018-11-28 14:29 次阅读

由于需要在项目中增加Websocket协议,与客户端进行通信,不想使用开源的库,比如WebSocketPP,就自己根据WebSocket协议实现一套函数,完全使用C++实现。

代码已经实现,放在个人github上面,下面进行解释说明:

一、原理

Websocket协议解析,可以参考博客http://www.cnblogs.com/jice1990/p/5435419.html,这里就不详细细说。

服务器端实现就是使用TCP协议,使用传统的socket流程进行绑定监听,使用epoll控制多路并发,收到Websocket握手包时候进行握手处理,握手成功便可进行数据收发。

二、实现

1、服务器监听

该部分使用的是TCP socket流程,首先是通过socket函数建立socket,通过bind函数绑定到某个端口,本例使用的是9000,然后通过listen函数开启监听,代码如下:

listenfd_ = socket(AF_INET, SOCK_STREAM, 0); if(listenfd_ == -1){

DEBUG_LOG("创建套接字失败!"); return -1; } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(sockaddr_in)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); if(-1 == bind(listenfd_, (struct sockaddr *) (&server_addr), sizeof(server_addr))){ DEBUG_LOG("绑定套接字失败!"); return -1; } if(-1 == listen(listenfd_, 5)){ DEBUG_LOG("监听失败!"); return -1; }

2、epoll控制多路并发

该部分使用的是epoll流程,首先在初始化时候使用epoll_create创建epoll句柄

epollfd_ = epoll_create(1024);

然后通过epoll_wait等待fd事件来临,当监听到是listenfd事件时候,说明是客户端连接服务器,就使用accept接受连接,然后注册该连接EPOLLIN事件,当epoll监听到EPOLLIN事件时候,即可进行握手和数据读取。代码如下:

void ctl_event(int fd, bool flag){ struct epoll_event ev; ev.data.fd = fd; ev.events = flag ? EPOLLIN : 0; epoll_ctl(epollfd_, flag ? EPOLL_CTL_ADD : EPOLL_CTL_DEL, fd, &ev); if(flag){ set_noblock(fd); websocket_handler_map_[fd] = new Websocket_Handler(fd); if(fd != listenfd_) DEBUG_LOG("fd: %d 加入epoll循环", fd); } else{ close(fd); delete websocket_handler_map_[fd]; websocket_handler_map_.erase(fd); DEBUG_LOG("fd: %d 退出epoll循环", fd); } }int epoll_loop(){ struct sockaddr_in client_addr; socklen_t clilen; int nfds = 0; int fd = 0; int bufflen = 0; struct epoll_event events[MAXEVENTSSIZE]; while(true){ nfds = epoll_wait(epollfd_, events, MAXEVENTSSIZE, TIMEWAIT); for(int i = 0; i < nfds; i++){           if(events[i].data.fd == listenfd_){                fd = accept(listenfd_, (struct sockaddr *)&client_addr, &clilen);                ctl_event(fd, true);            }            else if(events[i].events & EPOLLIN){                     if((fd = events[i].data.fd) < 0)                               continue;                Websocket_Handler *handler = websocket_handler_map_[fd];                if(handler == NULL)                    continue;                               if((bufflen = read(fd, handler->getbuff(), BUFFLEN)) <= 0) {                ctl_event(fd, false);                }                else{                    handler->process(); } } } } return 0; }

3、Websocket握手连接

握手部分主要是根据Websocket握手包进行解析,然后根据Sec-WebSocket-Key进行SHA1哈希,生成相应的key,返回给客户端,与客户端进行握手。代码如下:

//该函数是获取websocket握手包的信息,按照分割字符进行解析int fetch_http_info(){ std::istringstream s(buff_); std::string request; std::getline(s, request); if (request[request.size()-1] == '\r') { request.erase(request.end()-1); } else { return -1; } std::string header; std::string::size_type end; while (std::getline(s, header) && header != "\r") { if (header[header.size()-1] != '\r') { continue; //end } else { header.erase(header.end()-1); //remove last char } end = header.find(": ",0); if (end != std::string::npos) { std::string key = header.substr(0,end); std::string value = header.substr(end+2); header_map_[key] = value; } } return 0; }//该函数是根据websocket返回包的格式拼接相应的返回包void parse_str(char *request){ strcat(request, "HTTP/1.1 101 Switching Protocols\r\n"); strcat(request, "Connection: upgrade\r\n"); strcat(request, "Sec-WebSocket-Accept: "); std::string server_key = header_map_["Sec-WebSocket-Key"]; server_key += MAGIC_KEY; SHA1 sha; unsigned int message_digest[5]; sha.Reset(); sha << server_key.c_str();    sha.Result(message_digest);    for (int i = 0; i < 5; i++) {        message_digest[i] = htonl(message_digest[i]);    }    server_key = base64_encode(reinterpret_cast(message_digest),20); server_key += "\r\n"; strcat(request, server_key.c_str()); strcat(request, "Upgrade: websocket\r\n\r\n"); }

4、数据读取

当服务器与客户端握手成功后,就可以进行正常的通信,读取数据了。使用的是TCP协议的方法,解析Websocket包根据协议格式,在前面博客里面有详细分析,这里只把实现代码贴出来。

int fetch_websocket_info(char *msg){ int pos = 0; fetch_fin(msg, pos); fetch_opcode(msg, pos); fetch_mask(msg, pos); fetch_payload_length(msg, pos); fetch_masking_key(msg, pos); return fetch_payload(msg, pos); } int fetch_fin(char *msg, int &pos){ fin_ = (unsigned char)msg[pos] >> 7; return 0; }int fetch_opcode(char *msg, int &pos){ opcode_ = msg[pos] & 0x0f; pos++; return 0; }int fetch_mask(char *msg, int &pos){ mask_ = (unsigned char)msg[pos] >> 7; return 0; }int fetch_masking_key(char *msg, int &pos){ if(mask_ != 1) return 0; for(int i = 0; i < 4; i++)        masking_key_[i] = msg[pos + i];    pos += 4;    return 0; }int fetch_payload_length(char *msg, int &pos){    payload_length_ = msg[pos] & 0x7f;    pos++;    if(payload_length_ == 126){        uint16_t length = 0;        memcpy(&length, msg + pos, 2);        pos += 2;        payload_length_ = ntohs(length);    }     else if(payload_length_ == 127){        uint32_t length = 0;        memcpy(&length, msg + pos, 4);        pos += 4;        payload_length_ = ntohl(length);    }     return 0; }int fetch_payload(char *msg, int &pos){    memset(payload_, 0, sizeof(payload_));    if(mask_ != 1){        memcpy(payload_, msg + pos, payload_length_);    }     else {         for(uint i = 0; i < payload_length_; i++){            int j = i % 4;            payload_[i] = msg[pos + i] ^ masking_key_[j];        }    }    pos += payload_length_;     return 0; }

5、总结

到此为止,完整实现了使用C++对Websocket协议进行解析,握手,数据收发,不借助开源库就实现了websocket相关功能,最大程度的与项目保存兼容。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73623
  • WebSocket
    +关注

    关注

    0

    文章

    29

    浏览量

    3745

原文标题:WebSocket的C++服务器端实现

文章出处:【微信号:C_Expert,微信公众号:C语言专家集中营】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Django3如何使用WebSocket实现WebShell

    websocket 服务。 大致看了下觉得这不够有趣,翻了翻 django 的官方文档发现 django 原生是不支持 websocket 的,但 django3 之后支持了 asgi 协议可以自己
    的头像 发表于 11-17 09:58 4366次阅读

    基于TCP的一种新的网络协议WebSocket

    开启 WebSocket 服务WebSocket 服务是网页程序、安卓 App、微信小程序等获得数据和服务的接口,是基于TCP 的一种新的网络协议,它实现了浏览器与服务器全双工通信。通
    发表于 12-16 07:38

    C++教程之函数的递归调用

    C++教程之函数的递归调用 在执行函数 f 的过程中,又要调用 f 函数本身,称为函数的递归调用;形式上:一个正在执行的
    发表于 05-15 18:00 35次下载

    基于C++的modbus通讯协议模型实现

    基于C++的modbus通讯协议模型实现,很好的资料,快来下载学习吧。
    发表于 03-21 17:27 55次下载

    如何在中断C函数中调用C++

    之前,我们在单片机程序开发时都会面对中断函数。众所周知的,这个中断函数肯定是要用C函数来定义的。我在用C++进行程序开发的时候就发现了一个需
    发表于 05-09 18:17 0次下载
    如何在中断<b class='flag-5'>C</b><b class='flag-5'>函数</b>中调用<b class='flag-5'>C++</b>

    WebSocket有什么优点

    WebSocket是一种在单个TCP连接上进行全双工通信的协议WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。
    的头像 发表于 02-15 15:53 8299次阅读
    <b class='flag-5'>WebSocket</b>有什么优点

    C++之重载函数学习总结

    函数重载是c++c的一个重要升级;函数重载通过参数列表区分不同的同名函数;extern关键字能够实现
    的头像 发表于 12-24 17:10 794次阅读

    EE-128:C++中的DSP:从C++调用汇编类成员函数

    EE-128:C++中的DSP:从C++调用汇编类成员函数
    发表于 04-16 17:04 2次下载
    EE-128:<b class='flag-5'>C++</b>中的DSP:从<b class='flag-5'>C++</b>调用汇编类成员<b class='flag-5'>函数</b>

    C++中如何用虚函数实现多态

    01 — C++函数探索 C++是一门面向对象语言,在C++里运行时多态是由虚函数和纯虚函数
    的头像 发表于 09-29 14:18 1694次阅读

    C++ C语言函数查询电子版下载

    C++ C语言函数查询电子版下载
    发表于 01-18 10:15 0次下载

    函数C++开发者如何有效利用

    函数是基类中声明的成员函数,且使用者期望在派生类中将其重新定义。那么,在 C++ 中,什么是虚函数呢?在 C++ 中,通常将虚
    的头像 发表于 02-11 09:39 932次阅读

    深度解析C++中的虚函数

    函数作为C++的重要特性,让人又爱又怕,爱它功能强大,但又怕驾驭不好,让它反咬一口,今天我们用CPU的角度,撕掉语法的伪装,重新认识一下虚函数。 虚函数
    的头像 发表于 02-15 11:14 828次阅读
    深度解析<b class='flag-5'>C++</b>中的虚<b class='flag-5'>函数</b>

    C++基础知识之函数1

    函数C++ 中的一个重要概念,它可以让我们将一段代码封装起来,然后在需要的时候调用它。C++ 中的函数有以下几个特点: * 函数
    的头像 发表于 04-03 10:34 568次阅读

    websocket协议的原理

    WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
    的头像 发表于 11-09 15:13 1172次阅读
    <b class='flag-5'>websocket</b><b class='flag-5'>协议</b>的原理

    同样是函数,在CC++中有什么区别

    同样是函数,在 CC++ 中有什么区别? 第一个返回值。 C语言的函数可以不写返回值类型,编译器会默认为返回 int。 但是
    的头像 发表于 11-29 10:25 274次阅读