websocket和http的瓜葛以及websocket协议实现「建议收藏」

websocket和http的瓜葛以及websocket协议实现「建议收藏」websocket和http的瓜葛以及websocket协议实现,方法论,如何在现有的tcp传输层协议上封装应用层协议

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

目录

前言.

websocket和http的瓜葛

http的弊端引出为什么需要websocket

引出问题   —   服务器无法主动向客户端发送数据,  如果服务端存在一定地状态变更, 却无法实时地主动向客户端推送这个数据

解决问题   —   websocket全双工地通讯协议地诞生, 服务器可以主动向客户端发送数据

websocket的特点

报文分析

websocket在我们生活中的实例场景(服务器(后端)向网页客户端(前端)实时刷新数据)

websocket协议的实现分块分析, 如何在reactor的基础上封装websocket应用层协议 (哪些协议究竟是如何封装实现的)

过程分析

 握手细节:

基于TCP连接完成之后,进行一次握手的意义 

细节分析:   如何区别握手数据  和  普通交互数据 ? 

握手细节核心: Sec-WebSocket-Key     —>   Sec-WebSocket-Accept

transform 数据推送的细节            —    数据封包和解包.  

做自定义协议必须的三部分.    基于tcp的自定义应用层协议

线上测试工具  +  我的测试结果

总结本文


前言.

  • 由于本文的websocket的实现我是基于reactor写的, 所以需要用到我之前写的reactor实现的部分代码, 如果对于reactor不太熟悉的友友可以去康康

epoll高度封装reactor,几乎所有可见服务器的底层框架_小杰312的博客-CSDN博客epoll高度封装reactor,几乎所有可见服务器的底层框架websocket和http的瓜葛以及websocket协议实现「建议收藏」https://blog.csdn.net/weixin_53695360/article/details/123894158?spm=1001.2014.3001.5502

websocket和http的瓜葛

http的弊端引出为什么需要websocket

  • http是一种无状态, 无连接,   非持久化 的单向半双工应用层协议
  • 啥叫作无状态, 对于历史连接是完全没有记忆的, 每一次连接都是新的连接
  • 无连接的和非持久化其实是一个意思, 一次请求, 一次响应, 不会持续.
  • 单向半双工指的是 通信请求只能由客户端发起, 服务端只能对于请求做出应答, 服务端不能主动地向客户端发送数据

