首页 > 后端开发 > PHP问题 > php怎么实现聊天室功能

php怎么实现聊天室功能

藏色散人
发布: 2023-03-11 18:20:02
原创
4241 人浏览过

php实现聊天室功能的方法:首先创建前端代码;然后在PHP后端文件中通过创建“private function initSocket(){...}”等方法实现聊天室功能即可。

php怎么实现聊天室功能

本文操作环境:windows7系统、PHP7.1版,DELL G3电脑

php怎么实现聊天室功能?

一、功能界面

9fe3b7bf3c38b74e59402c4dcaeaf04.png

1a6478a20108fb563e6ee71d77c054a.png

二、具体代码实现

1、前端代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

<!DOCTYPE html>

<html>

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>websocket聊天室</title>

    <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">

    <link rel="stylesheet" href="css/reset.min.css">

    <link rel="stylesheet" href="css/style.css">

    <style>

        .message img {

            float: left;

            width: 40px;

            height: 40px;

            margin-right: 12px;

            border-radius: 50%;

        }

        .you {

            margin-left: 60px;

            margin-top: -39px;

        }

        .me-header {

            float: right !important;

            margin-right: 0 !important;

        }

        .me {

            margin-right: 60px;

            margin-top: -39px;

        }

        .active-chat::-webkit-scrollbar, .left::-webkit-scrollbar {

            width: 2px;

        }

    </style>

</head>

<body>

<div>

    <div>

        <div>

            <div style="padding: 20px 29px;height: auto;">

                <div class="" style="font: 400 13.3333px Arial;font-weight: 400;">在线人数:<span id="numbers">0</span> 人

                </div>

            </div>

            <ul>

            </ul>

        </div>

        <div>

            <div><span>Tips: <span>PHP之websocket聊天室</span></span></div>

            <div class="chat active-chat" data-chat="person1"

                 style="height: auto;border-width: 0px;padding: 10px;height: 483px; padding: 10px;overflow-y: auto;scrollTop: 100px">

            </div>

            <div>

                <a href="javascript:;" class="write-link attach"></a>

                <input type="text" id="input-value" onkeydown="confirm(event)"/>

                <a href="javascript:;" class="write-link smiley"></a>

                <a href="javascript:;" class="write-link send" onclick="send()"></a>

            </div>

        </div>

    </div>

</div>

<script src="js/index.js"></script>

