当前位置:   article > 正文

C实现websocket服务,实例

C实现websocket服务,实例

目录

0、数据帧格式

1、头文件

2、初始化:websocket_init()

 3、base64_encode()

4、int _readline()

5、int shakehands()

6、void inverted_string()

7、 int recv_frame_head()

8、void umask() 

9、int send_frame_head() 

 10、int main()


0、数据帧格式

/*-----------为了便于理解,在这里吧数据帧格式粘出来-------------------
0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
--------------------------------------------------------------------*/ 

1、头文件

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>
  7. #include <openssl/sha.h>
  8. #include <openssl/pem.h>
  9. #include <openssl/bio.h>
  10. #include <openssl/evp.h>
  11. #define BUFFER_SIZE 1024
  12. #define RESPONSE_HEADER_LEN_MAX 1024
  13. #define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  14. #define    WEB_PORT                 8080
  15. #define    WEB_ADDR                 "127.0.0.1"
  16. #define    WEB_LISTEN_MAX     1024
  17. int websock;
  18. typedef struct _frame_head {
  19.     char fin;
  20.     char opcode;
  21.     char mask;
  22.     unsigned long long payload_length;
  23.     char masking_key[4];
  24. } frame_head;

2、初始化:websocket_init()

  1. int websocket_init()
  2. {
  3.     websock = socket(AF_INET,SOCK_STREAM, 0);
  4.     struct sockaddr_in server_sockaddr;
  5.     server_sockaddr.sin_family = AF_INET;
  6.     server_sockaddr.sin_port = htons(WEB_PORT);
  7.     server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  8.     if(bind(websock,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr)) < 0)
  9.     {
  10.         printf("bind websock error.");
  11.         return -1;
  12.     }
  13.     printf("bind OK.");
  14.     if(listen(websock,WEB_LISTEN_MAX) < 0)
  15.     {
  16.         printf("websock listen error.");
  17.         return -1;
  18.     }
  19.     printf("listen OK.");
  20.     
  21.     printf("%s OK %d.",__func__,websock);
  22.     return websock;
  23. }

 3、base64_encode()

        Base64编码函数,握手函数会用到。

  1. /*
  2. *Base64编码函数 
  3. *握手函数会用到
  4. */
  5. int base64_encode(char *in_str, int in_len, char *out_str)
  6. {
  7.     BIO *b64, *bio;
  8.     BUF_MEM *bptr = NULL;
  9.     size_t size = 0;
  10.     if (in_str == NULL || out_str == NULL)
  11.         return -1;
  12.     b64 = BIO_new(BIO_f_base64());
  13.     bio = BIO_new(BIO_s_mem());
  14.     bio = BIO_push(b64, bio);
  15.     BIO_write(bio, in_str, in_len);
  16.     BIO_flush(bio);
  17.     BIO_get_mem_ptr(bio, &bptr);
  18.     memcpy(out_str, bptr->data, bptr->length);
  19.     out_str[bptr->length-1] = '\0';
  20.     size = bptr->length;
  21.     BIO_free_all(bio);
  22.     return size;
  23. }

4、int _readline()

        逐行读取函数 ,握手函数循环调用,每次获得一行字符串,返回下一行开始位置。

  1. /*
  2. *逐行读取函数 
  3. *握手函数循环调用,每次获得一行字符串,返回下一行开始位置
  4. */
  5. int _readline(char* allbuf,int level,char* linebuf)
  6. {
  7.     int len = strlen(allbuf);
  8.     for (;level<len;++level)
  9.     {
  10.         if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
  11.             return level+2;
  12.         else
  13.             *(linebuf++) = allbuf[level];
  14.     }
  15.     return -1;
  16. }

