热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Unix/Linux编程:网络编程之基于Reactor实现WebSocket服务

使用到的上一篇文章中基于epoll实现的Reactor模型。OpenSSL使用其中的SHA1,base64encode等库openSSL源码使用1.1.0l$ta

使用到的上一篇文章中基于epoll实现的Reactor模型。

OpenSSL使用其中的SHA1,base64 encode等库

openSSL源码使用1.1.0l

 

$ tar xzvf OpenSSL-1.1.0l.tar.gz
$ cd OpenSSL-1.1.0l
$ ./config –-prefix=/usr/local/openssl
$ make
$ sudo make install

安装完以后需要将openSSL的库和头文件添加到默认的搜索路径。否则后续编译需要加一大串 -I "...." -L "..." 这样的命令

执行

这是给全局用户修改

sudo vim /etc/profile

加入这两行然后重新登录shell后,gcc会将上面路径默认添加到搜索路径。

之后就可以编译了比如

websocket简介 参考文档RFC6455

1. 握手 基于http

2. 传输,握手后可以传输自己的应用层定义数据。 websocket是双向的,服务器也可以主动通知客户端。

2. 数据帧

 

websocket协议的实现

/* Server based on EPOLL Reactor Model* compile command: gcc -I /usr/local/openssl/include -L /usr/local/openssl/lib websocket_server.c -o websocket_server -lcrypto* if the environment variable has been added. just add the '-lcrypto' for compile.*/
#include
#include
#include
#include
#include
#include
#include #include
#include
#include
#include
#include
#include #include // import openssl for sha-1
#include
#include
#include #define BUFFER_LENGTH 1024
#define EVENT_SIZE 1024
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"// define the state machine for web socket protocolenum WS_STATUS {WS_INIT = 0,WS_HANDSHAKE = 1,WS_DATATRANSFORM = 2,WS_DATAEND
};struct sockitem {int sockfd;int (*callback)(int events, void* arg);char recvbuffer[BUFFER_LENGTH];char sendbuffer[BUFFER_LENGTH];int rlength; // length of the received bufferint slength; // length of the send buffer.int status;
};struct reactor{int epollfd;struct epoll_event events[EVENT_SIZE];
};// store for big-endian. // for FIN RSV1 RSV2 RSV3 opcode
struct _websocket_ophdr {unsigned char opcode:4,rsv3:1,rsv2:1,rsv1:1,fin:1;unsigned char payload_length:7,mask:1;
}__attribute__((packed));// struct for RFC6455 Websocket data frame.
struct _websocket_head_126 {// length:1 0-->125// length:2 126 --> 2^23// length:3 127 --> 2^71unsigned short payload_length;char mask_key[4];unsigned char data[8];
}__attribute__((packed));struct _websocket_head_127 {unsigned long long payload_length;char mask_key[4];unsigned char data[8];
} __attribute__((packed));typedef struct _websocket_head_127 websocket_head_127;
typedef struct _websocket_head_126 websocket_head_126;
typedef struct _websocket_ophdr ophdr;struct reactor * g_eventloop = NULL;//==============function declaration==============
int recv_cb(int events, void* arg);
char* decode_packet(char *stream, char *mask, int length, int *ret);
int encode_packet(char *buffer,char *mask, char *stream, int length);
//===============end declaration================== int send_cb(int events, void *arg) {if (!(events & EPOLLOUT) || arg == NULL)return -1;struct sockitem* si = (struct sockitem*)arg;int clientfd = si->sockfd;int epoll_fd = g_eventloop->epollfd;if (send(clientfd, si->sendbuffer, si->slength, 0)== -1) {if(errno == EAGAIN || errno == EWOULDBLOCK) {// the send buffer is full.// add send into EPOLLOUT event.return 0;}// send data error maybe some network issue.perror("send error");close(clientfd);return -1;}// change state into EPOLLINstruct epoll_event ev;ev.events = EPOLLIN | EPOLLET;//WS_DATATRANSFORM; // once we have finished the transfer of header. change the state.if(si->status == WS_HANDSHAKE)si->status++; si->callback = recv_cb;ev.data.ptr = si;epoll_ctl(epoll_fd, EPOLL_CTL_MOD, clientfd, &ev);return 0;
}int base64_encode(char *in_str, int in_len, char *out_str) { BIO *b64, *bio; BUF_MEM *bptr = NULL; size_t size = 0; if (in_str == NULL || out_str == NULL) return -1; b64 = BIO_new(BIO_f_base64()); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio);BIO_write(bio, in_str, in_len); BIO_flush(bio); BIO_get_mem_ptr(bio, &bptr); memcpy(out_str, bptr->data, bptr->length); out_str[bptr->length-1] = '\0'; size = bptr->length; BIO_free_all(bio); return size;
}int readline(char *allbuf, int level, char*linebuf) {int len = strlen(allbuf);for(; level }// implement for state handshake of the websocket protocol.
int handshake(struct sockitem *si, struct reactor *mainloop) {char linebuf[BUFSIZ] = {0};char sec_accept[32] = {0};unsigned char sha1_data[SHA_DIGEST_LENGTH + 1] = {0};char head[BUFFER_LENGTH] = {0};int level = 0;// si->recvbuffer, si->rlength;do {memset(linebuf, 0, sizeof(linebuf));level = readline(si->recvbuffer, level, linebuf);// if this line container the Sec-WebSocket-Key.if(strstr(linebuf, "Sec-WebSocket-Key") != NULL) {strcat(linebuf, GUID); // add the GUID// encode by SHA1 alrogithm from openSSLSHA1((unsigned char*)&linebuf + 19, strlen(linebuf + 19), (unsigned char*)&sha1_data);base64_encode(sha1_data, strlen(sha1_data), sec_accept);sprintf(head, "HTTP/1.1 101 Switching Protocols\r\n" \"Upgrade: websocket\r\n" \"Connection: Upgrade\r\n" \"Sec-WebSocket-Accept: %s\r\n" \"\r\n" , sec_accept);printf("response\n");printf("%s\n\n\n", head);memset(si->sendbuffer, 0, BUFFER_LENGTH);memset(si->recvbuffer, 0, BUFFER_LENGTH);// copy head to sendbuffersi->slength = strlen(head);memcpy(si->sendbuffer, head, si->slength);si->rlength = 0;struct epoll_event ev;ev.events = EPOLLOUT | EPOLLET;//si->sockfd = si->sockfd;si->callback = send_cb;ev.data.ptr = si;epoll_ctl(mainloop->epollfd, EPOLL_CTL_MOD, si->sockfd, &ev);break;}} while((si->recvbuffer[level] != 'r' || si->recvbuffer[level + 1] != '\n') && level!= -1);return 0;
}// implementation for data transform for WS_DATATRANSFORM state.
int datatransform(struct sockitem *si, struct reactor *mainloop) {// si->status --> WS_DATAFRANSFORM// si->recvbufferint ret = 0;char mask[4] = {0};char *data = decode_packet(si->recvbuffer, mask, si->rlength, &ret);// process the data frame from the client.printf("data : %s , length : %d\n", data, ret);ret = encode_packet(si->sendbuffer, mask, data, ret);si->slength = ret;memset(si->recvbuffer, 0, BUFFER_LENGTH);struct epoll_event ev;ev.events = EPOLLOUT | EPOLLET;// si->sockfd = si->sockfd;si->callback = send_cb;si->status = WS_DATATRANSFORM;ev.data.ptr = si;epoll_ctl(mainloop->epollfd, EPOLL_CTL_MOD, si->sockfd, &ev);
}void umask(char *data,int len,char *mask) { int i; for (i &#61; 0;i }char* decode_packet(char *stream, char *mask, int length, int *ret) {ophdr *hdr &#61; (ophdr*)stream;unsigned char *data &#61; stream &#43; sizeof(ophdr);int size &#61; 0;int start &#61; 0;//char mask[4] &#61; {0};int i &#61; 0;//if (hdr->fin &#61;&#61; 1) return NULL;if ((hdr->mask & 0x7F) &#61;&#61; 126) {websocket_head_126 *hdr126 &#61; (websocket_head_126*)data;size &#61; hdr126->payload_length;for (i &#61; 0;i <4;i &#43;&#43;) {mask[i] &#61; hdr126->mask_key[i];}start &#61; 8;} else if ((hdr->mask & 0x7F) &#61;&#61; 127) {websocket_head_127 *hdr127 &#61; (websocket_head_127*)data;size &#61; hdr127->payload_length;for (i &#61; 0;i <4;i &#43;&#43;) {mask[i] &#61; hdr127->mask_key[i];}start &#61; 14;} else {size &#61; hdr->payload_length;memcpy(mask, data, 4);start &#61; 6;}*ret &#61; size;umask(stream&#43;start, size, mask);return stream &#43; start;
}int encode_packet(char *buffer,char *mask, char *stream, int length) {ophdr head &#61; {0};head.fin &#61; 1;head.opcode &#61; 1;int size &#61; 0;if (length <126) {head.payload_length &#61; length;memcpy(buffer, &head, sizeof(ophdr));size &#61; 2;} else if (length <0xffff) {websocket_head_126 hdr &#61; {0};hdr.payload_length &#61; length;memcpy(hdr.mask_key, mask, 4);memcpy(buffer, &head, sizeof(ophdr));memcpy(buffer&#43;sizeof(ophdr), &hdr, sizeof(websocket_head_126));size &#61; sizeof(websocket_head_126);} else {websocket_head_127 hdr &#61; {0};hdr.payload_length &#61; length;memcpy(hdr.mask_key, mask, 4);memcpy(buffer, &head, sizeof(ophdr));memcpy(buffer&#43;sizeof(ophdr), &hdr, sizeof(websocket_head_127));size &#61; sizeof(websocket_head_127);}memcpy(buffer&#43;2, stream, length);return length &#43; 2;
}int recv_cb(int events, void* arg) {if (!(events & EPOLLIN) || arg &#61;&#61; NULL)return -1;struct sockitem* si &#61; (struct sockitem*)arg;int clientfd &#61; si->sockfd;int epoll_fd &#61; g_eventloop->epollfd;// char buffer[BUFFER_LENGTH] &#61; { 0 };struct epoll_event ev;int ret &#61; recv(clientfd, si->recvbuffer, BUFFER_LENGTH, 0);if (ret <0) {if (errno &#61;&#61; EAGAIN || errno &#61;&#61; EWOULDBLOCK) {printf("read all data\n");}close(clientfd);ev.events &#61; EPOLLIN | EPOLLET;//ev->data.fd &#61; clientfd;ev.data.ptr &#61; NULL;free(si);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);}else if (ret &#61;&#61; 0) {printf(" disconnect clientfd:%d\n", clientfd);close(clientfd);ev.events &#61; EPOLLIN | EPOLLET;//ev->data.fd &#61; clientfd;ev.data.ptr &#61; NULL;free(si);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);return 0;}else {printf("Recv: \n%.*s\nTotal: %d Bytes\n", ret, si->recvbuffer, ret);
#if 0si->rlength &#61; ret;memcpy(si->sendbuffer, si->recvbuffer, si->rlength);si->slength &#61; si->rlength;
#else// state machine.if (si->status &#61;&#61; WS_HANDSHAKE) {handshake(si, g_eventloop);} else if (si->status &#61;&#61; WS_DATATRANSFORM) {datatransform(si, g_eventloop);} else if (si->status &#61;&#61; WS_DATAEND) {} else {assert(0);}#endif }
}// callback handler for accept events of the sockfd.
int accept_cb(int events, void* arg) {if (!(events & EPOLLIN) || arg &#61;&#61; NULL)return -1;struct sockitem* psi &#61; (struct sockitem*)arg;int epoll_fd &#61; g_eventloop->epollfd;int sockfd &#61; psi->sockfd;struct sockaddr_in client_addr;struct epoll_event ev;memset(&client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len &#61; sizeof(client_addr);int clientfd &#61; accept(sockfd, (struct sockaddr*)&client_addr, &client_len);if (clientfd <&#61; 0)return -1; // do nothing.char str[INET_ADDRSTRLEN] &#61; { 0 };struct sockitem* si &#61; (struct sockitem*)malloc(sizeof(struct sockitem));si->sockfd &#61; clientfd;si->callback &#61; recv_cb;si->status &#61; WS_HANDSHAKE;printf("received from %s at port:%d, sockfd:%d, clientfd:%d\n",inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),ntohs(client_addr.sin_port), sockfd, clientfd);ev.events &#61; EPOLLIN | EPOLLET;ev.data.ptr &#61; si;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientfd, &ev);
}// ./epoll 8080.
int main(int argc, char* argv[]) {if (argc <2) {printf("parameter error!\n");exit(EXIT_FAILURE);}int port &#61; atoi(argv[1]);int sockfd &#61; socket(AF_INET, SOCK_STREAM, 0);if (sockfd <0) {return -1;}struct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family &#61; AF_INET;addr.sin_port &#61; htons(port); // convert network bytesaddr.sin_addr.s_addr &#61; INADDR_ANY;if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) <0) {exit(EXIT_FAILURE);}printf("start server and wait for connection...\n");if (listen(sockfd, 5) <0) {exit(EXIT_FAILURE);}g_eventloop &#61; (struct reactor*) malloc(sizeof(struct reactor));// epoll coding.g_eventloop->epollfd &#61; epoll_create(1); // create the epoll root node for epoll object.struct epoll_event ev;ev.events &#61; EPOLLIN;//ev.data.fd &#61; sockfd;// define socketitemstruct sockitem* si &#61; (struct sockitem*)malloc(sizeof(struct sockitem));si->sockfd &#61; sockfd;si->status &#61; WS_INIT;si->callback &#61; accept_cb;ev.data.ptr &#61; si;epoll_ctl(g_eventloop->epollfd, EPOLL_CTL_ADD, sockfd, &ev);while (1) {// condition wait.// max amount of events could be caught by 1 epoll_wait call.int nready &#61; epoll_wait(g_eventloop->epollfd, g_eventloop->events, EVENT_SIZE, -1);if (nready <-1) {break;}int i &#61; 0;for (i &#61; 0; i events[i].events & EPOLLIN) {struct sockitem* si &#61; (struct sockitem*)g_eventloop->events[i].data.ptr;if(si && si->callback)si->callback(g_eventloop->events[i].events, si);}if (g_eventloop->events[i].events & EPOLLOUT) {struct sockitem* si &#61; (struct sockitem*)g_eventloop->events[i].data.ptr;if(si && si->callback)si->callback(g_eventloop->events[i].events, si);}}}close(sockfd);exit(EXIT_SUCCESS);
}

测试&#xff1a;

客户端

服务端&#xff1a;


推荐阅读
  • 第四讲ApacheLAMP服务器基本配置Apache的编译安装从Apache的官方网站下载源码包:http:httpd.apache.orgdownload.cgi今 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • centos6.8 下nginx1.10 安装 ... [详细]
  • Howtobuilda./configure&&make&&makeins ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 图片复制到服务器 方向变了_双服务器热备更新配置文件步骤问题及解决方法
    本文介绍了在将图片复制到服务器并进行方向变换的过程中,双服务器热备更新配置文件所出现的问题及解决方法。通过停止所有服务、更新配置、重启服务等操作,可以避免数据中断和操作不规范导致的问题。同时还提到了注意事项,如Avimet版本的差异以及配置文件和批处理文件的存放路径等。通过严格执行切换步骤,可以成功进行更新操作。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文主要讨论了如何通过已知图片的base64流将图片上传到文件服务器并返回URL的问题。通过模拟文件上传过程,成功解决了该问题。然而,在返回的URL中出现了一个名为blob的文件,作者对于该文件的具体含义以及base64转blob格式的意义有所困惑。本文将对这些问题进行探讨和解答。 ... [详细]
  • loader资源模块加载器webpack资源模块加载webpack内部(内部loader)默认只会处理javascript文件,也就是说它会把打包过程中所有遇到的 ... [详细]
  • 对于一般的扩展包,我们一般直接pipinstallxxx即可安装,但是unrar直接安装后,发现并不能通过Python程序实现解压的功能& ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • CentOS7.8下编译muduo库找不到Boost库报错的解决方法
    本文介绍了在CentOS7.8下编译muduo库时出现找不到Boost库报错的问题,并提供了解决方法。文章详细介绍了从Github上下载muduo和muduo-tutorial源代码的步骤,并指导如何编译muduo库。最后,作者提供了陈硕老师的Github链接和muduo库的简介。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • PHP连接MySQL的2种方法小结以及防止乱码【PHP】
    后端开发|php教程PHP,MySQL,乱码后端开发-php教程PHP的MySQL配置报错信息:ClassmysqlinotfoundinAnswer:1.在confphp.ini ... [详细]
author-avatar
奇丶葩故事会
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有