Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
ZLMediaKit
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
张翔宇
ZLMediaKit
Commits
644905a3
Commit
644905a3
authored
Sep 16, 2019
by
xiongziliang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
实现websocket客户端模板
parent
30950757
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
85 行增加
和
289 行删除
+85
-289
3rdpart/ZLToolKit
+1
-1
src/Http/WebSocketClient.cpp
+0
-288
src/Http/WebSocketClient.h
+0
-0
tests/test_wsClient.cpp
+84
-0
没有找到文件。
ZLToolKit
@
91246bb0
Subproject commit
0b406073125080ab8edd13ee7c14e573e54baa3
5
Subproject commit
91246bb01475c7336040a4b7ec35d0584887f36
5
src/Http/WebSocketClient.cpp
deleted
100644 → 0
查看文件 @
30950757
#include "WebSocketClient.h"
int
mediakit
::
WebSocketClient
::
send
(
const
string
&
buf
)
{
if
(
_sock
)
{
if
(
_WSClientStatus
==
WORKING
)
{
_session
->
send
(
buf
);
return
0
;
}
else
{
return
-
1
;
}
}
}
void
mediakit
::
WebSocketClient
::
clear
()
{
_method
.
clear
();
_path
.
clear
();
_parser
.
Clear
();
_recvedBodySize
=
0
;
_totalBodySize
=
0
;
_aliveTicker
.
resetTime
();
_chunkedSplitter
.
reset
();
HttpRequestSplitter
::
reset
();
}
const
std
::
string
&
mediakit
::
WebSocketClient
::
responseStatus
()
const
{
return
_parser
.
Url
();
}
const
mediakit
::
WebSocketClient
::
HttpHeader
&
mediakit
::
WebSocketClient
::
responseHeader
()
const
{
return
_parser
.
getValues
();
}
const
mediakit
::
Parser
&
mediakit
::
WebSocketClient
::
response
()
const
{
return
_parser
;
}
const
std
::
string
&
mediakit
::
WebSocketClient
::
getUrl
()
const
{
return
_url
;
}
int64_t
mediakit
::
WebSocketClient
::
onResponseHeader
(
const
string
&
status
,
const
HttpHeader
&
headers
)
{
DebugL
<<
status
;
//无Content-Length字段时默认后面全是content
return
-
1
;
}
void
mediakit
::
WebSocketClient
::
onResponseBody
(
const
char
*
buf
,
int64_t
size
,
int64_t
recvedSize
,
int64_t
totalSize
)
{
DebugL
<<
size
<<
" "
<<
recvedSize
<<
" "
<<
totalSize
;
}
void
mediakit
::
WebSocketClient
::
onResponseCompleted
()
{
DebugL
;
}
int64_t
mediakit
::
WebSocketClient
::
onRecvHeader
(
const
char
*
data
,
uint64_t
len
)
{
_parser
.
Parse
(
data
);
if
(
_parser
.
Url
()
==
"101"
)
{
switch
(
_WSClientStatus
)
{
case
HANDSHAKING
:
{
StrCaseMap
&
valueMap
=
_parser
.
getValues
();
auto
key
=
valueMap
.
find
(
"Sec-WebSocket-Accept"
);
if
(
key
!=
valueMap
.
end
()
&&
key
->
second
.
length
()
>
0
)
{
onConnect
(
SockException
());
}
break
;
}
}
return
-
1
;
}
else
{
shutdown
(
SockException
(
Err_shutdown
,
_parser
.
Url
().
c_str
()));
return
0
;
}
return
-
1
;
}
void
mediakit
::
WebSocketClient
::
onRecvContent
(
const
char
*
data
,
uint64_t
len
)
{
if
(
_chunkedSplitter
)
{
_chunkedSplitter
->
input
(
data
,
len
);
return
;
}
auto
recvedBodySize
=
_recvedBodySize
+
len
;
if
(
_totalBodySize
<
0
)
{
//不限长度的content,最大支持INT64_MAX个字节
onResponseBody
(
data
,
len
,
recvedBodySize
,
INT64_MAX
);
_recvedBodySize
=
recvedBodySize
;
return
;
}
//固定长度的content
if
(
recvedBodySize
<
_totalBodySize
)
{
//content还未接收完毕
onResponseBody
(
data
,
len
,
recvedBodySize
,
_totalBodySize
);
_recvedBodySize
=
recvedBodySize
;
return
;
}
//content接收完毕
onResponseBody
(
data
,
_totalBodySize
-
_recvedBodySize
,
_totalBodySize
,
_totalBodySize
);
bool
biggerThanExpected
=
recvedBodySize
>
_totalBodySize
;
onResponseCompleted_l
();
if
(
biggerThanExpected
)
{
//声明的content数据比真实的小,那么我们只截取前面部分的并断开链接
shutdown
(
SockException
(
Err_shutdown
,
"http response content size bigger than expected"
));
}
}
void
mediakit
::
WebSocketClient
::
onConnect
(
const
SockException
&
ex
)
{
_aliveTicker
.
resetTime
();
if
(
ex
)
{
onDisconnect
(
ex
);
return
;
}
//先假设http客户端只会接收一点点数据(只接受http头,节省内存)
_sock
->
setReadBuffer
(
std
::
make_shared
<
BufferRaw
>
(
1
*
1024
));
_totalBodySize
=
0
;
_recvedBodySize
=
0
;
HttpRequestSplitter
::
reset
();
_chunkedSplitter
.
reset
();
if
(
_WSClientStatus
==
WSCONNECT
)
{
//Websocket握手
string
random
=
get_random
(
16
);
auto
Sec_WebSocket_Key
=
encodeBase64
(
SHA1
::
encode_bin
(
random
));
_key
=
Sec_WebSocket_Key
;
string
p
=
generate_websocket_client_handshake
(
_ip
.
c_str
(),
_port
,
_url
.
c_str
(),
_key
.
c_str
());
TcpClient
::
send
(
p
);
_WSClientStatus
=
HANDSHAKING
;
}
else
if
(
_WSClientStatus
==
HANDSHAKING
)
{
_WSClientStatus
=
WORKING
;
}
onFlush
();
}
void
mediakit
::
WebSocketClient
::
onRecv
(
const
Buffer
::
Ptr
&
pBuf
)
{
_aliveTicker
.
resetTime
();
if
(
_WSClientStatus
==
HANDSHAKING
||
_WSClientStatus
==
WSCONNECT
)
HttpRequestSplitter
::
input
(
pBuf
->
data
(),
pBuf
->
size
());
else
if
(
_WSClientStatus
==
WORKING
)
{
WebSocketSplitter
::
decode
((
uint8_t
*
)
pBuf
->
data
(),
pBuf
->
size
());
}
}
void
mediakit
::
WebSocketClient
::
onErr
(
const
SockException
&
ex
)
{
_session
->
onError
(
ex
);
onDisconnect
(
ex
);
}
void
mediakit
::
WebSocketClient
::
onManager
()
{
if
(
_WSClientStatus
!=
WORKING
)
{
if
(
_fTimeOutSec
>
0
&&
_aliveTicker
.
elapsedTime
()
>
_fTimeOutSec
*
1000
)
{
//超时
shutdown
(
SockException
(
Err_timeout
,
"ws server respone timeout"
));
}
}
else
_session
->
onManager
();
}
std
::
string
mediakit
::
WebSocketClient
::
generate_websocket_client_handshake
(
const
char
*
ip
,
uint16_t
port
,
const
char
*
path
,
const
char
*
key
)
{
/**
* @brief 业务数据被分片的单片最大大小, 等于 65535 - 14 - 1
*/
#define DATA_FRAME_MAX_LEN 65520
#define HANDSHAKE_SIZE 1024
char
buf
[
HANDSHAKE_SIZE
]
=
{
0
};
snprintf
(
buf
,
HANDSHAKE_SIZE
,
"GET %s HTTP/1.1
\r\n
"
"Host: %s:%d
\r\n
"
"Upgrade: websocket
\r\n
"
"Connection: Upgrade
\r\n
"
"Sec-WebSocket-Key: %s
\r\n
"
"Sec-WebSocket-Version: 13
\r\n
"
"
\r\n
"
,
path
,
ip
,
port
,
key
);
string
temBuf
(
buf
);
return
temBuf
;
}
std
::
string
mediakit
::
WebSocketClient
::
get_random
(
size_t
n
)
{
random_device
rd
;
_StrPrinter
printer
;
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
unsigned
int
rnd
=
rd
();
printer
<<
rnd
%
9
;
}
return
string
(
printer
);
}
void
mediakit
::
WebSocketClient
::
onWebSocketDecodeHeader
(
const
WebSocketHeader
&
packet
)
{
//新包,原来的包残余数据清空掉
_remian_data
.
clear
();
if
(
_firstPacket
)
{
//这是个WebSocket会话而不是普通的Http会话
_firstPacket
=
false
;
//此处截取数据并进行websocket协议打包
}
}
void
mediakit
::
WebSocketClient
::
onWebSocketDecodePlayload
(
const
WebSocketHeader
&
packet
,
const
uint8_t
*
ptr
,
uint64_t
len
,
uint64_t
recved
)
{
_remian_data
.
append
((
char
*
)
ptr
,
len
);
}
void
mediakit
::
WebSocketClient
::
onWebSocketDecodeComplete
(
const
WebSocketHeader
&
header_in
)
{
WebSocketHeader
&
header
=
const_cast
<
WebSocketHeader
&>
(
header_in
);
auto
flag
=
header
.
_mask_flag
;
header
.
_mask_flag
=
false
;
switch
(
header
.
_opcode
)
{
case
WebSocketHeader
:
:
CLOSE
:
{
shutdown
(
SockException
(
Err_timeout
,
"session timeouted"
));
}
break
;
case
WebSocketHeader
:
:
PING
:
{
const_cast
<
WebSocketHeader
&>
(
header
).
_opcode
=
WebSocketHeader
::
PONG
;
WebSocketSplitter
::
encode
(
header
,
(
uint8_t
*
)
_remian_data
.
data
(),
_remian_data
.
size
());
}
break
;
case
WebSocketHeader
:
:
CONTINUATION
:
{
}
break
;
case
WebSocketHeader
:
:
TEXT
:
case
WebSocketHeader
:
:
BINARY
:
{
BufferString
::
Ptr
buffer
=
std
::
make_shared
<
BufferString
>
(
_remian_data
);
_session
->
onRecv
(
buffer
);
}
break
;
default
:
break
;
}
_remian_data
.
clear
();
header
.
_mask_flag
=
flag
;
}
void
mediakit
::
WebSocketClient
::
onWebSocketEncodeData
(
const
uint8_t
*
ptr
,
uint64_t
len
)
{
TcpClient
::
send
(
string
((
char
*
)
ptr
,
len
));
}
void
mediakit
::
WebSocketClient
::
onResponseCompleted_l
()
{
_totalBodySize
=
0
;
_recvedBodySize
=
0
;
onResponseCompleted
();
}
src/Http/WebSocketClient.h
查看文件 @
644905a3
差异被折叠。
点击展开。
tests/test_wsClient.cpp
0 → 100644
查看文件 @
644905a3
/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <signal.h>
#include <string>
#include <iostream>
#include "Util/MD5.h"
#include "Util/logger.h"
#include "Http/WebSocketClient.h"
using
namespace
std
;
using
namespace
toolkit
;
using
namespace
mediakit
;
class
EchoTcpClient
:
public
TcpClient
{
public
:
EchoTcpClient
(
const
EventPoller
::
Ptr
&
poller
=
nullptr
){
InfoL
;
}
~
EchoTcpClient
()
override
{
InfoL
;
}
protected
:
void
onRecv
(
const
Buffer
::
Ptr
&
pBuf
)
override
{
DebugL
<<
pBuf
->
toString
();
}
//被动断开连接回调
void
onErr
(
const
SockException
&
ex
)
override
{
WarnL
<<
ex
.
what
();
}
//tcp连接成功后每2秒触发一次该事件
void
onManager
()
override
{
send
(
"echo test!"
);
DebugL
<<
"send echo test"
;
}
//连接服务器结果回调
void
onConnect
(
const
SockException
&
ex
)
override
{
DebugL
<<
ex
.
what
();
}
//数据全部发送完毕后回调
void
onFlush
()
override
{
DebugL
;
}
};
int
main
(
int
argc
,
char
*
argv
[])
{
//设置退出信号处理函数
static
semaphore
sem
;
signal
(
SIGINT
,
[](
int
)
{
sem
.
post
();
});
// 设置退出信号
//设置日志
Logger
::
Instance
().
add
(
std
::
make_shared
<
ConsoleChannel
>
());
Logger
::
Instance
().
setWriter
(
std
::
make_shared
<
AsyncLogWriter
>
());
WebSocketClient
<
EchoTcpClient
>::
Ptr
client
=
std
::
make_shared
<
WebSocketClient
<
EchoTcpClient
>
>
();
client
->
startConnect
(
"127.0.0.1"
,
80
);
sem
.
wait
();
return
0
;
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论