引出问题   —   服务器无法主动向客户端发送数据,  如果服务端存在一定地状态变更, 却无法实时地主动向客户端推送这个数据

  • 针对上述问题, 最开始地时候还是使用http, 只不过通过定时轮询长轮询地方式来解决服务器需要向客户端主动发送数据地问题.

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • 定时轮询, 不断地定时地询问服务器, 你需要发送消息吗, 不断地请求, 定时发出询问, 如果服务器需要发送数据到客户端, 询问来了就可以发送状态变更,数据到客户端了
  • 定时轮询地弊端:可能存在很严重地延时性, 而且不断地轮询及其浪费占用服务端地资源, 增大服务端压力,不断地建立连接浪费资源          存在很多无效请求        服务器表示,不停的建立连接,大量消耗我的带宽和资源, 我需要很快的处理连接 , 而很多时候我都没有数据跟新, 存在大量无效请求  (服务器表示我很被动呀

借鉴一个兄弟的生活案例,便于理解:     【WebSocket 协议】Web 通信的下一步进化_我想养只猫 •͓͡•ʔ的博客-CSDN博客你可以在谷歌、百度搜索中找到许多类似的定义,但是我想通过一些简单和明显的例子来说明这这些。作为 HTML5 计划的一部分,开发的 WebSocket 规范引入了 WebSocketwebsocket和http的瓜葛以及websocket协议实现「建议收藏」https://blog.csdn.net/qq_41103843/article/details/124116838?utm_source=app&app_version=5.3.0&code=app_1562916241&uLinkId=usr1mkqgl919blen 上述大佬写的前端的websocket实现一个网页聊天室, 而且对于websocket的解释也是相当的优秀, 我的外卖例子均是借鉴它的

我们平时点外卖     (轮询实例)

0秒:食物到了吗?(客户)
0秒:正在配送。(外卖小哥)
1秒:食物到了吗?(客户)
1秒:正在配送。(外卖小哥)
2秒:食物到了吗?(客户)
2秒:正在配送。(外卖小哥)                  2秒及其之间那么多询问都是无效询问

3秒:食物到了吗?(客户)
3秒:是的,先生,这是您的外卖。(外卖小哥)

  • 升级为长轮询,也仅仅只能解决延时性地问题, 能达到实时地将服务端地状态数据推送到客户端地目的,          但是http连接始终打开,长连接,浪费系统资源, 客户端需要等待服务端响应,服务端也一直被客户端占用.       (一直占用服务器, 无效占用)     

长轮询生活实例  

0秒:食物到了吗?(客户)

。。。 中间电话一直不挂断, 直到外卖送到  
3秒:是的,先生,这是您的外卖。(外卖小哥)

解决问题   —   websocket全双工地通讯协议地诞生, 服务器可以主动向客户端发送数据

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • 阶段分为       一次握手阶段   +   数据交互阶段           
  • 主要核心:全双工, 服务器可以主动向客户端发送数据

websocket的特点

  • 建立在TCP协议上, 服务器端的实现比较容易
  • 与HTTP协议有着良好的兼容性, 默认端口也是80和443,并且握手阶段基于HTTP协议
  • 数据格式比较轻量,性能开销小,通信高效
  • 可以发送文本, 也可以发送二进制数据

报文分析

GET /chat HTTP/1.1 #请求行          

Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13                         

#请求头部

Jetbrains全家桶1年46,售后保障稳定

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • 注意:   上述框着的升级为websocket其实是告知服务器客户端想要建立的是websocket连接, 你支持吗, 如果服务器支持,在响应报文中也一定存在返回这两个头部字段
  • 响应
HTTP/1.1 101 Switching Protocols #响应行,状态行

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
# 响应报头
  • Sec-WebSocket-Accept其实是根据客户端的Key做Hash之后返回的结果

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • websocket在我们生活中的实例场景(服务器(后端)向网页客户端(前端)实时刷新数据)

  1. 弹幕的实时刷新
  2. 扫描微信二维码后的页面跳转
  3. 股票数据的实时刷新

websocket协议的实现分块分析, 如何在reactor的基础上封装websocket应用层协议 (哪些协议究竟是如何封装实现的)

  • 过程分析

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  •  握手细节:

websocket和http的瓜葛以及websocket协议实现「建议收藏」

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • 基于TCP连接完成之后,进行一次握手的意义 

  • 握手:确保服务器是支持websocket 协议的, 也就是服务器应答一下客户端, 你的升级请求我收到了,我是支持websocket升级的

细节分析:   如何区别握手数据  和  普通交互数据 ? 

  • 其实核心在于区分不同的阶段, 状态               —   状态机, 区分各个状态
  • 状态机      —   http协议底层也存在, 协议封装必不可少的部分,因为需要进行区分不同的阶段, 是握手建立连接的阶段, 还是数据交互的阶段   …  这些http协议底层肯定是需要区分的
  • 每一个连接都拥有如下三种状态
enum WEBSOCKET_STATUS {
	WS_HANDSHARK,//握手状态
	WS_DATATRANSFORM,//数据交互状态
	WS_DATAEND,//断开状态
};

握手细节核心: Sec-WebSocket-Key     —>   Sec-WebSocket-Accept

  • 为什么 需要经过层层加密   key -> Accept key ?  
  • 为了安全起见, 为了证明它们可以处理websocket请求。  密匙认证.
  • 加密过程伪代码如下:    获取accept.
//伪代码如下
str = Sec-WebSocket-Key;
//拿出Sec-WebSocket-Key客户端序列串

str += GUID;

sha = SHA-1(str); //SHA-1一种hash

accept = base64_encode(sha);
  • transform 数据推送的细节            —    数据封包和解包.  

  • 如下是websocket独有的数据帧格式, 握手之后的数据发送都需要按照数据帧的格式对需要发送的数据进行一个处理

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  • 做自定义协议必须的三部分.    基于tcp的自定义应用层协议

  1.  操作码.   eg: FIN  RSV
  2. 包长度
  3. mask-key 
  • 数据封包函数  + 数据解包函数    (具体的封包解包过程我还没有吃透, 如果后面有机会小杰希望可以重新分析来过, 如下目前是借鉴的前辈的代码)
void umask(char *data,int len,char *mask) {    
	int i;    
	for (i = 0;i < len;i ++)        
		*(data+i) ^= *(mask+(i%4));
}

char* decode_packet(char *stream, char *mask, int length, int *ret) {

	nty_ophdr *hdr =  (nty_ophdr*)stream;
	unsigned char *data = stream + sizeof(nty_ophdr);
	int size = 0;
	int start = 0;
	//char mask[4] = {0};
	int i = 0;

	//if (hdr->fin == 1) return NULL;

	if ((hdr->mask & 0x7F) == 126) {

		nty_websocket_head_126 *hdr126 = (nty_websocket_head_126*)data;
		size = hdr126->payload_length;
		
		for (i = 0;i < 4;i ++) {
			mask[i] = hdr126->mask_key[i];
		}
		
		start = 8;
		
	} else if ((hdr->mask & 0x7F) == 127) {

		nty_websocket_head_127 *hdr127 = (nty_websocket_head_127*)data;
		size = hdr127->payload_length;
		
		for (i = 0;i < 4;i ++) {
			mask[i] = hdr127->mask_key[i];
		}
		
		start = 14;

	} else {
		size = hdr->payload_length;

		memcpy(mask, data, 4);
		start = 6;
	}

	*ret = size;
	umask(stream+start, size, mask);

	return stream + start;
	
}


int encode_packet(char *buffer,char *mask, char *stream, int length) {

	nty_ophdr head = {0};
	head.fin = 1;
	head.opcode = 1;
	int size = 0;

	if (length < 126) {
		head.payload_length = length;
		memcpy(buffer, &head, sizeof(nty_ophdr));
		size = 2;
	} else if (length < 0xffff) {
		nty_websocket_head_126 hdr = {0};
		hdr.payload_length = length;
		memcpy(hdr.mask_key, mask, 4);

		memcpy(buffer, &head, sizeof(nty_ophdr));
		memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_126));
		size = sizeof(nty_websocket_head_126);
		
	} else {
		
		nty_websocket_head_127 hdr = {0};
		hdr.payload_length = length;
		memcpy(hdr.mask_key, mask, 4);
		
		memcpy(buffer, &head, sizeof(nty_ophdr));
		memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_127));

		size = sizeof(nty_websocket_head_127);
		
	}

	memcpy(buffer+2, stream, length);
	return length + 2;
}
  • 再注意一下recv数据之后需要按照不同的状态进行调用不同的函数进行处理数据