<script>

    var uname = prompt(&#39;请输入用户名&#39;, &#39;user&#39; + uuid(8, 16));

    var avatar = [&#39;bo-jackson.jpg&#39;, &#39;dog.png&#39;, &#39;drake.jpg&#39;, &#39;louis-ck.jpeg&#39;, &#39;michael-jordan.jpg&#39;, &#39;thomas.jpg&#39;];

    if (avatar[Math.round(Math.random() * 10)]) {

        var headerimg = "img/" + avatar[Math.round(Math.random() * 10)];

    } else {

        var headerimg = "img/" + avatar[0];

    }

    // 创建一个 websocket 连接

    var ws = new WebSocket("ws://127.0.0.1:1238");

    // websocket 创建成功事件

    ws.onopen = function () {

        var data = "系统消息:建立连接成功";

        console.log(data);

    };

    // websocket 接收到消息事件

    ws.onmessage = function (e) {

        var data = JSON.parse(e.data);

        switch (data.type) {

            case &#39;handShake&#39;:

                console.log(data.msg);

                //发送用户登录

                var user_info = {&#39;type&#39;: &#39;login&#39;, &#39;msg&#39;: uname, &#39;headerimg&#39;: headerimg};

                sendMsg(user_info);

                break;

            case &#39;login&#39;:

                userList(data.user_list);

                systemMessage(&#39;系统消息: &#39; + data.msg + &#39; 已上线&#39;);

                break;

            case &#39;logout&#39;:

                userList(data.user_list);

                if (data.msg.length > 0) {

                    systemMessage(&#39;系统消息: &#39; + data.msg + &#39; 已下线&#39;);

                }

                break;

            case &#39;user&#39;:

                messageList(data);

                break;

            case &#39;system&#39;:

                systemMessage();

                break;

        }

    }

    // websocket 错误事件

    ws.onerror = function () {

        var data = "系统消息 : 出错了,请退出重试.";

        alert(data);

    };

    function confirm(event) {

        var key_num = event.keyCode;

        if (13 == key_num) {

            send();

        } else {

            return false;

        }

    }

    function systemMessage(msg) {

        var html = `<div>

            <span>` + msg + `</span>

        </div>`;

        var active_chat = document.querySelector(&#39;div.active-chat&#39;);

        var oldHtml = active_chat.innerHTML;

        active_chat.innerHTML = oldHtml + html;

        active_chat.scrollTop = active_chat.scrollHeight;

    }

    function send() {

        var msg = document.querySelector("input#input-value").value;

        var reg = new RegExp("\r\n", "g");

        msg = msg.replace(reg, "");

        sendMsg({type: "user", msg: msg});

        document.querySelector("input#input-value").value = "";

    }

    function sendMsg(msg) {

        var data = JSON.stringify(msg);

        ws.send(data);

    }

    function userList(user) {

        var html = &#39;&#39;;

        for (var i = 0; i < user.length; i++) {

            html += `<li data-chat="person1">

                    <img src="` + user[i].headerimg + `" alt=""/>

                    <span>` + user[i].username + `</span>

                    <span>` + user[i].login_time + `</span>

                    <span style="color: green;font-size: 7px;">在线</span>

                </li>`;

        }

        document.querySelector(&#39;ul.people&#39;).innerHTML = html;

        document.querySelector(&#39;span#numbers&#39;).innerHTML = user.length;

    }

    function messageList(data) {

        if (data.from == uname) {

            var html = `<div>

                    <img src="` + data.headerimg + `" alt=""/>

                    <div class="bubble me">` + data.msg + `</div>

                </div>`;

        } else {

            var html = `<div>

                    <img src="` + data.headerimg + `" alt=""/>

                    <div class="bubble you">` + data.msg + `</div>

                </div>`;

        }

        var active_chat = document.querySelector(&#39;div.active-chat&#39;);

        var oldHtml = active_chat.innerHTML;

        active_chat.innerHTML = oldHtml + html;

        active_chat.scrollTop = active_chat.scrollHeight;

    }

    /**

     * 生产一个全局唯一ID作为用户名的默认值;

     *

     * @param len

     * @param radix

     * @returns {string}

     */

    function uuid(len, radix) {

        var chars = &#39;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz&#39;.split(&#39;&#39;);

        var uuid = [], i;

        radix = radix || chars.length;

        if (len) {

            for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];

        } else {

            var r;

            uuid[8] = uuid[13] = uuid[18] = uuid[23] = &#39;-&#39;;

            uuid[14] = &#39;4&#39;;

            for (i = 0; i < 36; i++) {

                if (!uuid[i]) {

                    r = 0 | Math.random() * 16;

                    uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];

                }

            }

        }

        return uuid.join(&#39;&#39;);

    }

</script>

</body>

</html>

登录后复制

2、后端代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

<?php

/**

 * Created by PhpStorm.

 * User: 25754

 * Date: 2019/4/23

 * Time: 14:13

 */

class socketServer

{

    const LISTEN_SOCKET_NUM = 9;

    const LOG_PATH = "./log/";

    private $_ip = "127.0.0.1";

    private $_port = 1238;

    private $_socketPool = array();

    private $_master = null;

    public function __construct()

    {

        $this->initSocket();

    }

    private function initSocket()

    {

        try {

            //创建socket套接字

            $this->_master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

            // 设置IP和端口重用,在重启服务器后能重新使用此端口;

            socket_set_option($this->_master, SOL_SOCKET, SO_REUSEADDR, 1);

            //绑定地址与端口

            socket_bind($this->_master, $this->_ip, $this->_port);

            //listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接,其中的能存储的请求不明的socket数目。

            socket_listen($this->_master, self::LISTEN_SOCKET_NUM);

        } catch (Exception $e) {

            $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage()));

        }

        //将socket保存到socket池中

        $this->_socketPool[0] = array(&#39;resource&#39; => $this->_master);

        $pid = getmypid();

        $this->debug(array("server: {$this->_master} started,pid: {$pid}"));

        while (true) {

            try {

                $this->run();

            } catch (Exception $e) {

                $this->debug(array("code: " . $e->getCode() . ", message: " . $e->getMessage()));

            }

        }

    }

    private function run()

    {

        $write = $except = NULL;

        $sockets = array_column($this->_socketPool, &#39;resource&#39;);

        $read_num = socket_select($sockets, $write, $except, NULL);

        if (false === $read_num) {

            $this->debug(array(&#39;socket_select_error&#39;, $err_code = socket_last_error(), socket_strerror($err_code)));

            return;

        }

        foreach ($sockets as $socket) {

            if ($socket == $this->_master) {

                $client = socket_accept($this->_master);

                if ($client === false) {

                    $this->debug([&#39;socket_accept_error&#39;, $err_code = socket_last_error(), socket_strerror($err_code)]);

                    continue;

                }

                //连接

                $this->connection($client);

            } else {

                //接受数据

                $bytes = @socket_recv($socket, $buffer, 2048, 0);

                if ($bytes == 0) {

                    $recv_msg = $this->disconnection($socket);

                } else {

                    if ($this->_socketPool[(int)$socket][&#39;handShake&#39;] == false) {

                        $this->handShake($socket, $buffer);

                        continue;

                    } else {

                        $recv_msg = $this->parse($buffer);

                    }

                }

                $msg = $this->doEvents($socket, $recv_msg);

                echo($msg);

                socket_getpeername ( $socket  , $address ,$port );

                $this->debug(array(

                    &#39;send_success&#39;,

                    json_encode($recv_msg),

                    $address,

                    $port

                ));

                $this->broadcast($msg);

            }

        }

    }

    /**

     * 数据广播

     * @param $data

     */

    private function broadcast($data)

    {

        foreach ($this->_socketPool as $socket) {

            if ($socket[&#39;resource&#39;] == $this->_master) {

                continue;

            }

            socket_write($socket[&#39;resource&#39;], $data, strlen($data));

        }

    }

    /**

     * 业务处理

     * @param $socket

     * @param $recv_msg

     * @return string

     */

    private function doEvents($socket, $recv_msg)

    {

        $msg_type = $recv_msg[&#39;type&#39;];

        $msg_content = $recv_msg[&#39;msg&#39;];

        $response = [];

        switch ($msg_type) {

            case &#39;login&#39;:

                $this->_socketPool[(int)$socket][&#39;userInfo&#39;] = array("username" => $msg_content, &#39;headerimg&#39; => $recv_msg[&#39;headerimg&#39;], "login_time" => date("h:i"));

                // 取得最新的名字记录

                $user_list = array_column($this->_socketPool, &#39;userInfo&#39;);

                $response[&#39;type&#39;] = &#39;login&#39;;

                $response[&#39;msg&#39;] = $msg_content;

                $response[&#39;user_list&#39;] = $user_list;

                break;

            case &#39;logout&#39;:

                $user_list = array_column($this->_socketPool, &#39;userInfo&#39;);

                $response[&#39;type&#39;] = &#39;logout&#39;;

                $response[&#39;user_list&#39;] = $user_list;

                if ($nowUserSocket = @$this->_socketPool[(int)$socket]) {

                    $response[&#39;msg&#39;] = $nowUserSocket[&#39;userInfo&#39;][&#39;username&#39;];

                }else{

                    $response[&#39;msg&#39;] = &#39;&#39;;

                }

                break;

            case &#39;user&#39;:

                $userInfo = $this->_socketPool[(int)$socket][&#39;userInfo&#39;];

                $response[&#39;type&#39;] = &#39;user&#39;;

                $response[&#39;from&#39;] = $userInfo[&#39;username&#39;];

                $response[&#39;msg&#39;] = $msg_content;

                $response[&#39;headerimg&#39;] = $userInfo[&#39;headerimg&#39;];

                break;

        }

        return $this->frame(json_encode($response));

    }

    /**

     * socket握手

     * @param $socket

     * @param $buffer

     * @return bool

     */

    public function handShake($socket, $buffer)

    {

        $acceptKey = $this->encry($buffer);

        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .

            "Upgrade: websocket\r\n" .

            "Connection: Upgrade\r\n" .

            "Sec-WebSocket-Accept: " . $acceptKey . "\r\n\r\n";

        // 写入socket

        socket_write($socket, $upgrade, strlen($upgrade));

        // 标记握手已经成功,下次接受数据采用数据帧格式

        $this->_socketPool[(int)$socket][&#39;handShake&#39;] = true;

        socket_getpeername ( $socket  , $address ,$port );

        $this->debug(array(

            &#39;hand_shake_success&#39;,

            $socket,

            $address,

            $port

        ));

        //发送消息通知客户端握手成功

        $msg = array(&#39;type&#39; => &#39;handShake&#39;, &#39;msg&#39; => &#39;握手成功&#39;);

        $msg = $this->frame(json_encode($msg));

        socket_write($socket, $msg, strlen($msg));

        return true;

    }

    /**

     * 帧数据封装

     * @param $msg

     * @return string

     */

    private function frame($msg)

    {

        $frame = [];

        $frame[0] = &#39;81&#39;;

        $len = strlen($msg);

        if ($len < 126) {

            $frame[1] = $len < 16 ? &#39;0&#39; . dechex($len) : dechex($len);

        } else if ($len < 65025) {

            $s = dechex($len);

            $frame[1] = &#39;7e&#39; . str_repeat(&#39;0&#39;, 4 - strlen($s)) . $s;

        } else {

            $s = dechex($len);

            $frame[1] = &#39;7f&#39; . str_repeat(&#39;0&#39;, 16 - strlen($s)) . $s;

        }

        $data = &#39;&#39;;

        $l = strlen($msg);

        for ($i = 0; $i < $l; $i++) {

            $data .= dechex(ord($msg{$i}));

        }

        $frame[2] = $data;

        $data = implode(&#39;&#39;, $frame);

        return pack("H*", $data);

    }

    /**

     * 接受数据解析

     * @param $buffer

     * @return mixed

     */

    private function parse($buffer)

    {

        $decoded = &#39;&#39;;

        $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 json_decode($decoded, true);

    }

    // 提取 Sec-WebSocket-Key 信息

    private function getKey($req)

    {

        $key = null;

        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $req, $match)) {

            $key = $match[1];

        }

        return $key;

    }

    //加密 Sec-WebSocket-Key

    private function encry($req)

    {

        $key = $this->getKey($req);

        return base64_encode(sha1($key . &#39;258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#39;, true));

    }

    /**

     * 连接socket

     * @param $client

     */

    public function connection($client)

    {

        socket_getpeername ( $client  , $address ,$port );

        $info = array(

            &#39;resource&#39; => $client,

            &#39;userInfo&#39; => &#39;&#39;,

            &#39;handShake&#39; => false,

            &#39;ip&#39; => $address,

            &#39;port&#39; => $port,

        );

        $this->_socketPool[(int)$client] = $info;

        $this->debug(array_merge([&#39;socket_connect&#39;], $info));

    }

    /**

     * 断开连接

     * @param $socket

     * @return array

     */

    public function disconnection($socket)

    {

        $recv_msg = array(

            &#39;type&#39; => &#39;logout&#39;,

            &#39;msg&#39; => @$this->_socketPool[(int)$socket][&#39;username&#39;],

        );

        unset($this->_socketPool[(int)$socket]);

        return $recv_msg;

    }

    /**

     * 日志

     * @param array $info

     */

    private function debug(array $info)

    {

        $time = date(&#39;Y-m-d H:i:s&#39;);

        array_unshift($info, $time);

        $info = array_map(&#39;json_encode&#39;, $info);

        file_put_contents(self::LOG_PATH . &#39;websocket_debug.log&#39;, implode(&#39; | &#39;, $info) . "\r\n", FILE_APPEND);

    }

}

new socketServer();

登录后复制

推荐学习:《PHP视频教程

以上是php怎么实现聊天室功能的详细内容。更多信息请关注PHP中文网其他相关文章!

相关标签:
php
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板