Linux Socket 编程--- WebSocket

什么是websocket

websocket 协议是基于 TCP 的上层协议,是HTTP 的一个升级,为浏览器提供一个双通道的长链接。并且不依赖与多个 HTTP 短链接。
1. websocket 可以实现长链接
2. websocket 可以实现server 推送 到客户端。
3. websocket 是用来取代
websocket 是独立的基于-TCP的协议,与HTTP唯一的关系就是 它的握手可以被 HTTP 服务其翻译为 Upgrade 请求。

为什么要用websocket

常见的使用场景:
1. 游戏
2. 股票信息更新
3. 多用户同时编辑的应用
4. 以及其他需要实时的场景

怎么样实现

协议样式

协议分为 两个部分: handshake(握手) DataTransfer ( 传输)

客户端发来的协议样式(handshake)

在这里插入图片描述

服务端发送的协议样式(handshake)

在这里插入图片描述
首行 分别是 Request-Line 格式 和 Status-Line Format (见RFC2616-- HTTP1.1 协议)
在这里插入图片描述

把 Sec-WebSocket-Key 与 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 拼接在一起。
Sec-WebSocket-Accept = base64-encoded(SHA1(Sec-WebSocket-Key + “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”))
SHA1 是 openssl 库提供的方法。
base64-encoded 也是 openssl 库提供的方法。

数据传输阶段 包的样式

DataFrame
在这里插入图片描述

在这里插入图片描述
FIN:
表示 final fragement 上一条消息的结束标志位,这一条消息的开始标志位。
RSV1,RSV2,RSV3
必须为0 ,除非双方定义了非0 的含义
在这里插入图片描述

struct ws_ophdr {
 
  unsigned char opcode:4,
  			rsv3:1,
  			rsv2:1,
  			rsv1:1,
  			fin:1;

  unsigned char pl_len:7,
   			  mask:1;

};
#include <openssl/asn1.h>
#include <stdio.h> //printf
#include <errno.h>// errorno
#include <string.h> //memset strerror
#include <sys/epoll.h> //epoll_create 
#include <stdlib.h> //calloc atoi
#include <unistd.h> //close
#include <sys/socket.h> //socket
#include <sys/fcntl.h> //fcntl
#include <netinet/in.h> //sockaddr_in 结构体 
#include <arpa/inet.h> // htol 等


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

#define ACCEPT_KEY_LENGTH  64
#define MAX_EPOLL_EVENTS  1024
#define SERVER_PORT   8888
#define PORT_COUNT  2
#define  BUFFER_LENGTH  1024
#define GUID		"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
//回调函数指针
typedef int NCALLBACK(int,uint32_t,void*);

struct ntyevent{
     int fd;
     int events;// EPOLLIN: 1 EPOLLOUT:4 
     void *args;
     int (*callback)(int fd, uint32_t events, void *arg);
     int status;
     char rbuffer[BUFFER_LENGTH];
     int rlength;
     char wbuffer[BUFFER_LENGTH];
     int wlength;
     char sec_accept[ACCEPT_KEY_LENGTH];
     int wsstatus; //0,1,2,3
};

struct eventblock{
    struct ntyevent* events;
    struct eventblock* next;
};

struct ntyreactor{
    int epfd;
    int blkcnt;
    struct eventblock* evblks;
    
};

// ## 状态机 ###
enum{
     WS_HANDSHARK = 0,
     WS_TRANSMISSION=1,
     WS_END = 2,
     WS_COUNT
};

struct ws_ophdr{
    unsigned char opcode:4,
    rsv3:1,
    rsv2:1,
    rsv1:1,
    fin:1;
    unsigned char pl_len:7,
    mask:1;
};

void umask(char *payload,int length,char *mask_key){
     	int i = 0;

	for (i = 0;i < length;i ++) {
		payload[i] ^= mask_key[i%4];
	}
}

int recv_cb(int fd, uint32_t events, void *arg);
int send_cb(int fd, uint32_t events, void *arg);
int accept_cb(int fd, uint32_t events, void *arg);
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 idx,char *linebuf)
{
    int len = (int)strlen(allbuf);
    for(;idx < len;++idx){
		if(allbuf[idx] == '\r' && allbuf[idx+1] == '\n')
		   return idx+2;
		else 
		    *(linebuf++) = allbuf[idx];
	}
	return -1;
}