websocket和http的瓜葛以及websocket协议实现「建议收藏」

  •  整体代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>

#include <fcntl.h>
#include <errno.h>
#include <sys/epoll.h>

#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>

typedef struct sockaddr SA;

#define BUFFSIZE			1024
#define GUID 					"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"


enum WEBSOCKET_STATUS {
	WS_HANDSHARK,//握手状态
	WS_DATATRANSFORM,//数据交互状态
	WS_DATAEND,//断开状态
};


struct sockitem {
	int sockfd;
	int (*callback)(int fd, int events, void* arg);
	//arg 传入 sockitem* 

	char recvbuffer[BUFFSIZE];
	char sendbuffer[BUFFSIZE];

	int rlen;//recvlen
	int slen;//sendlen

	int status;//存储状态
};

//mainloop / eventloop 
struct reactor {

	int epfd;
	struct epoll_event events[512];
};

struct reactor* eventloop = NULL; //事件循环


int recv_cb(int fd, int events, void* arg);
int send_cb(int fd, int events, void* arg);


// websocket
char* decode_packet(char *stream, char *mask, int length, int *ret);
int encode_packet(char *buffer,char *mask, char *stream, int length);

struct _nty_ophdr {

	unsigned char opcode:4,
		 rsv3:1,
		 rsv2:1,
		 rsv1:1,
		 fin:1;
	unsigned char payload_length:7,
		mask:1;

} __attribute__ ((packed));

