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

代码已经实现,放在个人github上面,地址:https://github.com/jice1001/websocket.git。下面进行解释说明:

一、原理

  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<const unsigned char*>(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相关功能,最大程度的与项目保存兼容。

WebSocket的C++服务器端实现的更多相关文章

  1. HTML5_07之WebSocket

    1.HTML5新特性之WebSocket: ①HTTP协议的不足:基于“请求——响应”模型,只有在客户端发送请求后,服务器才会给予响应:对于实时的股票走势图,以及聊天通讯等无法满足需求: ②解决方案: ...

  2. 浅谈php中使用websocket

    在PHP中,开发者需要考虑的东西比较多,从socket的连接.建立.绑定.监听等都需要开发者自己去操作完成,对于初学者来说,难度方面也挺大的,所以本文的思路如下: 1.socket协议的简介 2.介绍 ...

  3. 一步一步学WebSocket (一) 初识WebSocket

    众所周知,Http协议是无状态的,并且是基于Request/Response的方式与服务器进行交互,也就是我们常说的单工模式.但是随着互联网的发展,浏览器与服务端进行双向通信需求的增加,长轮询向服务器 ...

  4. python tornado websocket 多聊天室(返回消息给部分连接者)

    python tornado 构建多个聊天室, 多个聊天室之间相互独立, 实现服务器端将消息返回给相应的部分客户端! chatHome.py // 服务器端, 渲染主页 --> 聊天室建立web ...

  5. 初识WebSocket

    众所周知,Http协议是无状态的,并且是基于Request/Response的方式与服务器进行交互,也就是我们常说的单工模式.但是随着互联 网的发展,浏览器与服务端进行双向通信需求的增加,长轮询向服务 ...

  6. HTML5 直播协议之 WebSocket 和 MSE

    当前为了满足比较火热的移动 Web 端直播需求, 一系列的 HTML5 直播技术迅速的发展了起来. 常见的可用于 HTML5 的直播技术有 HLS, WebSocket 与 WebRTC. 今天我要向 ...

  7. WebSocket技术

    webSocket技术 在html5技术革新中,加入了WebSocket技术 1.webSocket实际是TCP连接 webSocket在最初将发送http连接请求到服务器端, 但是在header中加 ...

  8. WebSocket实时异步通信

    WebSocket实时异步通信 [一]WebSocket简介 WebSocket是HTML5推出一个协议规范,用来B/S模式中服务器端和客户端之间进行实时异步通信. 众所周知,传统的HTTP协议中,服 ...

  9. WebSocket与Tcp连接

    最近做了一个项目,客户端为WebSocket页面,服务器端为Tcp控制台 .将代码贴出来,供需要的参考. 1.服务器端代码 其中服务器的Session使用了第三方插件,为TCP连接. 2.客户端代码如 ...

随机推荐

  1. Rate Monotonic Scheduling algorithm

    这篇文章写得不错 http://barrgroup.com/embedded-systems/How-To/RMA-Rate-Monotonic-Algorithm 另外rtems的官方文档也有类似说 ...

  2. centos 6.9 x86 安装搭建hadoop集群环境

    又来折腾hadoop了 文件准备: centos 6.9 x86 minimal版本 163的源 下软件的时候可能会用到 jdk-8u144-linux-i586.tar.gz ftp工具 putty ...

  3. 读懂JVM垃圾收集日志

    JVM垃圾收集搞懂原理很重要,但是连垃圾收集的日志都不懂,这tmd还分析个屁的问题啊,典型的空有理论知识,动手实践为零.本篇就来具体的学习一下JVM中的垃圾收集日志. 第一步,垃圾收集的选项 图1 J ...

  4. JS基础常识理解

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. rtsp转rtmp、hls网页直播服务器EasyNVR前端兼容性调试:ie下的 pointer-events- none

    发现问题: 之前在做EasyNVR 的web页面开发过程中,力求的都是一个播放效果的.功能的展示.对于兼容性也有注意,但有些细节还是难免有所疏忽. 内部测试发现:由于我们是流媒体的实时视频直播,在we ...

  6. Angular入门(一) 环境配置

    angular/cli 安装 ♦ npm uninstall -g angular-cli /cnpm install -g angular-cli ※采用npm安装失败: Missing write ...

  7. LRM-00109: could not open parameter file

    SQL>startup                                                                                       ...

  8. 九度OJ 1169:比较奇偶数个数 (基础题)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:9459 解决:3146 题目描述: 第一行输入一个数,为n,第二行输入n个数,这n个数中,如果偶数比奇数多,输出NO,否则输出YES. 输入 ...

  9. c语言高速推断一个数是偶数还是奇数

    #include <stdio.h> int main() { int a; while(1) { printf("please input the number:\n" ...

  10. phpPHP创建创建jpg格式图片以及压缩图片(转)

    其实是因为一些业务上的需求,所以需要对用户上传后的图片进行压缩,因为上传的图片比较大,显示的时候加载起来如果网速不给力的话就很吃力了,而且大图片也浪费空间,于是找了一下相关的资源,主要方法在开源中国上 ...