基于Swoole框架的WebSocket的简易在线聊天

一、背景:学习+研究

技术栈:Swoole + Linux + jQuery + HTML + CSS

Swoole是高性能的PHP协程框架,适用于高并发、高性能要求的领域,为PHP性能的提升提供了独一无二的可能性。

WebSocket是B/S或C/S网络结构模式下实现双方实时通信的协议,是一种在单个TCP连接上进行全双工通信的协议。Swoole\Websocket\Server 继承自 Swoole\Http\Server,是实现了WebSocket协议的服务器。

只有WebSocket客户端才能与WebSocket服务器通信。

二、类说明

WebScoket 服务器:

Swoole\WebSocket\Server

方法:

onopen() 监听WebSocket连接打开事件

onmessage() 监听WebSocket消息事件

onclose() 监听WebSocket连接关闭事件

push() 推送消息到已连接的客户端

isEstablished() 判断WebSocket连接是否正确

代码片段解析:

1、使用 FILE 作为存储层实现数据存储,当然还可以选择Redis等服务,也可以用Swoole\Table等实现,这里只为学习 WebSocket。

2、发送消息的时候,每个消息都定义一个类型 type,便于客户端解析数据做业务逻辑。

type 类型有:

    USER_IN  用户登录

    USER_OUT 用户离开

    USER_MSG 用户发送的消息

3、$ws->isEstablished 是为了判断连接是否有效,保证推送消息能够成功。

if ($ws->isEstablished($fd)) {
     $ws->push($fd, json_encode($data));
}

4、服务器接收到消息时,服务器会轮询把消息推送给所有用户(包括消息发送者),所以要区分一下消息是否是自己发送的。

foreach ($ws->connections as $item_fd) {
	if($item_fd != $frame->fd){
		$data = [
			//'num' => $num,
			'msg' => $frame->data,
			'type' => 'USER_MSG',
			'from_fd' => $frame->fd
		];
		
		// 判断websocket连接是否正确,否则会push失败
		if ($ws->isEstablished($item_fd)) {
			$ws->push($item_fd, json_encode($data));
		}
	}else{
		$data = [
			//'num' => $num,
			'msg' => $frame->data,
			'type' => 'USER_MSG',
			'from_fd' => $frame->fd
		];
		$ws->push($frame->fd, json_encode($data));
	}
}

程序源码:

服务端:PHP代码:ws_chat_server.php

<?php
defined('SWOOLE_SERVER') OR define('SWOOLE_SERVER','127.0.0.1');

// 面向过程编程
// 使用文件缓存  获取用户在线数
function getOnlineUserNum(){
	$data = file_get_contents('./chat/user_num.txt');
	return $data;
}
// 使用文件缓存 增加用户在线数
function setIncOnlineUserNum($type = null){
	if($type == 'init'){
		$num = 0;
	}else{
		$num = getOnlineUserNum() + 1;
	}
	file_put_contents('./chat/user_num.txt',$num);
	return $num;
}
// 使用文件缓存 减少用户在线数
function setDecOnlineUserNum($type = null){
	if($type == 'init'){
		$num = 0;
	}else{
		$num = getOnlineUserNum() - 1;
	}
	file_put_contents('./chat/user_num.txt',$num);
	return $num;
}



/**
 * WebSocket 服务器
 */
setIncOnlineUserNum('init');
 
//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new Swoole\WebSocket\Server(SWOOLE_SERVER, 9504);

//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
	echo 'WS-'.$request->fd . ' connected. '.PHP_EOL;
	
	$num = setIncOnlineUserNum();
	
	foreach ($ws->connections as $fd) {
        // 判断websocket连接是否正确,否则会push失败
        if($request->fd == $fd){
			$data = [
				'num' => $num,
				'msg' => $request->fd.' 进入了聊天室',
				'fd' => $request->fd,
				'type' => 'USER_IN'
			];
			$ws->push($request->fd, json_encode($data));
		}else{
			$data = [
				'num' => $num,
				'msg' => $request->fd.' 进入了聊天室',
				'type' => 'USER_IN'
			];
			if ($ws->isEstablished($fd)) {
				$ws->push($fd, json_encode($data));
			}
		}
    }
});

//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
    echo "Receive Message: {$frame->data}\n";
	
	foreach ($ws->connections as $item_fd) {
		if($item_fd != $frame->fd){
			$data = [
				//'num' => $num,
				'msg' => $frame->data,
				'type' => 'USER_MSG',
				'from_fd' => $frame->fd
			];
			
			// 判断websocket连接是否正确,否则会push失败
			if ($ws->isEstablished($item_fd)) {
				$ws->push($item_fd, json_encode($data));
			}
		}else{
			$data = [
				//'num' => $num,
				'msg' => $frame->data,
				'type' => 'USER_MSG',
				'from_fd' => $frame->fd
			];
			$ws->push($frame->fd, json_encode($data));
		}
    }
	
});

