- A+
本篇文章上接 C语言在linux的使用笔记
本篇属于第五步
在第四步中我们已经可以简单的完成 socket的服务端及客户端,我本来想直接在windows上写一个客户端链接到linux上的服务端,但是由于目前完全是小白一个,在windows编译上遇到很多解决不了的问题所以决定暂时放弃这个工作(没有老师百度google也很无力)。
现在打算利用 js中的ws:websocket 来作为客户端,不多说了直接上流程及代码(参考git上的案例)
1.创建TCP链接类 文件名 tcp.c
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
int passive_server(int port,int queue)
{
///定义sockfd
int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
///定义sockaddr_in
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(port);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
///bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)
{
perror("bind");
exit(1);
}
///listen,成功返回0,出错返回-1
if(listen(server_sockfd,queue) == -1)
{
perror("listen");
exit(1);
}
printf("监听%d端口\n",port);
return server_sockfd;
}
2. 创建一个接口文件 tcp.h
#pragma once
extern int passive_server(int port,int queue);
3. 创建主类 main.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include "tcp.h"
#define BUFFER_SIZE 1024
#define RESPONSE_HEADER_LEN_MAX 1024
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
/*-------------------------------------------------------------------
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 ... |
+---------------------------------------------------------------+
--------------------------------------------------------------------*/
typedef struct _frame_head {
char fin;
char opcode;
char mask;
unsigned long long payload_length;
char masking_key[4];
}frame_head;
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;
}
/**
* @brief _readline
* read a line string from all buffer
* @param allbuf
* @param level
* @param linebuf
* @return
*/
int _readline(char* allbuf,int level,char* linebuf)
{
int len = strlen(allbuf);
for (;level<len;++level)
{
if(allbuf[level]=='\r' && allbuf[level+1]=='\n')
return level+2;
else
*(linebuf++) = allbuf[level];
}
return -1;
}
int shakehands(int cli_fd)
{
//next line's point num
int level = 0;
//all request data
char buffer[BUFFER_SIZE];
//a line data
char linebuf[256];
//Sec-WebSocket-Accept
char sec_accept[32];
//sha1 data
unsigned char sha1_data[SHA_DIGEST_LENGTH+1]={0};
//reponse head buffer
char head[BUFFER_SIZE] = {0};
if (read(cli_fd,buffer,sizeof(buffer))<=0)
perror("read");
printf("request\n");
printf("%s\n",buffer);
do {
memset(linebuf,0,sizeof(linebuf));
level = _readline(buffer,level,linebuf);
//printf("line:%s\n",linebuf);
if (strstr(linebuf,"Sec-WebSocket-Key")!=NULL)
{
strcat(linebuf,GUID);
// printf("key:%s\nlen=%d\n",linebuf+19,strlen(linebuf+19));
SHA1((unsigned char*)&linebuf+19,strlen(linebuf+19),(unsigned char*)&sha1_data);
// printf("sha1:%s\n",sha1_data);
base64_encode(sha1_data,strlen(sha1_data),sec_accept);
// printf("base64:%s\n",sec_accept);
/* write the response */
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",head);
if (write(cli_fd,head,strlen(head))<0)
perror("write");
break;
}
}while((buffer[level]!='\r' || buffer[level+1]!='\n') && level!=-1);
return 0;
}
int recv_frame_head(int fd,frame_head* head)
{
char one_char;
/*read fin and op code*/
if (read(fd,&one_char,1)<=0)
{
perror("read fin");
return -1;
}
head->fin = (one_char & 0x80) == 0x80;
head->opcode = one_char & 0x0F;
if (read(fd,&one_char,1)<=0)
{
perror("read mask");
return -1;
}
head->mask = (one_char & 0x80) == 0X80;
/*get payload length*/
head->payload_length = one_char & 0x7F;
if (head->payload_length == 126)
{
char extern_len[2];
if (read(fd,extern_len,2)<=0)
{
perror("read extern_len");
return -1;
}
head->payload_length = (extern_len[0]&0xFF) << 8 | (extern_len[1]&0xFF);
}
else if (head->payload_length == 127)
{
char extern_len[8],temp;
int i;
if (read(fd,extern_len,8)<=0)
{
perror("read extern_len");
return -1;
}
for(i=0;i<4;i++)
{
temp = extern_len[i];
extern_len[i] = extern_len[7-i];
extern_len[7-i] = temp;
}
memcpy(&(head->payload_length),extern_len,8);
}
/*read masking-key*/
if (read(fd,head->masking_key,4)<=0)
{
perror("read masking-key");
return -1;
}
return 0;
}
/**
* @brief umask
* xor decode
* @param data
* @param len
* @param mask
*/
void umask(char *data,int len,char *mask)
{
int i;
for (i=0;i<len;++i)
*(data+i) ^= *(mask+(i%4));
}
int send_frame_head(int fd,frame_head* head)
{
char *response_head;
int head_length = 0;
if(head->payload_length<126)
{
response_head = (char*)malloc(2);
response_head[0] = 0x81;
response_head[1] = head->payload_length;
head_length = 2;
}
else if (head->payload_length<0xFFFF)
{
response_head = (char*)malloc(4);
response_head[0] = 0x81;
response_head[1] = 126;
response_head[2] = (head->payload_length >> 8 & 0xFF);
response_head[3] = (head->payload_length & 0xFF);
head_length = 4;
}
else
{
//no code
response_head = (char*)malloc(12);
// response_head[0] = 0x81;
// response_head[1] = 127;
// response_head[2] = (head->payload_length >> 8 & 0xFF);
// response_head[3] = (head->payload_length & 0xFF);
head_length = 12;
}
if(write(fd,response_head,head_length)<=0)
{
perror("write head");
return -1;
}
free(response_head);
return 0;
}
int main()
{
int ser_fd = passive_server(9527,20);
struct sockaddr_in client_addr;
socklen_t addr_length = sizeof(client_addr);
int conn = accept(ser_fd,(struct sockaddr*)&client_addr, &addr_length);
shakehands(conn);
int count = 10;
while (count--)
{
frame_head head;
int rul = recv_frame_head(conn,&head);
if (rul < 0)
break;
printf("fin=%d\nopcode=0x%X\nmask=%d\npayload_len=%llu\n",head.fin,head.opcode,head.mask,head.payload_length);
//echo head
send_frame_head(conn,&head);
//read payload data
char payload_data[1024] = {0};
int size = 0;
do {
int rul;
rul = read(conn,payload_data,1024);
if (rul<=0)
break;
size+=rul;
umask(payload_data,size,head.masking_key);
printf("recive:%s",payload_data);
//echo data
if (write(conn,payload_data,rul)<=0)
break;
}while(size<head.payload_length);
printf("\n-----------\n");
}
close(conn);
close(ser_fd);
}
3.编译前,先安装 libssl-dev
yum -y install libssl-dev
提示不需要任何更新(奇葩),但不知道为什么
只有手动安装了
下载源代码 下载地址
openssl-1.0.2q.tar (也可以用我的这个)
解压 tar -zxvf openssl-1.0.2q.tar
进入目录 cd openssl-1.0.2q
配置 ./config
安装 make install
默认会安装在 /usr/local/ssl 中
这个算作一个静态库 不要覆盖系统库以防破坏系统本身
安装成功后 查看 /usr/local/ssl/lib/ 是否有 libcrypto.a libssl.a这两个文件,如果存在则说明安装成功
4.编译
gcc main.c tcp.c -o server -I /usr/local/ssl/include -L /usr/local/ssl/lib -lssl -lcrypto
5.运行
./server
查看是否开启 netstat -tnl
tcp 0 0 0.0.0.0:9527 0.0.0.0:* LISTEN
看到 9527端口已经开启
6.客户端 client.html
<button onclick="svc_connectPlatform()"> connect</button>
<button onclick="svc_send('hello jack')"> send</button>
<script>
function svc_connectPlatform() {
//alert("");
var wsServer = 'ws://103.94.181.37:9527/';
try {
svc_websocket = new WebSocket(wsServer);
} catch (evt) {
console.log("new WebSocket error:" + evt.data);
svc_websocket = null;
if (typeof(connCb) != "undefined" && connCb != null)
connCb("-1", "connect error!");
return;
}
//alert("");
svc_websocket.onopen = svc_onOpen;
svc_websocket.onclose = svc_onClose;
svc_websocket.onmessage = svc_onMessage;
svc_websocket.onerror = svc_onError;
}
function svc_onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function svc_onClose(evt) {
console.log("Disconnected");
}
function svc_onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function svc_onError(evt) {
console.log('Error occured: ' + evt.data);
}
function svc_send(msg) {
if (svc_websocket.readyState == WebSocket.OPEN) {
svc_websocket.send(msg);
} else {
console.log("send failed. websocket not open. please check.");
}
}
</script>
开启服务端后,在用google浏览器打开 client.html 查看console.log的情况
效果图
其中很重要的是握手协议 参考网站
至此结束 第六步地址
- 我的微信
- 这是我的微信扫一扫
-
- 我的微信公众号
- 我的微信公众号扫一扫
-