PHP Socket服务器搭建和测试
1.socket服务器搭建思路
1) 目的:理解socket服务器工作机制
2) 思路:创建socket -> 把socket加入连接池 -> 处理接收信息 -> 握手动作 -> 发送信息
2.socket服务器代码
注:复制到php文件,直接命令行可以运行,不需要其他支持
特别注意:为了能传输中文_sendMsg做了json_encode()
<?php /**
* Socket服务器
* @author wuchangliang 2018/1/17
*/
class SocketServer
{
private $sockets; //连接池
private $master;
private $handshake; /**
* @param $address
* @param $port
*/
public function run($address, $port)
{
//配置错误级别、运行时间、刷新缓冲区
echo iconv('UTF-8', 'GBK', "欢迎来到PHP Socket测试服务。 \n");
error_reporting(0);
set_time_limit(0);
ob_implicit_flush(); //创建socket
$this->master = $this->_connect($address, $port);
$this->sockets[] = $this->master; //运行socket
while (true) {
$sockets = $this->sockets;
$write = NULL;
$except = NULL;
socket_select($sockets, $write, $except, NULL); //$write,$except传引用
foreach ($sockets as $socket) {
if ($socket == $this->master) {
$client = socket_accept($socket);
$this->handshake = false;
if ($client) {
$this->sockets[] = $client; //加入连接池
}
} else {
//接收信息
$bytes = @socket_recv($socket, $buffer, 2048, 0);
if ($bytes <= 6) {
$this->_disConnect($socket);
continue;
}; //处理信息
if (!$this->handshake) {
$this->_handshake($socket, $buffer);
} else {
$buffer = $this->_decode($buffer);
$this->_sendMsg($buffer, $socket);
}
} }
}
} /**
* 创建socket连接
* @param $address
* @param $port
* @return resource
*/
private function _connect($address, $port)
{
//创建socket
$master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
or die("socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n");
socket_bind($master, $address, $port)
or die("socket_bind() failed: reason: " . socket_strerror(socket_last_error($master)) . "\n");
socket_listen($master, 5)
or die("socket_listen() failed: reason: " . socket_strerror(socket_last_error($master)) . "\n");
return $master;
} /**
* 握手动作
* @param $socket
* @param $buffer
*/
private function _handshake($socket, $buffer)
{
//握手动作信息
$buf = substr($buffer, strpos($buffer, '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"; //记录握手动作
socket_write($socket, $new_message, strlen($new_message));
$this->handshake = true;
} /**
* 断开socket连接
* @param $socket
*/
private function _disConnect($socket)
{
$index = array_search($socket, $this->sockets);
socket_close($socket);
if ($index >= 0) {
array_splice($this->sockets, $index, 1);
}
} /**
* 发送信息
* @param $buffer
* @param $client
*/
private function _sendMsg($buffer, $client)
{
$send_buffer = $this->_frame(json_encode($buffer));
foreach ($this->sockets as $socket) {
if ($socket != $this->master && $socket != $client) { //广播发送(除了自己)
socket_write($socket, $send_buffer, strlen($send_buffer));
}
}
} /**
* 解析数据帧
* @param $buffer
* @return null|string
*/
private function _decode($buffer)
{
$len = $masks = $data = $decoded = null;
$len = ord($buffer[1]) & 127;
if ($len === 126) {
$masks = substr($buffer, 4, 4);
$data = substr($buffer, 8);
} else if ($len === 127) {
$masks = substr($buffer, 10, 4);
$data = substr($buffer, 14);
} else {
$masks = substr($buffer, 2, 4);
$data = substr($buffer, 6);
}
for ($index = 0; $index < strlen($data); $index++) {
$decoded .= $data[$index] ^ $masks[$index % 4];
}
return $decoded;
} /**
* 处理返回帧
* @param $buffer
* @return string
*/
private function _frame($buffer)
{
$len = strlen($buffer);
if ($len <= 125) {
return "\x81" . chr($len) . $buffer;
} else if ($len <= 65535) {
return "\x81" . chr(126) . pack("n", $len) . $buffer;
} else {
return "\x81" . char(127) . pack("xxxxN", $len) . $buffer;
}
}
} $sc = new SocketServer();
$sc->run('127.0.0.1', 2046);
3.客户端代码
注:直接复制到html,和上面的php文件在同一文件夹即可,特别注意onmessage解析的两层 parse
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript"type="text/javascript">
websocket = new WebSocket('ws://127.0.0.1:2046/');
websocket.onopen = function(evt) {
console.log('connect');
websocket.send('{"data":"您好,世界!"}');
};
websocket.onclose = function(evt) {
console.log('onclose');
console.log(evt);
};
websocket.onmessage = function(evt) {
console.log('onmessage');
if (evt.data) {
console.log(JSON.parse(JSON.parse(evt.data)));
}
};
websocket.onerror = function(evt) {
console.log('onerror');
console.log(evt);
};
function sendMsg(){
var sendData = { 'data': document.getElementById('name').value};
websocket.send(JSON.stringify(sendData));
}
</script>
</head>
<body>
<h2>WebSocket Test</h2>
<input type="text" name="name" id="name" />
<a href="javascript:;" onclick="sendMsg()">点击发送</a>
</body>
</html>
4.测试示例
PHP Socket服务器搭建和测试的更多相关文章
- kafka服务器搭建与测试
kafka服务器搭建 kafka自身集群信息的管理以及对producer和consumer的信息管理是通过zookeeper实现的,zookeepr是kafka不可分割的一部分,所以搭建zookeep ...
- 【转】 Ubuntu samba服务器搭建及测试--不错
原文网址:http://blog.csdn.net/longfeey/article/details/5937968 Ubuntu samba服务配置是很不错的文件应用很有学习价值, 这里我主要讲解U ...
- 通过线程监控socket服务器是否done机
现实中的socket可能会因为各种原因done机,但这么重要的服务器怎么能允许这种事情发生?这次我们就来通过一个线程去监控socket服务器,如果done机重新将其启动. 下面是监控项目和socket ...
- Socket实现仿QQ聊天(可部署于广域网)附源码(2)-服务器搭建
1.前言 这是本系列的第二篇文章,第一篇文章得到了很多朋友们的支持,在这里表示非常的感谢.对于这一系列文章需要补充的是这只是一篇入门级别的Socket通信文章,对于专业人员来说完全可以跳过.本文只介绍 ...
- Okra框架(二) 搭建Socket服务器
本文将介绍使用Okra框架帮助开发者快速搭建高性能应用程序Socket服务端. 博主接触的网络游戏(包含但不限于网页, 手机)的服务端通信使用的协议基本上就Socket,Http或是WebSocket ...
- Service系统服务(四):搭建单区域DNS服务器、特殊DNS解析、配置DNS子域授权、搭建并测试缓存DNS
一.搭建单区域DNS服务器 目标: 本例要求要求为DNS区域tedu.cn搭建一台DNS服务器,以便用户能通过域名的方式访问网站.测试阶段主要提供以下正向记录: svr7.tedu.cn ---> ...
- Nginx 服务器搭建
什么是Nginx ? Nginx与Apache IIS等软件一样,是一款服务器软件,为web站点提供服务 除此之外,Nginx 还是一款反向代理服务器,我们可以利用Nginx实现负载均衡 所谓负载均衡 ...
- Java游戏服务器搭建
一.前言 此游戏服务器架构是一个单服的形式,也就是说所有游戏逻辑在一个工程里,没有区分登陆服务器.战斗服务器.世界服务器等.此架构已成功应用在了多款页游服务器 .在此框架中没有实现相关业务逻辑,只有简 ...
- WebRTC 入门教程(二)| WebRTC信令控制与STUN/TURN服务器搭建
WebRTC 入门教程(二)| WebRTC信令控制与STUN/TURN服务器搭建 四月 4, 2019 作者:李超,音视频技术专家.本文首发于 RTC 开发者社区,欢迎在社区留言与作者交流. htt ...
随机推荐
- c# 的默认访问修饰符(转)
c# 的访问修饰符是private 还是 internal? 准确的说,不能一概而论. 类(class)或结构(struct)如果不是在其它类或结构中的话,它的访问类型要不就是internal, 要不 ...
- HBase Shell基本使用总结
创建一个表 hbase(main):002:0> create 'member', 'cf_tmp', 'address', 'info' 查看所有表清单 hbase(main):003:0&g ...
- c# 父类的引用指向子类的实例
在C#中关于父类对子类的引用大概有这么几种: 父类是接口,普通类,抽象类 public interface A { void a(); } public class B { public void b ...
- 在GitHub,他们是怎么玩的? (转)
文章来源: http://www.cocoachina.com/gamedev/misc/2014/0530/8616.html Github.com,现在是全世界程序员,尤其是开源爱好者的乐园. ...
- SublimeText插件autoprefixer : css 添加前续
/* 使用前 */ body { background: linear-gradient(to bottom, #DADADA, #000); } p a { -webkit-border-radiu ...
- cookie 的 写入,读取, 删除
页面跳转,cookie存储参数 1,设置cookie function setCookie(name,value) { var Days = 30; var exp = new Date(); exp ...
- 使用ionic cordova build android --release --prod命令打包报错解决方法
使用ionic cordova build android --release --prod命令打包报有如下错误及解决方法 只要把以下内容添加到build-extras.gradle或(build** ...
- Android打包混淆文件模板
# This is a configuration file for ProGuard. # http://proguard.sourceforge.net/index.html#manual/usa ...
- Apache Module mod_ssl
http://httpd.apache.org/docs/current/mod/mod_ssl.html Description: Strong cryptography using the Sec ...
- IOS 控制当前控制器支持哪些方向
#pragma mark - 实现这个方法来控制屏幕方向 /** * 控制当前控制器支持哪些方向 * 返回值是UIInterfaceOrientationMask* */ - (NSUInteger) ...