我是靠谱客的博主 动听耳机,最近开发中收集的这篇文章主要介绍linux下c实现websocket连接服务,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一个典型的Websocket握手请求

提取客户端的Sec-Websocket-Accept的client_key

获取client_key 通过SHA-1 信息摘要计算 server_key

SHA-1信息摘

base64 解码&编码

发送经过SHA-1信息摘加密后的握手key

http&https数据封装

http&https数据拆分 

数据发送接口

数据接收接口


Websocket 使用和 HTTP 相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,Websocket 协议使用 80 端口;运行在 TLS 之上时,默认使用 443 端口。

一个典型的Websocket握手请求

客户端请求

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

服务器回应

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
  •  Connection 必须设置 Upgrade,表示客户端希望连接升级。
  •  Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
  •  Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。
  •  Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
  •  Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
  •  其他一些定义在 HTTP 协议中的字段,如 Cookie 等,也可以在 Websocket 中使用。

提取客户端的Sec-Websocket-Accept的client_key

char * fetchSecKey(const char * buf)
{
  char *key;
  char *keyBegin;
  char *flag="Sec-WebSocket-Key: ";
  int i=0, bufLen=0;
 
  key=(char *)malloc(WEB_SOCKET_KEY_LEN_MAX);
  memset(key,0, WEB_SOCKET_KEY_LEN_MAX);
  if(!buf)
    {
      return NULL;
    }
 
  keyBegin=strstr(buf,flag);
  if(!keyBegin)
    {
      return NULL;
    }
  keyBegin+=strlen(flag);
 
  bufLen=strlen(buf);
  for(i=0;i<bufLen;i++)
    {
      if(keyBegin[i]==0x0A||keyBegin[i]==0x0D)
	{
	  break;
	}
      key[i]=keyBegin[i];
    }
  
  return key;
}

获取client_key 通过SHA-1 信息摘要计算 server_key

char * computeAcceptKey(const char * buf)
{
  char * clientKey;
  char * serverKey; 
  char * sha1DataTemp;
  char * sha1Data;
  short temp;
  int i,n;
  const char * GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
  if(!buf)
    {
      return NULL;
    }
  clientKey=(char *)malloc(LINE_MAX);
  memset(clientKey,0,LINE_MAX);
  clientKey=fetchSecKey(buf);
 
  if(!clientKey)
    {
      return NULL;
    }
  strcat(clientKey,GUID);
  sha1DataTemp=sha1_hash(clientKey);
  n=strlen(sha1DataTemp);
  sha1Data=(char *)malloc(n/2+1);
  memset(sha1Data,0,n/2+1);
  for(i=0;i<n;i+=2)
    {      
      sha1Data[i/2]=htoi(sha1DataTemp,i,2);    
    } 
  serverKey = base64_encode(sha1Data, strlen(sha1Data)); 
  return serverKey;
}

SHA-1信息摘

typedef struct SHA1Context{
	unsigned Message_Digest[5];      
	unsigned Length_Low;             
	unsigned Length_High;            
	unsigned char Message_Block[64]; 
	int Message_Block_Index;         
	int Computed;                    
	int Corrupted;                   
} SHA1Context;
 
void SHA1Reset(SHA1Context *);
int SHA1Result(SHA1Context *);
void SHA1Input( SHA1Context *,const char *,unsigned);

 
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
 
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context *);
 
void SHA1Reset(SHA1Context *context){// 初始化动作
	context->Length_Low             = 0;
	context->Length_High            = 0;
	context->Message_Block_Index    = 0;
 
	context->Message_Digest[0]      = 0x67452301;
	context->Message_Digest[1]      = 0xEFCDAB89;
	context->Message_Digest[2]      = 0x98BADCFE;
	context->Message_Digest[3]      = 0x10325476;
	context->Message_Digest[4]      = 0xC3D2E1F0;
 
	context->Computed   = 0;
	context->Corrupted  = 0;
}
 
 
int SHA1Result(SHA1Context *context){// 成功返回1,失败返回0
	if (context->Corrupted) {
		return 0;
	}
	if (!context->Computed) {
		SHA1PadMessage(context);
		context->Computed = 1;
	}
	return 1;
}
 
 
void SHA1Input(SHA1Context *context,const char *message_array,unsigned length){
	if (!length) return;
 
	if (context->Computed || context->Corrupted){
		context->Corrupted = 1;
		return;
	}
 
	while(length-- && !context->Corrupted){
		context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
 
		context->Length_Low += 8;
 
		context->Length_Low &= 0xFFFFFFFF;
		if (context->Length_Low == 0){
			context->Length_High++;
			context->Length_High &= 0xFFFFFFFF;
			if (context->Length_High == 0) context->Corrupted = 1;
		}
 
		if (context->Message_Block_Index == 64){
			SHA1ProcessMessageBlock(context);
		}
		message_array++;
	}
}
 