struct _nty_websocket_head_126 {
	unsigned short payload_length;
	char mask_key[4];
	unsigned char data[8];
} __attribute__ ((packed));

struct _nty_websocket_head_127 {

	unsigned long long payload_length;
	char mask_key[4];

	unsigned char data[8];
	
} __attribute__ ((packed));

typedef struct _nty_websocket_head_127 nty_websocket_head_127;
typedef struct _nty_websocket_head_126 nty_websocket_head_126;
typedef struct _nty_ophdr nty_ophdr;


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;
}


//读取一行, allbuff整个缓冲区, level 当前ind linebuff 存储一行
int readline(char* allbuff, int level, char* linebuff ) {
	int n = strlen(allbuff);

	for (; level < n; ++level) {
		//\r\n 回车换行, 表示行末
		if (allbuff[level] == '\r' && allbuff[level + 1] == '\n') {
			return level + 2;
		} else {
			*(linebuff++) = allbuff[level]; //存储行数据
		}
	}
	return -1;
}


//握手, 
int handshark(struct sockitem* si, struct reactor* mainloop) {
	char linebuff[256];//存储一行
	char sec_accept[32];//存储进行处理之后的子序列
	unsigned char sha1_data[SHA_DIGEST_LENGTH + 1] = {0};
	char head[BUFFSIZE] = {0};//存储整个头部信息
  int level = 0;
	//读取Sec-WebSocket-Key并且处理获取accept-key返回密匙
	do {
		memset(linebuff, 0, sizeof(linebuff));//清空
		level = readline(si->recvbuffer, level, linebuff);

		if (strstr(linebuff, "Sec-WebSocket-Key") != NULL) {
			//说明是key 值, 需要进行加密处理
			strcat(linebuff, GUID);//str += GDID
			SHA1((unsigned char*)&linebuff + 19, strlen(linebuff + 19), (unsigned char*)&sha1_data);  
			//SHA1(str);
			base64_encode(sha1_data, strlen(sha1_data), sec_accept);
			//将数据全部放入到head中, 写响应信息
			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);


			//然后进行将其加入到reactor中
			memset(si->recvbuffer, 0, BUFFSIZE);
			memcpy(si->sendbuffer, head, strlen(head));//to send
			si->slen = strlen(head);

			//to set epollout events;
			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->epfd, EPOLL_CTL_MOD, si->sockfd, &ev);
			//握手之后接下来server 需要关注send数据
			break;
		}

	} while ((si->recvbuffer[level] != '\r' || si->recvbuffer[level + 1] != '\n') && level != -1);

	return 0;

}

//数据交互函数
int transform(struct sockitem *si, struct reactor *mainloop) {

	int ret = 0;
	char mask[4] = {0};
	char *data = decode_packet(si->recvbuffer, mask, si->rlen, &ret);


	printf("data : %s , length : %d\n", data, ret);

	ret = encode_packet(si->sendbuffer, mask, data, ret);
	si->slen = ret;

	memset(si->recvbuffer, 0, BUFFSIZE);

	struct epoll_event ev;
	ev.events = EPOLLOUT | EPOLLET;
	//ev.data.fd = clientfd;
	si->sockfd = si->sockfd;
	si->callback = send_cb;
	si->status = WS_DATATRANSFORM;//标识IO事件处于数据交互状态.
	ev.data.ptr = si;

	epoll_ctl(mainloop->epfd, EPOLL_CTL_MOD, si->sockfd, &ev);

	return 0;
}