int ws_handshake(struct ntyevent* ev)
{
    int idx = 0;
    char sec_data[128] ={0};
    char sec_accept[128] = {0};
    do {
        char linebuf[1024] = {0};
        idx = readline(ev->rbuffer,idx,linebuf);
        if(strstr(linebuf,"Sec-WebSocket-Key"))
        {
           // printf("idx:%d,line:%s\n",idx,linebuf);
            strcat(linebuf,GUID);
            //Sec-WebSocket-Key
            SHA1(linebuf+sizeof("Sec-WebSocket-Key "),strlen(linebuf+sizeof("Sec-WebSocket-Key ")),sec_data);//openssl
            base64_encode(sec_data,strlen(sec_data),sec_accept);
           // printf("idx:%d,line:%s\n",idx,sec_accept);
            memcpy(ev->sec_accept,sec_accept,ACCEPT_KEY_LENGTH);

        }
    }while((ev->rbuffer[idx] !='\r' || ev->rbuffer[idx+1] !='\n') && idx !=-1);
}

int ws_transmission(struct ntyevent* ev)
{
    // ev->buffer
    struct ws_ophdr *hdr = (struct ws_ophdr *)ev->rbuffer;//把收到的内容强转成
    unsigned char *payload = NULL;
    if(hdr->pl_len < 126){
        unsigned char *payload ;
       if( hdr->mask ){
          payload = ev->rbuffer  + 6;
          umask(payload,hdr->pl_len,ev->rbuffer +  2);// client 发过来的都是 mask 的payload 消息。
          //修改 mask 的标记
       }else{
          payload = ev->rbuffer  + 2;
       }
       printf("payload:%s\n",payload);
        
    }else if(hdr->pl_len == 126){

    }else if(hdr->pl_len == 127){

    }
}
int ws_send_transmission(struct ntyevent* ev){
    memset(ev->wbuffer,0,sizeof(ev->wbuffer));
    //memcpy(ev->wbuffer,ev->rbuffer,BUFFER_LENGTH); // 拷贝过来
    struct ws_ophdr *hdr = (struct ws_ophdr *)ev->wbuffer;//把收到的内容强转成
    unsigned char *payload = NULL ;
    if(hdr->pl_len < 126){
          printf("here\n");
          hdr->fin = 1;
          payload = ev->wbuffer  + 2;
          payload = "123";
          //umask(payload,hdr->pl_len,ev->wbuffer +  2);// client 发过来的都是 mask 的payload 消息。
          hdr->mask = 0;
          printf("payload in send:%s\n",payload);
        
    }else if(hdr->pl_len == 126){

    }else if(hdr->pl_len == 127){

    }
    return 0;
}

int ws_request(struct ntyevent *ev)
{
    if(ev->wsstatus == WS_HANDSHARK)
    {
        //printf("hand\n");
        ws_handshake(ev);
        //ev->wsstatus = WS_TRANSMISSION;
        
    }else if(ev->wsstatus == WS_TRANSMISSION){
        //printf("trans\n");
        ws_transmission(ev);
    }
}

