首先感谢SGuniver_22,本文实现内容,是在他实现的websocket基础上,移植而来。
他的博客:https://blog.csdn.net/SGuniver_22
他的github:https://github.com/wexiangis/websocket_for_linux
移植代码下载:https://download.csdn.net/download/zn2857/16671309
1.首先去git下载websocket源码
。。。
2.实现ssl(asr1601平台实现了,只调用库函数,没有源代码)
#ifdef CA_CERT
const unsigned char app_ca1_cert[] = {
"-----BEGIN CERTIFICATE-----\r\n"
"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\r\n"
"-----END CERTIFICATE-----\r\n"
};
#endif
#ifdef OWN_CERT
const unsigned char client_cert[] = {
};
const unsigned char client_key[] = {
};
#endif
static struct ssl_client * ssl_client_init(int fd);
static void ssl_client_shutdown(struct ssl_client * client);
static int ssl_client_write( void *ssl, const unsigned char *buf, int len );
static int ssl_client_read( void *ssl, unsigned char *buf, int len );
static unsigned int havege_rand(void *unused, unsigned char * unusedstr, size_t unusedt)
{
char * p = (char *)malloc(16);
unsigned int rand = (unsigned int)p;
free(p);
if ((rand & 0xff) == 0) {
rand |= 0x96;
}
return rand;
}
static void debug(void *userdata, int level,
const char *filename, int line,
const char *msg)
{
//ssl_debug("%s", msg);
}
//生成握手key的长度
#define WEBSOCKET_SHAKE_KEY_LEN 16
struct ssl_client {
mbedtls_ssl_context ssl;
mbedtls_ssl_config config;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
int fd;
};
struct ssl_client * ssl_c = NULL;
static int ssl_client_recv(void * ctx, unsigned char * buf, unsigned int len)
{
struct ssl_client * client = (struct ssl_client *)ctx;
return recv(client->fd, buf, len, 0);
}
static int ssl_client_send(void * ctx, const unsigned char *buf, unsigned int len)
{
struct ssl_client * client = (struct ssl_client *)ctx;
return send(client->fd, buf, len, 0);
}
#ifdef CA_CERT
#define HOSTNAME "asr"
#endif
static struct ssl_client * ssl_client_init(int fd)
{
char *pers = "ssl_client";
int ret;
struct ssl_client * client = (struct ssl_client *)malloc(sizeof(struct ssl_client));
if (!client) {
printf("Cannot malloc memory(ssl_client_init)\n");
return NULL;
}
memset(client, 0, sizeof(struct ssl_client));
#ifdef CA_CERT
mbedtls_x509_crt *ca_chain;
ca_chain = (mbedtls_x509_crt *)malloc(sizeof(mbedtls_x509_crt));
memset(ca_chain, 0, sizeof(mbedtls_x509_crt));
#endif
mbedtls_entropy_init(&client->entropy);
mbedtls_ctr_drbg_init(&client->ctr_drbg);
if((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func,
&client->entropy,
(unsigned char *)pers, strlen(pers))) != 0)
{
printf( " failed\n ! ctr_drbg_init returned %d\n", ret );
goto failed;
}
mbedtls_ssl_init(&(client->ssl));
mbedtls_ssl_config_init(&(client->config));
client->fd = fd;
mbedtls_ssl_conf_rng(&(client->config), mbedtls_ctr_drbg_random, &client->ctr_drbg);
#ifdef CA_CERT
mbedtls_x509_crt_parse(ca_chain,app_ca1_cert,strlen(app_ca1_cert));
mbedtls_ssl_conf_ca_chain(&(client->config), ca_chain, NULL);
mbedtls_ssl_set_hostname(&(client->ssl), HOSTNAME);
#endif
#ifdef OWN_CERT
mbedtls_x509_crt *client_chain = NULL;
mbedtls_pk_context *client_rsa = NULL;
client_chain = (mbedtls_x509_crt *)malloc(sizeof(mbedtls_x509_crt));
memset(client_chain, 0, sizeof(mbedtls_x509_crt));
mbedtls_x509_crt_parse(client_chain,client_cert,strlen(client_cert));
client_rsa = (mbedtls_pk_context *)malloc(sizeof(mbedtls_pk_context));
memset(client_rsa, 0, sizeof(mbedtls_rsa_context));
mbedtls_pk_parse_key(client_rsa, client_key, strlen(client_key), NULL,0);
mbedtls_ssl_conf_own_cert(&client->config, client_chain, client_rsa);
#endif
//mbedtls_ssl_conf_endpoint(&(client->config), MBEDTLS_SSL_IS_CLIENT);
mbedtls_ssl_config_defaults(&client->config, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
// mbedtls_ssl_conf_ciphersuites_for_version(&client->config, 0, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3);
#ifdef CA_CERT
mbedtls_ssl_conf_authmode(&client->config, MBEDTLS_SSL_VERIFY_REQUIRED);
#else
mbedtls_ssl_conf_authmode(&client->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
#endif
mbedtls_ssl_conf_rng(&client->config, mbedtls_ctr_drbg_random, &client->ctr_drbg);
mbedtls_ssl_conf_dbg(&client->config, debug, NULL);
//mbedtls_debug_set_threshold(1000);
mbedtls_ssl_set_bio(&client->ssl, client,
ssl_client_send, ssl_client_recv, NULL);
mbedtls_ssl_setup(&client->ssl, &client->config);
return client;
failed:
#ifdef CA_CERT
if(ca_chain) free(ca_chain);
#endif
if (client) free(client);
return NULL;
}
static int ssl_client_write( void *ssl, const unsigned char *buf, int len )
{
return ssl_write(ssl, buf, len );
}
static int ssl_client_read( void *ssl, unsigned char *buf, int len )
{
return ssl_read( ssl, buf, len );
}
3.在ws_connectToServer函数里将socket创建,连接,改为asr1601芯片平台实现方式。注意有的websocket服务器不需要调用fcntl函数设置非阻塞
int ws_createSocket(char *ip, int port)
{
int ret, sock = -1;
struct addrinfo hints;
struct addrinfo *result, *rp;
int port_buf[10] = {0};
printf("websocket_createSocket!\n");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
itoa(port,port_buf);
ret = getaddrinfo(ip, port_buf, &hints, &result);
if (ret != 0) {
printf("ws_createSocket: resolve error\n");
return result;
}
for (rp = result; rp; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock < 0) {
printf("ws_createSocket: socket error\n");
continue;
}
// ret = fcntl(sock, F_GETFL, 0);
// fcntl(sock, F_SETFL, ret | O_NONBLOCK);
ret = connect(sock, rp->ai_addr, rp->ai_addrlen);
if (ret < 0) {
printf("ws_createSocket:socket connect error\n");
close(sock);
sock = -1;
continue;
} else {
break;
}
}
freeaddrinfo(result);
return sock;
}
int ws_connectToServer(char *ip, int port, char *path, int timeoutMs)
{
int32_t ret, fd = -1;
int32_t timeoutCount = 0;
char retBuff[512] = {0};
char httpHead[512] = {0};
char shakeKey[128] = {0};
char *p;
fd = ws_createSocket(ip,port);
if(fd < 0)
return fd;
if(!ssl_c){
ssl_c = ssl_client_init(fd);
if(NULL == ssl_c)
return -1;
}
//发送http协议头
memset(shakeKey, 0, sizeof(shakeKey));
ws_buildShakeKey(shakeKey); //创建握手key
memset(httpHead, 0, sizeof(httpHead)); //创建协议包
ws_buildHttpHead(ip, port, path, shakeKey, (char *)httpHead); //组装http请求头
ssl_client_write(&(ssl_c->ssl), httpHead, strlen((const char *)httpHead));
#ifdef WS_DEBUG
sdk_uart_printf("ws_connectToServer: \r\n%s\r\n", httpHead);
#endif
while (1)
{
memset(retBuff, 0, sizeof(retBuff));
ret = ssl_client_read(&(ssl_c->ssl), retBuff, sizeof(retBuff));
if (ret > 0)
{
#ifdef WS_DEBUG
//显示http返回
sdk_uart_printf("ws_connectToServer: %d \r\n%s\r\n", ret, retBuff);
#endif
//返回的是http回应信息
if (strncmp((const char *)retBuff, "HTTP", 4) == 0)
{
//定位到握手字符串
if ((p = strstr((char *)retBuff, "sec-websocket-accept: ")) != NULL)
{
p += strlen("sec-websocket-accept: ");
sscanf((const char *)p, "%s\r\n", p);
//比对握手信息
if (ws_matchShakeKey(shakeKey, strlen((const char *)shakeKey), p, strlen((const char *)p)) == 0)
{
sdk_uart_printf("shake hands success\r\n");
return ssl_c;
}
//握手信号不对, 重发协议包
else
{
sdk_uart_printf("shake hands info err, resend pkg\r\n");
ret = ssl_client_write(&(ssl_c->ssl), httpHead, strlen((const char *)httpHead));
}
}
//重发协议包
else
{
sdk_uart_printf("shake hands signal err, resend pkg\r\n");
ret = ssl_client_write(&(ssl_c->ssl), httpHead, strlen((const char *)httpHead));
}
}
//显示异常返回数据
else
{
//#ifdef WS_DEBUG
if (retBuff[0] >= ' ' && retBuff[0] <= '~')
sdk_uart_printf("ws_connectToServer: %d\r\n%s\r\n", ret, retBuff);
else
{
p = retBuff;
sdk_uart_printf("ws_connectToServer: %d\r\n", ret);
while (*p)
sdk_uart_printf("%.2X ", *p++);
sdk_uart_printf("\r\n");
}
//#endif
}
}
else
{
ssl_client_write(&(ssl_c->ssl), httpHead, strlen((const char *)httpHead));
sdk_uart_printf("ws_connectToServer: \r\n%s\r\n", httpHead);
}
ws_delayms(1000);
//超时检查
if (++timeoutCount > timeoutMs * 2)
break;
}
//连接失败,返回耗时(负值)
ws_close(fd);
sdk_uart_printf("connect err\n");
return -timeoutCount;
}
4.实现初始化
// extern void update_the_cp_ver(char *cp_ver); // max length 128
// Device bootup hook before Phase1Inits.
// If you have some work to be init, you may implete it here.
// ex: you may start your task here. or do some initialize here.
void Phase1PreInits(void)
{
sdk_uart_printf("[CUST] enter %s\n", __func__);
sdk_uart_printf("[CUST] enter %s\n", __func__);
}
// Device bootup hook after Phase1Inits.
// If you have some work to be init, you may implete it here.
// ex: you may start your task here. or do some initialize here.
void Phase1PostInits(void)
{
sdk_uart_printf("[CUST] enter %s\n", __func__);
sdk_uart_printf("[CUST] enter %s\n", __func__);
}
// Device bootup hook before Phase2Inits.
// If you have some work to be init, you may implete it here.
// ex: you may start your task here. or do some initialize here.
void Phase2PreInits(void)
{
sdk_uart_printf("[CUST] enter %s\n", __func__);
sdk_uart_printf("[CUST] enter %s\n", __func__);
}
// Device bootup hook after Phase2Inits.
// If you have some work to be init, you may implete it here.
// ex: you may start your task here. or do some initialize here.
void Phase2PostInits(void)
{
int ret;
sdk_uart_printf("[CUST] enter %s\n", __func__);
sdk_uart_printf("[CUST] enter %s\n", __func__);
ret = OSAFlagCreate(&_flag_app_ref);
ASSERT(ret == OS_SUCCESS);
ret = OSATaskCreate(&_task_app_ref, _task_app_stack, _TASK_APP_STACK_SIZE, 120, "_task_app", _task_app, NULL);
ASSERT(ret == OS_SUCCESS);
ret = OSATimerCreate(&_timer_ref);
ASSERT(ret == OS_SUCCESS);
}
5.实现任务函数
static void _timer_callback(UINT32 tmrId)
{
OSAFlagSet(_flag_app_ref, TASK_TIMER_CHANGE_FLAG_BIT, OSA_FLAG_OR);
}
static int wait_dialer_reg_net(void)
{
while (!CM_GetDefaultConnectStatus()) {
sdk_uart_printf("[app]wait net ...");
ws_delayms(5000);
}
sdk_uart_printf("[app]NetWork dialer success");
return OS_SUCCESS;
}
static void _task_app(void *ptr)
{
int ret;
OSA_STATUS status;
UINT32 flag_value;
UINT32 flag_mask = TASK_TIMER_CHANGE_FLAG_BIT;
char send_buff[4096];
memset(send_buff, 0, sizeof(send_buff));
sprintf(send_buff, "Say hi~ from client");
WsData_Type retPkgType;
char recv_buff[4096];
//等待网络附着
wait_dialer_reg_net();
//连接websocket,尝试10次
if ((fd = ws_connectToServer(ip, port, "/api/ws", 5)) <= 0) //1-ssl
{
sdk_uart_printf("ws failed !\r\n");
}else
{
sdk_uart_printf("ws connect success! \n");
}
//开启定时器,驱动代码执行
OSATimerStart(_timer_ref, 3 * 200, 3 * 200, _timer_callback, 0); // 30 seconds timer
while(1) {
status = OSAFlagWait(_flag_app_ref, flag_mask, OSA_FLAG_OR_CLEAR, &flag_value, OSA_SUSPEND);
ASSERT(status == OS_SUCCESS);
if(fd)
{
ret = ws_send(fd, send_buff, strlen(send_buff), true, WDT_TXTDATA);
if(ret <= 0)
sdk_uart_printf("ws send err\n");
else
sdk_uart_printf("wst send: %s\n",send_buff);
memset(recv_buff, 0, sizeof(recv_buff));
ret = ws_recv(fd,recv_buff,sizeof(recv_buff) - 1,NULL);
if (ret <= 0) {
sdk_uart_printf("ws: recv error: %d", ret);
ws_close(fd);
}
sdk_uart_printf("ws ssl rcv: %s,len: %d\n",recv_buff,ret);
}
}
ws_close(fd);
}