概述
Linux C/C++ websocket协议与服务器实现
- 一、websocket
- 二、握手 handshake
- 三、传输transmission
- 四、完整代码
一、websocket
webService、webSocket、socket、http之间的区别
Http、Socket、WebSocket之间联系与区别
Socket 与 WebSocket
二、握手 handshake
进行TCP三次握手建立连接后
对于 WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输,与 HTTP 无关了
1.客户端发送一个请求给服务器
2.websocket把从中的key(websocketkey)解析出来,解析出来之后,与GUID(固定值,客户端和服务端都率先知道)做一个连接, 对结果做一个哈希,再做一个base64 编码给 客户端
3.客户端就会验证,如果成功了,就会从握手状态进入 Transmission状态
服务端先要把下面的 请求中的 websocketkey给解析出来
这是GUID
接下来
三、传输transmission
传输的格式
如果payload len 长度小于126那么只需要用payload len那一段,如果payload len 长度为126,那么就额外要用蓝色那部分,如果payload len长度为127,那么就还要额外用蓝色和黄色那一部分
1.当长度<126时候
payload Data的位置,要是ev->buffer 多6个字节。
如果不解密的话,直接从客户端发送数据过来,发现是一个密文
文档中看到,有长度为4的数组 mask_key[4]
,
payload[i]=payload[i]^mask_key[i%4]
,来进行umask
这些MASK标志位是由客户端发送过来的,只要置为1了,就要进行unmask,mask_key也是客户端发过来的
四、完整代码
实现websocket
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#define BUFFER_LENGTH 4096
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 8888
#define PORT_COUNT 100
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
//定义几种状态(握手、传输、结束)
enum {
WS_HANDSHARK = 0,
WS_TRANMISSION = 1,
WS_END = 2,
};
//定义websocket 头部操作数 (下面利用了结构体的位域 给每个变量分配空间)
typedef struct _ws_ophdr { //websocket operator holder 操作符
//注意opcode对应的是高位,fin是低位。(要根据网络字节序来)
unsigned char opcode:4,
rsv3:1,
rsv2:1,
rsv1:1,
fin:1;
unsigned char pl_len:7,
mask:1;
} ws_ophdr;
typedef struct _ws_head_126 {
unsigned short payload_length;
char mask_key[4];
} ws_head_126;
typedef struct _ws_head_127 {
long long payload_length;
char mask_key[4];
} ws_head_127;
typedef int NCALLBACK(int ,int, void*);
struct ntyevent {
int fd;
int events;
void *arg;
int (*callback)(int fd, int events, void *arg);
int status;
char buffer[BUFFER_LENGTH];
int length;
long last_active;
int status_machine; //状态
};
struct eventblock {
struct eventblock *next;
struct ntyevent *events;
};
struct ntyreactor {
int epfd;
int blkcnt;
struct eventblock *evblk; //fd --> 100w
};
int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd);
void nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg) {
ev->fd = fd;
ev->callback = callback;
ev->events = 0;
ev->arg = arg;
ev->last_active = time(NULL);
return ;
}
int nty_event_add(int epfd, int events, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
if (ev->status == 1) {
op = EPOLL_CTL_MOD;
} else {
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0) {
printf("event add failed [fd=%d], events[%d]n", ev->fd, events);
return -1;
}
return 0;
}
int nty_event_del(int epfd, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
if (ev->status != 1) {
return -1;
}
ep_ev.data.ptr = ev;
ev->status = 0;
epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev);
return 0;
}
//base64_encode(直接用就行了)
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] = '