5、int shakehands()

        握手函数 。负责处理新客户端的连接,接收客户端http格式的请求,从中获得Sec-WebSocket-Key对应的值,与魔法字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 进行连接后进行sha1 hash,再将结果(sha1的直接结果,不是转化为字符串后的结果)进行Base64编码。最后构造响应头部,发送响应,与客户端建立websocket连接。

  1. /*
  2. 握手函数 
  3. 负责处理新客户端的连接,接收客户端http格式的请求,从中获得Sec-WebSocket-Key对应的值,与魔法字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 进行连接后进行sha1 hash,再将结果(sha1的直接结果,不是转化为字符串后的结果)进行Base64编码。最后构造响应头部,发送响应,与客户端建立websocket连接
  4. */
  5. int shakehands(int cli_fd)
  6. {
  7.     //next line's point num
  8.     int level = 0;
  9.     //all request data
  10.     char buffer[BUFFER_SIZE];
  11.     //a line data
  12.     char linebuf[256];
  13.     //Sec-WebSocket-Accept
  14.     char sec_accept[32];
  15.     //sha1 data
  16.     unsigned char sha1_data[SHA_DIGEST_LENGTH+1]={0};
  17.     //reponse head buffer
  18.     char head[BUFFER_SIZE] = {0};
  19.     if (read(cli_fd,buffer,sizeof(buffer))<=0)
  20.         perror("read");
  21.     printf("request\n");
  22.     printf("%s\n",buffer);
  23.     do {
  24.         memset(linebuf,0,sizeof(linebuf));
  25.         level = _readline(buffer,level,linebuf);
  26.         //printf("line:%s\n",linebuf);
  27.         if (strstr(linebuf,"Sec-WebSocket-Key")!=NULL)
  28.         {
  29.             strcat(linebuf,GUID);
  30. //            printf("key:%s\nlen=%d\n",linebuf+19,strlen(linebuf+19));
  31.             SHA1((unsigned char*)&linebuf+19,strlen(linebuf+19),(unsigned char*)&sha1_data);
  32. //            printf("sha1:%s\n",sha1_data);
  33.             base64_encode(sha1_data,strlen(sha1_data),sec_accept);
  34. //            printf("base64:%s\n",sec_accept);
  35.             /* write the response */
  36.             sprintf(head, "HTTP/1.1 101 Switching Protocols\r\n" \
  37.                           "Upgrade: websocket\r\n" \
  38.                           "Connection: Upgrade\r\n" \
  39.                           "Sec-WebSocket-Accept: %s\r\n" \
  40.                           "\r\n",sec_accept);
  41.             printf("response\n");
  42.             printf("%s",head);
  43.             if (write(cli_fd,head,strlen(head))<0)
  44.                 perror("write");
  45.             break;
  46.         }
  47.     }while((buffer[level]!='\r' || buffer[level+1]!='\n') && level!=-1);
  48.     return 0;
  49. }

6、void inverted_string()

        字符串反转函数 ,用于解决大端小端问题。

  1. /*
  2. 字符串反转函数 
  3. 用于解决大端小端问题
  4. */
  5. void inverted_string(char *str,int len)
  6. {
  7.     int i; char temp;
  8.     for (i=0;i<len/2;++i)
  9.     {
  10.         temp = *(str+i);
  11.         *(str+i) = *(str+len-i-1);
  12.         *(str+len-i-1) = temp;
  13.     }
  14. }

7、 int recv_frame_head()

        接收及存储数据帧头 。调用者传一个数据帧头结构体指针用于获取解析后的帧头解析过程依照MDN中说的结构解析就好。

  1. /*
  2. 接收及存储数据帧头 
  3. 调用者传一个数据帧头结构体指针用于获取解析后的帧头 
  4. 解析过程依照MDN中说的结构解析就好
  5. */
  6. int recv_frame_head(int fd,frame_head* head)
  7. {
  8.     char one_char;
  9.     /*read fin and op code*/
  10.     if (read(fd,&one_char,1)<=0)
  11.     {
  12.         perror("read fin");
  13.         return -1;
  14.     }
  15.     head->fin = (one_char & 0x80) == 0x80;
  16.     head->opcode = one_char & 0x0F;
  17.     if (read(fd,&one_char,1)<=0)
  18.     {
  19.         perror("read mask");
  20.         return -1;
  21.     }
  22.     head->mask = (one_char & 0x80) == 0X80;
  23.     /*get payload length*/
  24.     head->payload_length = one_char & 0x7F;
  25.     if (head->payload_length == 126)
  26.     {
  27.         char extern_len[2];
  28.         if (read(fd,extern_len,2)<=0)
  29.         {
  30.             perror("read extern_len");
  31.             return -1;
  32.         }
  33.         head->payload_length = (extern_len[0]&0xFF) << 8 | (extern_len[1]&0xFF);
  34.     }
  35.     else if (head->payload_length == 127)
  36.     {
  37.         char extern_len[8];
  38.         if (read(fd,extern_len,8)<=0)
  39.         {
  40.             perror("read extern_len");
  41.             return -1;
  42.         }
  43.         inverted_string(extern_len,8);
  44.         memcpy(&(head->payload_length),extern_len,8);
  45.     }
  46.     /*read masking-key*/
  47.     if (read(fd,head->masking_key,4)<=0)
  48.     {
  49.         perror("read masking-key");
  50.         return -1;
  51.     }
  52.     return 0;
  53. }