void umask(char *data,int len,char *mask) {    
	int i;    
	for (i = 0;i < len;i ++)        
		*(data+i) ^= *(mask+(i%4));
}

char* decode_packet(char *stream, char *mask, int length, int *ret) {

	nty_ophdr *hdr =  (nty_ophdr*)stream;
	unsigned char *data = stream + sizeof(nty_ophdr);
	int size = 0;
	int start = 0;
	//char mask[4] = {0};
	int i = 0;

	//if (hdr->fin == 1) return NULL;

	if ((hdr->mask & 0x7F) == 126) {

		nty_websocket_head_126 *hdr126 = (nty_websocket_head_126*)data;
		size = hdr126->payload_length;
		
		for (i = 0;i < 4;i ++) {
			mask[i] = hdr126->mask_key[i];
		}
		
		start = 8;
		
	} else if ((hdr->mask & 0x7F) == 127) {

		nty_websocket_head_127 *hdr127 = (nty_websocket_head_127*)data;
		size = hdr127->payload_length;
		
		for (i = 0;i < 4;i ++) {
			mask[i] = hdr127->mask_key[i];
		}
		
		start = 14;

	} else {
		size = hdr->payload_length;

		memcpy(mask, data, 4);
		start = 6;
	}

	*ret = size;
	umask(stream+start, size, mask);

	return stream + start;
	
}


int encode_packet(char *buffer,char *mask, char *stream, int length) {

	nty_ophdr head = {0};
	head.fin = 1;
	head.opcode = 1;
	int size = 0;

	if (length < 126) {
		head.payload_length = length;
		memcpy(buffer, &head, sizeof(nty_ophdr));
		size = 2;
	} else if (length < 0xffff) {
		nty_websocket_head_126 hdr = {0};
		hdr.payload_length = length;
		memcpy(hdr.mask_key, mask, 4);

		memcpy(buffer, &head, sizeof(nty_ophdr));
		memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_126));
		size = sizeof(nty_websocket_head_126);
		
	} else {
		
		nty_websocket_head_127 hdr = {0};
		hdr.payload_length = length;
		memcpy(hdr.mask_key, mask, 4);
		
		memcpy(buffer, &head, sizeof(nty_ophdr));
		memcpy(buffer+sizeof(nty_ophdr), &hdr, sizeof(nty_websocket_head_127));

		size = sizeof(nty_websocket_head_127);
		
	}

	memcpy(buffer+2, stream, length);

	return length + 2;
}



//设置非阻塞

static int set_nonblock(int fd) {
	int flags;
	flags = fcntl(fd, F_GETFL, 0);
	if (flags < 0) return -1;
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return -1;
	return 0;
}


int send_cb(int fd, int events, void* arg) {
	//发送sendbuffer中的数据
	struct sockitem* si = (struct sockitem*)arg;
	send(fd, si->sendbuffer, si->slen, 0);
	//设置关注读事件, 写完这批交互数据,接下来该继续读了

	struct epoll_event ev;
	
	ev.events = EPOLLIN | EPOLLET;
	
	si->sockfd = fd;//从新设置sockfd
	si->callback = recv_cb;
	ev.data.ptr = si;

	memset(si->sendbuffer, 0, BUFFSIZE);
	//发送完数据从新将缓冲区置为0
	epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);

}

//关闭连接
int close_connection(struct sockitem* si, unsigned int event) {
	struct epoll_event ev;
	
	close(si->sockfd);//关闭连接
	//将关注IO事件结点从监视红黑树中删除
	ev.events = event;

	epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, si->sockfd, &ev);
	free(si);
	return 0;
}