void SHA1ProcessMessageBlock(SHA1Context *context){
	const unsigned K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
	int         t;                
	unsigned    temp;             
	unsigned    W[80];            
	unsigned    A, B, C, D, E;    
 
	for(t = 0; t < 16; t++) {
	W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
	W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
	W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
	W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
	}
	
	for(t = 16; t < 80; t++)  W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
 
	A = context->Message_Digest[0];
	B = context->Message_Digest[1];
	C = context->Message_Digest[2];
	D = context->Message_Digest[3];
	E = context->Message_Digest[4];
 
	for(t = 0; t < 20; t++) {
		temp =  SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
		temp &= 0xFFFFFFFF;
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 20; t < 40; t++) {
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
		temp &= 0xFFFFFFFF;
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 40; t < 60; t++) {
		temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
		temp &= 0xFFFFFFFF;
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	for(t = 60; t < 80; t++) {
		temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
		temp &= 0xFFFFFFFF;
		E = D;
		D = C;
		C = SHA1CircularShift(30,B);
		B = A;
		A = temp;
	}
	context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
	context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
	context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
	context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
	context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
	context->Message_Block_Index = 0;
}
 
void SHA1PadMessage(SHA1Context *context){
	if (context->Message_Block_Index > 55) {
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 64)  context->Message_Block[context->Message_Block_Index++] = 0;
		SHA1ProcessMessageBlock(context);
		while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
	} else {
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
	}
	context->Message_Block[56] = (context->Length_High >> 24 ) & 0xFF;
	context->Message_Block[57] = (context->Length_High >> 16 ) & 0xFF;
	context->Message_Block[58] = (context->Length_High >> 8 ) & 0xFF;
	context->Message_Block[59] = (context->Length_High) & 0xFF;
	context->Message_Block[60] = (context->Length_Low >> 24 ) & 0xFF;
	context->Message_Block[61] = (context->Length_Low >> 16 ) & 0xFF;
	context->Message_Block[62] = (context->Length_Low >> 8 ) & 0xFF;
	context->Message_Block[63] = (context->Length_Low) & 0xFF;
 
	SHA1ProcessMessageBlock(context);
}

char * sha1_hash(const char *source){// Main
	SHA1Context sha;
	char *buf;//[128];
 
	SHA1Reset(&sha);
	SHA1Input(&sha, source, strlen(source));
 
	if (!SHA1Result(&sha)){
		printf("SHA1 ERROR: Could not compute message digest");
		return NULL;
	} else {
	  buf=(char *)malloc(128);
		memset(buf,0,sizeof(buf));
		sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
		sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);
		return buf;
	}
}

base64 解码&编码

const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 
char* base64_encode(const char* data, int data_len); 
char *base64_decode(const char* data, int data_len); 
static char find_pos(char ch); 
/* */ 
char *base64_encode(const char* data, int data_len) 
{ 
    //int data_len = strlen(data); 
    int prepare = 0; 
    int ret_len; 
    int temp = 0; 
    char *ret = NULL; 
    char *f = NULL; 
    int tmp = 0; 
    char changed[4]; 
    int i = 0; 
    ret_len = data_len / 3; 
    temp = data_len % 3; 
    if (temp > 0) 
    { 
        ret_len += 1; 
    } 
    ret_len = ret_len*4 + 1; 
    ret = (char *)malloc(ret_len); 
      
    if ( ret == NULL) 
    { 
        printf("No enough memory.n"); 
        exit(0); 
    } 
    memset(ret, 0, ret_len); 
    f = ret; 
    while (tmp < data_len) 
    { 
        temp = 0; 
        prepare = 0; 
        memset(changed, '', 4); 
        while (temp < 3) 
        { 
            //printf("tmp = %dn", tmp); 
            if (tmp >= data_len) 
            { 
                break; 
            } 
            prepare = ((prepare << 8) | (data[tmp] & 0xFF)); 
            tmp++; 
            temp++; 
        } 
        prepare = (prepare<<((3-temp)*8)); 
        //printf("before for : temp = %d, prepare = %dn", temp, prepare); 
        for (i = 0; i < 4 ;i++ ) 
        { 
            if (temp < i) 
            { 
                changed[i] = 0x40; 
            } 
            else 
            { 
                changed[i] = (prepare>>((3-i)*6)) & 0x3F; 
            } 
            *f = base[changed[i]]; 
            //printf("%.2X", changed[i]); 
            f++; 
        } 
    } 
    *f = ''; 
      
    return ret; 
      
} 
/* */ 
static char find_pos(char ch)   
{ 
    char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[] 
    return (ptr - base); 
} 
/* */ 
char *base64_decode(const char *data, int data_len) 
{ 
    int ret_len = (data_len / 4) * 3; 
    int equal_count = 0; 
    char *ret = NULL; 
    char *f = NULL; 
    int tmp = 0; 
    int temp = 0; 
    char need[3]; 
    int prepare = 0; 
    int i = 0; 
    if (*(data + data_len - 1) == '=') 
    { 
        equal_count += 1; 
    } 
    if (*(data + data_len - 2) == '=') 
    { 
        equal_count += 1; 
    } 
    if (*(data + data_len - 3) == '=') 
    {//seems impossible 
        equal_count += 1; 
    } 
    switch (equal_count) 
    { 
    case 0: 
        ret_len += 4;//3 + 1 [1 for NULL] 
        break; 
    case 1: 
        ret_len += 4;//Ceil((6*3)/8)+1 
        break; 
    case 2: 
        ret_len += 3;//Ceil((6*2)/8)+1 
        break; 
    case 3: 
        ret_len += 2;//Ceil((6*1)/8)+1 
        break; 
    } 
    ret = (char *)malloc(ret_len); 
    if (ret == NULL) 
    { 
        printf("No enough memory.n"); 
        exit(0); 
    } 
    memset(ret, 0, ret_len); 
    f = ret; 
    while (tmp < (data_len - equal_count)) 
    { 
        temp = 0; 
        prepare = 0; 
        memset(need, 0, 4); 
        while (temp < 4) 
        { 
            if (tmp >= (data_len - equal_count)) 
            { 
                break; 
            } 
            prepare = (prepare << 6) | (find_pos(data[tmp])); 
            temp++; 
            tmp++; 
        } 
        prepare = prepare << ((4-temp) * 6); 
        for (i=0; i<3 ;i++ ) 
        { 
            if (i == temp) 
            { 
                break; 
            } 
            *f = (char)((prepare>>((2-i)*8)) & 0xFF); 
            f++; 
        } 
    } 
    *f = ''; 
    return ret; 
}