int ws_response(struct ntyevent* ev)
{
     // 上层每次收到都会回复的,所以得判断状态
    //根据状态回应消息。
    //printf("status:%d\n",ev->wsstatus);
    if(ev->wsstatus == WS_HANDSHARK)
    {
        ev->wlength= sprintf(ev->wbuffer,
      "HTTP/1.1 101 Switching Protocols\r\n"
      "Upgrade: websocket\r\n"
      "Connection: Upgrade\r\n"
      "Sec-WebSocket-Accept:%s\r\n\r\n"
    ,ev->sec_accept);
     ev->wsstatus = WS_TRANSMISSION;
        
    }else if(ev->wsstatus == WS_TRANSMISSION){
        ws_send_transmission(ev);
    }
    
    return 0;
}
//event 操作相关
/*
void nty_event_set(struct ntyevent *ev,int fd,int event,NCALLBACK callback, void *arg)
{
    ev->fd = fd;
    //ev->events = event;
    ev->events = 0;
    ev->callback= callback;
    ev->args =arg;

    return;
}
*/
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;
}
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;
}
void nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg) {

	ev->fd = fd;
	ev->callback = callback;
	ev->events = 0;
	ev->args = arg;
	//ev->last_active = time(NULL);

	return ;
	
}
int nty_epoll_event_configure(int epfd,int event,int op,struct ntyevent *ev){
      struct epoll_event ep_ev ={0,{0}};
      ep_ev.data.ptr = ev;
      ep_ev.events = ev->events = event;
      printf("epfd in configure:%d\n",epfd);
      printf("ev->fd in configure:%d\n",ev->fd);
      if(epoll_ctl(epfd,op,ev->fd,&ep_ev) < 0){
         printf("epoll event <op>[%d] [fd=%d],events[%d]\n",op,ev->fd,ep_ev.events);
         return -1;
      }
      return 0;
}


// 结构体 操作
int  ntyreactor_init(struct ntyreactor * reactor)
{
   if (reactor == NULL) return -1;
   memset(reactor,0,sizeof(struct ntyreactor));

   reactor->epfd = epoll_create(1);
   //printf("epfd in init :%d\n",reactor->epfd);
   if(reactor->epfd  <=0){
        printf("create epfd in %s err %s\n", __func__, strerror(errno));
        return -2;
   }
   // 首先分配了event 的内存空间
   struct ntyevent *evs = (struct ntyevent*) calloc(MAX_EPOLL_EVENTS,sizeof(struct ntyevent));
   if(evs == NULL){
      printf("create epfd in %s err %s\n", __func__, strerror(errno));
      close(reactor->epfd);
   }

   memset(evs,0,(MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));

   struct eventblock * block = (struct eventblock*)calloc(1, sizeof(struct eventblock));
   if(block == NULL){
     free(evs);
     close(reactor->epfd);
     return -3;
   }
   block->events = evs;
   block->next = NULL;

   reactor->evblks = block;
   reactor->blkcnt = 1;
   return 0;
}
int  ntyreactor_alloc(struct ntyreactor * reactor){
    if(reactor == NULL) return -1;
    if(reactor->evblks == NULL) return -1;
    struct eventblock *blk = reactor->evblks;
    //挪到最后一块
    while(blk->next != NULL){
        blk= blk->next;
    }
    
    struct ntyevent *evs = (struct ntyevent*)calloc(MAX_EPOLL_EVENTS,sizeof(struct ntyevent));
    if(evs == NULL){
        printf("ntyreactor_alloc ntyevent failed.\n");
        return -2;
    }
    memset(evs,0,(MAX_EPOLL_EVENTS)*sizeof(struct ntyevent));
    struct eventblock *block = calloc(1,sizeof(struct eventblock));
    if(block == NULL){
        printf("ntyreactor_alloc eventblock failed.\n");
        return -3;
    }
    block->events = evs;
    block->next = NULL;
    blk->next = block;
    reactor->blkcnt ++;

    return 0;
}
struct ntyevent* ntyreactor_idx(struct ntyreactor* reactor, int sockfd){
    if(reactor == NULL) return NULL;
    if(reactor->evblks == NULL) return NULL; //说明初始化失败了
    //从实验结果来看,系统 sockfd 都是从  3 开始分配的,因此 0,1,2 
    int blkidx = sockfd / MAX_EPOLL_EVENTS;// =0 说明还在第一块上, 1 在第二块上
    while(blkidx >= reactor->blkcnt){
        ntyreactor_alloc(reactor); // 不够重新分配新的 MAX_EPOLL_EVENTS 块 ntyevent
    }
   int i = 0;
   struct eventblock *blk = reactor->evblks;
   while(i++ != blkidx && blk != NULL){
     blk = blk->next;
   }
   //0~MAXEPOLL_EVENTS-1, MAX_EPOLL_EVENTS ~ 2MAX_EPOLL_EVENTS-1  ....
   // 实际情况 1,2,3 是空的
   return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}