//处理读取数据
int recv_cb(int fd, int events, void* arg) {
	struct sockitem* si = (struct sockitem*)arg;
	struct epoll_event ev;

	int ret = recv(fd, si->recvbuffer, BUFFSIZE, 0);
	if (ret < 0) {
		if (errno == EAGAIN || errno == EWOULDBLOCK) {
			return -1;//非阻塞表示缓冲区中没有数据
		} else {

		}

		close_connection(si, EPOLLIN);
	} else if (ret == 0) {
		printf("disconnect %d\n", fd);
		close_connection(si, EPOLLIN);
	} else {
		si->rlen = 0;//重置rlen

		if (si->status == WS_HANDSHARK) {
			//说明是请求握手数据
			printf("request\n");
			printf("%s\n", si->recvbuffer);
			handshark(si, eventloop);//完成握手
		} else if (si->status == WS_DATATRANSFORM) {
			transform(si, eventloop);
		} else if (si->status == WS_DATAEND) {
			close_connection(si, EPOLLOUT | EPOLLET);
		}


	}

}


int accept_cb(int fd, int events, void* arg) {
	//处理新的连接。 连接IO事件处理流程
	struct sockaddr_in cli_addr;
	memset(&cli_addr, 0, sizeof(cli_addr));
	socklen_t cli_len = sizeof(cli_addr);

	int cli_fd = accept(fd, (SA*)&cli_addr, &cli_len);
	if (cli_fd <= 0) return -1;

	char cli_ip[INET_ADDRSTRLEN] = {0};	//存储cli_ip

	printf("recv from ip %s at port %d\n", inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, sizeof(cli_ip)),
		ntohs(cli_addr.sin_port));
	//注册接下来的读事件处理器
	struct epoll_event ev;
	ev.events = EPOLLIN | EPOLLET;
	struct sockitem* si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = cli_fd;
	si->status = WS_HANDSHARK;//等待握手的状态
	si->callback = recv_cb;//设置事件处理器

	ev.data.ptr = si;
	epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, cli_fd, &ev);

	return cli_fd;

}

int main(int argc, char* argv[]) {
	if (argc != 2) {
		fprintf(stderr, "usage %s <port>", argv[0]);
		return -1;
	}

	int port = atoi(argv[1]);
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (sockfd == -1) {
		fprintf(stderr, "socket error");
		return -2;
	}

	set_nonblock(sockfd);

	struct sockaddr_in serv_addr;

	memset(&serv_addr, 0, sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = INADDR_ANY;
	serv_addr.sin_port = htons(port);

	if (bind(sockfd, (SA*)&serv_addr, sizeof(serv_addr)) == -1) {
		fprintf(stderr, "bind error");
		return -3;
	}


	if (listen(sockfd, 5) == -1) {
		fprintf(stderr, "listen error");
		return -4;
	}

	//init eventloop
	eventloop = (struct reactor*)malloc(sizeof(struct reactor));
	//创建监视事件红黑树的根部
	eventloop->epfd = epoll_create(1);

	//注册处理连接IO处理函数
	struct epoll_event ev;
	ev.events = EPOLLIN;

	struct sockitem* si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = sockfd;
	si->callback = accept_cb;
	ev.data.ptr = si;

	epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, sockfd, &ev);
	while (1) {
		int nready = epoll_wait(eventloop->epfd, eventloop->events, 512, -1);
		if (nready < -1) {
			break;
		}

		int i = 0;
		for (; i < nready; ++i) {

			if (eventloop->events[i].events & EPOLLIN) {
				struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
				si->callback(si->sockfd, eventloop->events[i].events, si);
			}

			if (eventloop->events[i].events & EPOLLOUT) {
				struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
				si->callback(si->sockfd, eventloop->events[i].events, si);
			}
		}
	}

	return 0;

}

  • 线上测试工具  +  我的测试结果

websocket在线测试WebSocket 在线测试 工具 物联网websocket和http的瓜葛以及websocket协议实现「建议收藏」http://www.websocket-test.com/websocket和http的瓜葛以及websocket协议实现「建议收藏」

 websocket和http的瓜葛以及websocket协议实现「建议收藏」