进制转化

int tolower(int c) 
{ 
    if (c >= 'A' && c <= 'Z') 
    { 
        return c + 'a' - 'A'; 
    } 
    else 
    { 
        return c; 
    } 
} 
 
int htoi(const char s[],int start,int len) 
{ 
  int i,j; 
    int n = 0; 
    if (s[0] == '0' && (s[1]=='x' || s[1]=='X')) //判断是否有前导0x或者0X
    { 
        i = 2; 
    } 
    else 
    { 
        i = 0; 
    } 
    i+=start;
    j=0;
    for (; (s[i] >= '0' && s[i] <= '9') 
	   || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >='A' && s[i] <= 'F');++i) 
    {   
        if(j>=len)
	{
	  break;
	}
        if (tolower(s[i]) > '9') 
        { 
            n = 16 * n + (10 + tolower(s[i]) - 'a'); 
        } 
        else 
        { 
            n = 16 * n + (tolower(s[i]) - '0'); 
        } 
	j++;
    } 
    return n; 
} 

发送经过SHA-1信息摘加密后的握手key

void shakeHand(int connfd,const char *serverKey)
{
  char responseHeader [RESPONSE_HEADER_LEN_MAX];
  if(!connfd)
    {
      return;
    }
  if(!serverKey)
    {
      return;
    }
  memset(responseHeader,'',RESPONSE_HEADER_LEN_MAX);
  sprintf(responseHeader, "HTTP/1.1 101 Switching Protocolsrn");
  sprintf(responseHeader, "%sUpgrade: websocketrn", responseHeader);
  sprintf(responseHeader, "%sConnection: Upgradern", responseHeader);
  sprintf(responseHeader, "%sSec-WebSocket-Accept: %srnrn", responseHeader, serverKey);
  printf("Response Header:%sn",responseHeader);
  write(connfd,responseHeader,strlen(responseHeader));
}

http&https数据封装

char* packData(const char* message, unsigned long* len)
{
	char* data = NULL;
	unsigned long n = 0;

	n = strlen(message);
	if (n < 126)
	{
		data = (char*)malloc(n + 2);
		memset(data, 0, n + 2);
		data[0] = 0x81;
		data[1] = n;
		memcpy(data + 2, message, n);
		*len = n + 2;
	}
	else if (n < 0xFFFF)
	{
		data = (char*)malloc(n + 4);
		memset(data, 0, n + 4);
		data[0] = 0x81;
		data[1] = 126;
		data[2] = (n >> 8 & 0xFF);
		data[3] = (n & 0xFF);
		memcpy(data + 4, message, n);
		*len = n + 4;
	}
	else
	{

		// 暂不处理超长内容  
		*len = 0;
	}


	return data;
}

http&https数据拆分 