8、void umask() 

        去掩码函数。从客户端发来的数据是经过异或加密的,我们在解析帧头的时候获取到了掩码,我们通过掩码可以解码出原数据。

  1. /*
  2. 去掩码函数 
  3. 从客户端发来的数据是经过异或加密的,我们在解析帧头的时候获取到了掩码,我们通过掩码可以解码出原数据。
  4. */
  5. void umask(char *data,int len,char *mask)
  6. {
  7.     int i;
  8.     for (i=0;i<len;++i)
  9.         *(data+i) ^= *(mask+(i%4));
  10. }

9、int send_frame_head() 

        发送数据帧头。

  1. /*发送数据帧头*/
  2. int send_frame_head(int fd,frame_head* head)
  3. {
  4.     char *response_head;
  5.     int head_length = 0;
  6.     if(head->payload_length<126)
  7.     {
  8.         response_head = (char*)malloc(2);
  9.         response_head[0] = 0x81;
  10.         response_head[1] = head->payload_length;
  11.         head_length = 2;
  12.     }
  13.     else if (head->payload_length<0xFFFF)
  14.     {
  15.         response_head = (char*)malloc(4);
  16.         response_head[0] = 0x81;
  17.         response_head[1] = 126;
  18.         response_head[2] = (head->payload_length >> 8 & 0xFF);
  19.         response_head[3] = (head->payload_length & 0xFF);
  20.         head_length = 4;
  21.     }
  22.     else
  23.     {
  24.         response_head = (char*)malloc(12);
  25.         response_head[0] = 0x81;
  26.         response_head[1] = 127;
  27.         memcpy(response_head+2,head->payload_length,sizeof(unsigned long long));
  28.         inverted_string(response_head+2,sizeof(unsigned long long));
  29.         head_length = 12;
  30.     }
  31.     if(write(fd,response_head,head_length)<=0)
  32.     {
  33.         perror("write head");
  34.         return -1;
  35.     }
  36.     free(response_head);
  37.     return 0;
  38. }

 10、int main()

  1. int main()
  2. {
  3.     if(websocket_init() < 0){
  4.         return -1;
  5.     }
  6.     int webclient = -1;
  7.     struct sockaddr_in client_in;
  8.     socklen_t size = sizeof(client_in);
  9.     bzero(&client_in,sizeof(client_in));
  10.     printf("wait accept");
  11.     webclient = accept(websock,(struct sockaddr*)&client_in,&size);
  12.     printf("accepting..");
  13.     shakehands(webclient);
  14.     while(1){
  15.         frame_head head;
  16.         int rul = recv_frame_head(webclient,&head);
  17.         if (rul < 0)
  18.             break;
  19. printf("fin=%d\nopcode=0x%X\nmask=%d\npayload_len=%llu\n",
  20. head.fin,head.opcode,head.mask,head.payload_length);
  21.         //send head
  22.         send_frame_head(webclient,&head);
  23.         //read payload data
  24.         char payload_data[1024] = {0};
  25.         int size = 0;
  26.         do {
  27.             int rul;
  28.             rul = read(webclient,payload_data,1024);
  29.             if (rul<=0)
  30.                 break;
  31.             size+=rul;
  32.             umask(payload_data,size,head.masking_key);
  33.             printf("recive:%s",payload_data);
  34.             //write data
  35.             if (write(webclient,payload_data,rul)<=0)
  36.                 break;
  37.         }while(size<head.payload_length);
  38.     }
  39. }


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/241831
推荐阅读
相关标签
  

闽ICP备14008679号