不会练习 QQ:2817228206
-
首先我们要知道http是什么? 为什么要有socket,socket和http的区别在那里
image.png
我们知道 http1.0 只有简单的request和response,到了1.1 才有了改变,一般的请求都是走3次握手和4次挥手
第一次握手:起初两端都处于CLOSED关闭状态,Client将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给Server,Client进入SYN-SENT状态,等待Server确认;
第二次握手:Server收到数据包后由标志位SYN=1得知Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN-RCVD状态,此时操作系统为该TCP连接分配TCP缓存和变量;
第三次握手:Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并且此时操作系统为该TCP连接分配TCP缓存和变量,并将该数据包发送给Server,Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client和Server就可以开始传输数据。
4次挥手
起初A和B处于ESTABLISHED状态——A发出连接释放报文段并处于FIN-WAIT-1状态——B发出确认报文段且进入CLOSE-WAIT状态——A收到确认后,进入FIN-WAIT-2状态,等待B的连接释放报文段——B没有要向A发出的数据,B发出连接释放报文段且进入LAST-ACK状态——A发出确认报文段且进入TIME-WAIT状态——B收到确认报文段后进入CLOSED状态——A经过等待计时器时间2MSL后,进入CLOSED状态。
前端
var ws = new WebSocket("ws://127.0.0.1:17122");
//握手监听函数
ws.onopen=function(){
//状态为1证明握手成功,然后把client自定义的名字发送过去
if(so.readyState==1){
//握手成功后对服务器发送信息
so.send('type=add&ming='+n);
}
}
//错误返回信息函数
ws.onerror = function(){
console.log("error");
};
//监听服务器端推送的消息
ws.onmessage = function (msg){
console.log(msg);
}
//断开WebSocket连接
ws.onclose = function(){
ws = false;
}
php tools.php
<?php
//解码数据
function unmask($text) {
$length = ord($text[1]) & 127;
if($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
}
elseif($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
}
else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
function hand_shake($buf){
$buf = substr($buf,strpos($buf,'Sec-WebSocket-Key:')+18);
$key = trim(substr($buf,0,strpos($buf,"\r\n")));
$new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$new_message = "HTTP/1.1 101 Switching Protocols\r\n";
$new_message .= "Upgrade: websocket\r\n";
$new_message .= "Sec-WebSocket-Version: 13\r\n";
$new_message .= "Connection: Upgrade\r\n";
$new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
return $new_message;
}
// encode
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$text;
}
php server.php
<?php
include_once("tools.php");
$address = "127.0.0.1";
$port = 20000; //调试的时候,可以多换端口来测试程序!
set_time_limit(0);
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_block($sock);
socket_bind($sock, $address, $port);
socket_listen($sock, 4);
while(true){
// 建立连接
$msgsock = socket_accept($sock);
// 读取数据 客户端发过来的
$buf = socket_read($msgsock, 8192);
// 响应加密
$response = hand_shake($buf);
// 发送响应
socket_write($msgsock,$response,strlen($response));
//正式开始通信...
$buf = socket_read($msgsock, 8192); //获取前端发送的消息
if(unmask($buf)==1){
socket_write($msgsock, mask(json_encode(['msg'=>true])), strlen($response_text));
}
if(unmask($buf)!=1){
//一旦编码出问题,就是返回空,又不报错
$msg = '[{
"Addr": "/ip4/95.179.192.99/tcp/10001",
"Peer": "QmQFy5XWmtZ7U2nZu198qYW8LefvrBK3Y9b3V1MqUfuoRT",
"Latency": "",
"Muxer": "你好",
"Direction": 0,
"Streams": null
}]';
$response_text = mask($msg);
socket_write($msgsock, $response_text, strlen($response_text));
}
// socket_close($msgsock);
};
// socket_close($sock);
网友评论