char * analyData(const char * buf,const int bufLen)
{
  char * data;
  char fin, maskFlag,masks[4];
  char * payloadData;
  char temp[8];
  unsigned long n, payloadLen=0;
  unsigned short usLen=0;
  int i=0; 
 
 
 if (bufLen < 2) 
   {
     return NULL;
   }
 
  fin = (buf[0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
  if (!fin)
   {
       return NULL;// 超过一帧暂不处理 
   }
 
   maskFlag = (buf[1] & 0x80) == 0x80; // 是否包含掩码  
   if (!maskFlag)
   {
       return NULL;// 不包含掩码的暂不处理
   }
 
   payloadLen = buf[1] & 0x7F; // 数据长度 
   if (payloadLen == 126)
   {      
     memcpy(masks,buf+4, 4);      
     payloadLen =(buf[2]&0xFF) << 8 | (buf[3]&0xFF);  
     payloadData=(char *)malloc(payloadLen);
     memset(payloadData,0,payloadLen);
     memcpy(payloadData,buf+8,payloadLen);
    }
    else if (payloadLen == 127)
    {
     memcpy(masks,buf+10,4);  
     for ( i = 0; i < 8; i++)
     {
         temp[i] = buf[9 - i];
     } 
 
     memcpy(&n,temp,8);  
     payloadData=(char *)malloc(n); 
     memset(payloadData,0,n); 
     memcpy(payloadData,buf+14,n);//toggle error(core dumped) if data is too long.
     payloadLen=n;    
     }
     else
     {   
      memcpy(masks,buf+2,4);    
      payloadData=(char *)malloc(payloadLen);
      memset(payloadData,0,payloadLen);
      memcpy(payloadData,buf+6,payloadLen); 
     }
 
     for (i = 0; i < payloadLen; i++)
     {
       payloadData[i] = (char)(payloadData[i] ^ masks[i % 4]);
     }
 
     printf("data(%d):%sn",payloadLen,payloadData);
     return payloadData;
}

数据发送接口

char sendData(char* json, int socket_fd)
{
	int i = 0, j = 0;
	int maxfd = 0, sel_ret = 0, ret = 0, fd = socket_fd;
	fd_set fds;
	char* pdata = NULL;
	struct timeval timeout = { 0 };
	long send_len = 0, total_send_len = 0, curr_send_len = 0;

	/* 接收客户端菜单指令并处理 */
	while (1) {
		/*防止过多占用CPU*/
		usleep(1000);
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		maxfd = fd + 1;
		timeout.tv_sec = 0;
		timeout.tv_usec = 100;
		sel_ret = 0;
		sel_ret = select(maxfd, NULL, &fds, NULL, &timeout);
		if (sel_ret <= 0) {
			continue;
		}
		if (FD_ISSET(fd, &fds))
		{
			total_send_len = 0;
			
			//根据收到的数据的第一个字节将数据封装特定JSON格式数据类型后再转base64发送
			if (json != NULL)
			{
				pdata = packData(json, &send_len);//http封装
				if (pdata)
				{
					while (total_send_len < send_len)
					{
						curr_send_len = send(fd, pdata + total_send_len, send_len - total_send_len, MSG_NOSIGNAL);
						if (curr_send_len > 0) {
							total_send_len += curr_send_len;
						}
						else {
							ret = SOCKRTEXIT;
							goto socket_exit;//socket 断开
						}
					}
					free(pdata);
					pdata = NULL;
				}
			}


			break;
		}
	}
socket_exit:
	if (pdata)
	{
		free(pdata);
		pdata = NULL;
	}
	return ret;
}

数据接收接口

char recvData(int fd,char *buf)
{
	int i = 0, j = 0;
	int maxfd = 0, sel_ret = 0, ret = 0;
	int fd;
	char* pdata = NULL;
	fd_set fdr;
	char recv_flag = 1;
	struct timeval timeout = { 0 };
	unsigned int total_recv_len = 0;
	unsigned int statime, curtime;
	char data[1024] = { 0 };
	while (1) {

		FD_ZERO(&fdr);
		FD_SET(fd, &fdr);
		maxfd = fd + 1;
		timeout.tv_sec = 0;
		timeout.tv_usec = 100;
		sel_ret = 0;
		sel_ret = select(maxfd, &fdr, NULL, NULL, &timeout);
		if (sel_ret <= 0) {
				continue;
		}
		if (FD_ISSET(fd, &fdr))
		{
			recv_flag = 1;
			memset(data, 0, 1024);
			total_recv_len = recv(socketinfo.client[i].socket_fd, data, sizeof(data), 0);
			buf = analyData(data, &total_recv_len);
		}
		if (strlen(data))
		{
			break;
		}
		usleep(1000);
	}
socket_exit:
	if (pdata) {
		free(pdata);
		pdata = NULL;
	}
	return ret;
}

最后

以上就是动听耳机为你收集整理的linux下c实现websocket连接服务的全部内容,希望文章能够帮你解决linux下c实现websocket连接服务所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部