//
int init_sock(unsigned short port)
{
    int fd = socket(AF_INET,SOCK_STREAM,0);
    //设置成非阻塞
    fcntl(fd,F_SETFL,O_NONBLOCK);
    
    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    int ret = bind(fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    if(ret < 0)
    {
        printf("bind failed.\n");
        return -1;
    }
    if(listen(fd,20)<0){
        printf("listen failed:%s \n",strerror(errno));
        return -1;
    }
    printf("listen server port: %d\n",port);
    return fd;

}


//根据callback 不同可以知道其为不同的
int ntyreactor_Register(struct ntyreactor* reactor,int sockfd,NCALLBACK callback){
    if(reactor == NULL) return -1;
    if(reactor->evblks == NULL) return -1;
    struct ntyevent *event = ntyreactor_idx(reactor,sockfd);
    if(event == NULL) return -1;
    // 往ntyevent 里面填充 数据,并加入到 reactor 兴趣列表里面
    //nty_event_set(event,sockfd,EPOLLIN,callback,(void*)reactor);
    //nty_epoll_event_configure(reactor->epfd,EPOLLIN,EPOLL_CTL_ADD,event);
    nty_event_set(event, sockfd, callback, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);
 

	return 0;
}
//这里传进来的 sockfd 都是 需要监听的Serverfd 


int ntyreactor_run(struct ntyreactor *reactor) {
	if (reactor == NULL) return -1;
	if (reactor->epfd < 0) return -1;
	if (reactor->evblks == NULL) return -1;
	
	struct epoll_event events[MAX_EPOLL_EVENTS+1];
	
	int checkpos = 0, i;

	while (1) {

		int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
		if (nready < 0) {
			printf("epoll_wait error, exit\n");
			continue;
		}

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

			struct ntyevent *ev = (struct ntyevent*)events[i].data.ptr;

			if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {
				ev->callback(ev->fd, events[i].events, ev->args);
			}
			if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {
				ev->callback(ev->fd, events[i].events, ev->args);
			}
			
		}

	}
}

int ntyreactor_destory(struct ntyreactor *reactor) {

	close(reactor->epfd);

	struct eventblock *blk = reactor->evblks;
	struct eventblock *blk_next;
	while (blk != NULL) {
		blk_next = blk->next;

		free(blk->events);
		free(blk);
		
		blk = blk_next;
	}

	return 0;
}