总结本文

  • 本文从http的弊端入手分析为啥需要websocket这个全新的应用层协议出来
  • 为了解决服务器需要向客户端主动推送数据的问题.    后端服务器向前端网页主动推送数据.       
  •   http 轮询 长连接 虽然也可以,但是轮询延迟长,而且不断地建立无效连接,结果服务器压根不需要推送数据,这样就很浪费资源,    长轮询,虽说是解决了延迟问题,可是不断地占据着服务器,对于服务器资源也是一种浪费, 毕竟你霸占服务器然而很长事件才需要推送一次数据
  • 于是全新地服务器可以主动推送数据地, 基于tcp地全双工地websocket 诞生了
  • websocket分为  握手和数据交互两大阶段。      握手阶段是基于http升级的.
  • 为了区分recv的时候的数据阶段,于是状态机诞生了   
  • 握手阶段的核心在于,密匙确认服务端是否支持websocket.    key—-> accept
  • 经过  str += GUID     SHA-1(str) hash   然后base64_encode (str);
  • 然后我们基于tcp如果需要封装自己的应用层协议:

特有数据帧格式:1.  操作码  2. 包长度   3,  mask-key

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/206835.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • keil生成bin文件失败_编译后生成的文件扩展名为

    keil生成bin文件失败_编译后生成的文件扩展名为在KEIL魔术棒中通过以下方式生成bin文件时,居然出错,当时震惊了,第一次出现这种情况:使用的命令:fromelf–bin–output.\bin\APP.bin.\STM32_APP\STM32_APP.axf生成的bin文件,居然变成了文件夹!如下图:一开始以为是ROM地址设置的不对,找之前的工程来对比“魔术棒”中的配置,发现没有问题!然后怀疑是工程文件名称太长,或者下划线太多。。。重新新建工程,重命名文件夹等各种操作,发现问题依旧!然后嫉妒怀疑是KEIL的BUG.

    2022年10月20日
  • java高并发详解

    java高并发详解转载地址:https://www.cnblogs.com/lr393993507/p/5909804.html&nbsp;&nbsp;对于开发的网站,如果网站的访问量非常大,那么我们应该考虑相关的、并发访问问题,并发是绝大部分程序员头疼的问题;为了更好的理解并发和同步,先明白两个重要的概念:异步和同步;&nbsp;1、同步和异…

  • PreparedStatement详解

    PreparedStatement详解JDBC初步.主要讲了基本访问数据库的步骤.其中第四步提到了用Statement去执行SQL语句.这里介绍个Statement的子类PreparedStatement.PreparedStatement(预处理执行语句)相比其父类Statement主要有以下几个优点.1.可以防止SQL注入.               2.在特定的驱动数据库下相对效率要高(不绝对)   

  • 大数据精准营销的网络营销策略研究_指南针的使用

    大数据精准营销的网络营销策略研究_指南针的使用互联网时代,也是一个数据大爆炸的时代,很多人通过网络营销,却忽略了一个重要的基础,那就是数据,而以各种各样数据为基础的互联网营销,需要的是一种大数据营销的思维,这种思维对于提升营销的质量和成功率都有极大的帮助。何为大数据营销,那么它又和网络营销的有何关联呢?其实大数据营销并不是一个复杂的概念,指的是通过所针对的目标的各种数据的变化和分析,来获得营销方向的指导,这种数据通过表格、文字等形式表现出来,…

  • 证书认证过程_过程装备与控制工程可考证书

    证书认证过程_过程装备与控制工程可考证书现在很多的网站都会用​​https证书申请​​了,因为https证书好处现在越来越受到人们的认知,https安全证书的好处不仅仅对于客户的信息隐私数据有保护,对于维护网站的知名度以及安全可信度也是大有帮助。这也是很多人用申请https证书,但是他们首先遇到的难题就是申请https证书的过程是怎么样的?申请​​https证书​​的步骤是怎样的呢?1、要想完成https证书的申请,要先确定申请什么类型的https:大体来说有安全等级可以分为域名型证书,企业型证书以及增强型证书。根据自己网站.

  • 如何找到字符串中的最长回文子串?「建议收藏」

    如何找到字符串中的最长回文子串?「建议收藏」作者|channingbreeze责编|胡巍巍小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT互联网公司。可是努…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号