//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
    echo "WS-{$fd} is closed.".PHP_EOL;
	
	$num = setDecOnlineUserNum();
	$data = [
		'num' => $num,
		'msg' => $fd.' 离开了聊天室',
		'type' => 'USER_OUT'
	];
	
	foreach ($ws->connections as $item_fd) {
		// 推送给除自己之外的所有人
		if($item_fd != $fd){
			// 判断websocket连接是否正确,否则会push失败
			if ($ws->isEstablished($item_fd)) {
				$ws->push($item_fd, json_encode($data));
			}
		}
    }
	
	echo "当前在线人员有:".$num . PHP_EOL;
	
});

$ws->start();

客户端:HTML代码:ws_chat_server_test.php

<html>
	<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<body>
<style>
	.message-body{
		border:1px solid #ccc;
		width:300px;
		height:450px;
	}
	
	.msg-server{
		background-color:blue;
		border:1px solid #ccc;
		height:23px;
		margin-top:0px;
		text-align: center;
		word-wrap:break-word;
	}
	.msg-user{
		color:black;
		background-color:#00FFCC;
		border:1px solid #ccc;
		margin-top:0px;
		text-align:left;
		word-wrap:break-word;
	}
	.msg-user-mine{
		color:black;
		background-color:#33CCFF;
		border:1px solid #ccc;
		margin-top:0px;
		text-align: right;
		word-wrap:break-word;
	}
	.msg-green{
		background-color:#DDFF77;
		border:1px solid #ccc;
		height:23px;
		margin-top:0px;
		text-align: center;
	}
	.msg-exit{
		background-color:#DDDDDD;
		border:1px solid #ccc;
		height:23px;
		margin-top:0px;
		text-align: center;
	}
	
	.message-send-input{
		width:240px;
		height:50px;
	}
	
	.message-send-button{
		background-color:#0000FF;
		color:white;
		border:2px solid;
		border-radius:25px;
		width:60px;
		height:50px;
	}

</style>

当前在线人数:<span id="onlineUser">0</span>人&nbsp;&nbsp;
客户端FD:<span id="onlineUserFD"></span>

<div id="message-body" class="message-body">

</div>

<div>
	<div style="float:left;">
		<textarea class="message-send-input" id="msg" name="value">

		</textarea>
	</div>
	
	<div style="float:left;">
		<input class="message-send-button" type="button" id="sendBtn" value="发送">
	</div>
</div>

<div id="show-logs">

</div>
</body>

<script lang="javascript">
    var wsServer = 'ws://127.0.0.1:9504';
    var websocket = new WebSocket(wsServer);
    websocket.onopen = function (evt) {// 建立连接
        showServerInfoSuccess("连接成功.");
    };

    websocket.onclose = function (evt) {// 关闭连接
        showServerInfoFailured("关闭连接");
    };

    websocket.onmessage = function (evt) {// 接收服务器推送的消息
        //showInfo(evt.data);
	
		// 进入或退出
		if(JSON.parse(evt.data).type == 'USER_IN'){
			if(typeof JSON.parse(evt.data).fd !== 'undefined'){
				$("#onlineUserFD").html(JSON.parse(evt.data).fd);
			}
			
			$("#onlineUser").html(JSON.parse(evt.data).num);
			showServerInfoSuccess(JSON.parse(evt.data).msg);
		}

		if(JSON.parse(evt.data).type == 'USER_OUT' ){
			$("#onlineUser").html(JSON.parse(evt.data).num);
			showServerInfoFailured(JSON.parse(evt.data).msg);
		}
		
		if(JSON.parse(evt.data).type == 'USER_MSG'){			
			if(JSON.parse(evt.data).from_fd == $("#onlineUserFD").html()){
				var msg = JSON.parse(evt.data).msg 
							+ ' 说:我';
				
				showUserMessageMine(msg);
			}else{
				var msg = JSON.parse(evt.data).from_fd 
							+ ' 说:'
							+ JSON.parse(evt.data).msg;
				
				showUserMessage(msg);
			}
		}
    };

    websocket.onerror = function (evt, e) {
        showInfo('发生错误: ' + evt.data);
    };

    var msg;
    var sendBtn = document.getElementById('sendBtn');
    sendBtn.onclick = function(){
        if (websocket.readyState === 1) {
            msg = $('#msg').val();
            websocket.send(msg);// 发送消息到服务器
        } else {
            alert('WebSocket 连接失败');
        }
    };


	function showInfo(msg){
        $("#show-logs").append("<div class='msg-logs'>"+msg+"</div>");
	}
	function showUserMessage(msg){
        $("#message-body").append("<div class='msg-user'>"+msg+"</div>");
	}
	function showUserMessageMine(msg){
        $("#message-body").append("<div class='msg-user-mine'>"+msg+"</div>");
	}
	function showServerMsg(msg){
        $("#message-body").append("<div class='msg-server'>服务器消息:"+msg+"</div>");
	}
	function showServerInfoSuccess(msg){
        $("#message-body").append("<div class='msg-green'>服务器消息:"+msg+"</div>");
	}
	function showServerInfoFailured(msg){
        $("#message-body").append("<div class='msg-exit'>服务器消息:"+msg+"</div>");
	}

</script>

</html>

效果图:

用户登录聊天室,自动通知到其他所有用户。

用户离开聊天室,也会通知剩下还在线的所有用户。

用户发送消息,推送到其他所有在线用户。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值