int recv_cb(int fd, uint32_t events, void *arg)
{
   struct ntyreactor *reactor = (struct ntyreactor*)arg;//???
   struct ntyevent *ev = ntyreactor_idx(reactor,fd);
   if(ev == NULL) return -1;
   int len = (int)recv(ev->fd,ev->rbuffer,BUFFER_LENGTH,0);
   nty_event_del(reactor->epfd, ev);//???
   if(len > 0){
     ev->rlength =len;
     ev->rbuffer[len]='\0';
     //printf("recv [%d]:%s\n",fd,ev->rbuffer);
     ws_request(ev);
     nty_event_set(ev, fd, send_cb, reactor);
	 nty_event_add(reactor->epfd, EPOLLOUT, ev);
   }else if(len == 0){
       nty_event_del(reactor->epfd, ev);//关闭了
       printf("recv_cb --> disconnect\n");
       close(ev->fd);
   }else {
         if(errno == EAGAIN && errno == EWOULDBLOCK){

         }else if(errno == ECONNRESET){
            nty_event_del(reactor->epfd, ev);//关闭了
            close(ev->fd);
         }
         printf("recv[fd=%d] error[%d]:%s\n",fd,errno,strerror(errno));
   }
   return len;
}
int send_cb(int fd, uint32_t events, void *arg)
{
    struct ntyreactor *reactor = (struct ntyreactor*)arg;
    struct ntyevent *ev = ntyreactor_idx(reactor,fd);
    if(ev == NULL) return -1;
    ws_response(ev);// websocket
    int len = (int)send(fd,ev->wbuffer,ev->wlength,0);
    if(len > 0){
        printf("send[fd=%d],[%d]%s\n",fd,len,ev->wbuffer);
		nty_event_del(reactor->epfd, ev);
		nty_event_set(ev, fd, recv_cb, reactor);
		nty_event_add(reactor->epfd, EPOLLIN, ev);
    }else{
        nty_event_del(reactor->epfd, ev);
        close(ev->fd);
        printf("send[fd=%d] error %s\n",fd,strerror(errno));
    }
    return len;
}
/*
int accept_cb(int fd,uint32_t events,void *arg){
    struct ntyreactor *reactor = (struct ntyreactor*) arg;
    if(reactor == NULL) return -1;
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    int clientfd;
    if((clientfd = accept(fd,(struct sockaddr*)&client_addr,&len)== -1)){
         if(errno != EAGAIN && errno != EINTR){

         }
         printf("accept: %s\n",strerror(errno));
         return -1;
    }
    printf("clientfd in accept:%d\n:",clientfd);
    int flag = 0;
    if((flag = fcntl(clientfd,F_SETFL,O_NONBLOCK)) < 0){
        printf("%s: fcntl nonblocking failed, %d\n",__func__,MAX_EPOLL_EVENTS);
        return -1;
    }
    struct ntyevent *event =ntyreactor_idx(reactor,clientfd);
    if(event == NULL) return -1;
  	nty_event_set(event, clientfd, recv_cb, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);

    printf("new connect [%s:%d],pos[%d]\n",
           inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),clientfd);
    return 0;
}
*/
int accept_cb(int fd, uint32_t events, void *arg) {

	struct ntyreactor *reactor = (struct ntyreactor*)arg;
	if (reactor == NULL) return -1;

	struct sockaddr_in client_addr;
	socklen_t len = sizeof(client_addr);

	int clientfd;

	if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) {
		if (errno != EAGAIN && errno != EINTR) {
			
		}
		printf("accept: %s\n", strerror(errno));
		return -1;
	}

	
	int flag = 0;
	if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) {
		printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
		return -1;
	}

	struct ntyevent *event = ntyreactor_idx(reactor, clientfd);

	if (event == NULL) return -1;
		
	nty_event_set(event, clientfd, recv_cb, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);

	

	printf("new connect [%s:%d], pos[%d]\n", 
		inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);

	return 0;

}
int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor) {

	if (reactor == NULL) return -1;
	if (reactor->evblks == NULL) return -1;

	struct ntyevent *event = ntyreactor_idx(reactor, sockfd);
	if (event == NULL) return -1;

	nty_event_set(event, sockfd, acceptor, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);

	return 0;
}

int main(int argc, char *argv[]) {
    struct ntyreactor *reactor = (struct ntyreactor*)calloc(1,sizeof(struct ntyreactor));
    ntyreactor_init(reactor); // 初始化时分配了一组 ntyevent
     
    unsigned short port = SERVER_PORT;
    //printf("EPOLLIN:%d\n",EPOLLIN);
    //printf("EPOLLOUT:%d\n",EPOLLOUT);
    //printf("EPOLL_CTL_ADD:%d\n",EPOLL_CTL_ADD);
    //printf("EPOLL_CTL_MOD:%d\n",EPOLL_CTL_MOD);
    //printf("EPOLL_CTL_DEL:%d\n",EPOLL_CTL_DEL);

    // 有参数时,第一个参数自动成为 端口号
    if(argc == 2){
        port = atoi(argv[1]);
    }
    int i = 0;
    int sockfds[PORT_COUNT] = {0};
    for(i = 0; i< PORT_COUNT;i++)
    {
        sockfds[i] = init_sock(port +i);
        //printf("sockfd[%d]=%d\n",i,sockfds[i]);
        //ntyreactor_Register(reactor,sockfds[i],(void*)accept_cb);//把监听端口加入到reactor里面,并加上accept 回调
        ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
    }
    ntyreactor_run(reactor);
    ntyreactor_destory(reactor);
	
	for (i = 0;i < PORT_COUNT;i ++) {
		close(sockfds[i]);
	}
	free(reactor);


    return 0;
}
 #  测试结论
 组装好dataFrame 发送到 

在这里插入图片描述
在这里插入图片描述

直接上代码

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:
服务器高级架构体系:
https://ke.qq.com/course/417774?flowToken=1010783

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值