時候我們的項目中會用到即時通訊功能,比如電商系統中的客服聊天功能,還有在支付過程中,當用戶支付成功后,第三方支付服務會回調我們的回調接口,此時我們需要通知前端支付成功。最近發現RabbitMQ可以很方便的實現即時通訊功能,如果你沒有特殊的業務需求,甚至可以不寫后端代碼,今天給大家講講如何使用RabbitMQ來實現即時通訊!
SpringBoot實戰電商項目mall(40k+star)地址:https://github.com/macrozheng/mall
轉自:https://www.jianshu.com/p/e132261456b5
參考:go語言中文文檔:www.topgoer.com
MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸協議),是一種基于發布/訂閱(publish/subscribe)模式的輕量級通訊協議,該協議構建于TCP/IP協議上。MQTT最大優點在于,可以以極少的代碼和有限的帶寬,為連接遠程設備提供實時可靠的消息服務。
RabbitMQ啟用MQTT功能,需要先安裝然RabbitMQ然后再啟用MQTT插件。
rabbitmq-plugins enable rabbitmq_mqtt
我們可以使用MQTT客戶端來測試MQTT的即時通訊功能,這里使用的是MQTTBox這個客戶端工具。
既然MQTTBox客戶端可以直接通過RabbitMQ實現即時通訊,那我們是不是直接使用前端技術也可以實現即時通訊?答案是肯定的!下面我們將通過html+javascript實現一個簡單的聊天功能,真正不寫一行后端代碼實現即時通訊!
rabbitmq-plugins enable rabbitmq_web_mqtt
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<label>目標Topic:<input id="targetTopicInput" type="text"></label><br>
<label>發送消息:<input id="messageInput" type="text"></label><br>
<button onclick="sendMessage()">發送</button>
<button onclick="clearMessage()">清空</button>
<div id="messageDiv"></div>
</div>
</body>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
<script>
//RabbitMQ的web-mqtt連接地址
const url = 'ws://localhost:15675/ws';
//獲取訂閱的topic
const topic = getQueryString("topic");
//連接到消息隊列
let client = mqtt.connect(url);
client.on('connect', function () {
//連接成功后訂閱topic
client.subscribe(topic, function (err) {
if (!err) {
showMessage("訂閱topic:" + topic + "成功!");
}
});
});
//獲取訂閱topic中的消息
client.on('message', function (topic, message) {
showMessage("收到消息:" + message.toString());
});
//發送消息
function sendMessage() {
let targetTopic = document.getElementById("targetTopicInput").value;
let message = document.getElementById("messageInput").value;
//向目標topic中發送消息
client.publish(targetTopic, message);
showMessage("發送消息給" + targetTopic + "的消息:" + message);
}
//從URL中獲取參數
function getQueryString(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
let r = window.location.search.substr(1).match(reg);
if (r != null) {
return decodeURIComponent(r[2]);
}
return null;
}
//在消息列表中展示消息
function showMessage(message) {
let messageDiv = document.getElementById("messageDiv");
let messageEle = document.createElement("div");
messageEle.innerText = message;
messageDiv.appendChild(messageEle);
}
//清空消息列表
function clearMessage() {
let messageDiv = document.getElementById("messageDiv");
messageDiv.innerHTML = "";
}
</script>
</html>
沒有特殊業務需求的時候,前端可以直接和RabbitMQ對接實現即時通訊。但是有時候我們需要通過服務端去通知前端,此時就需要在應用中集成MQTT了,接下來我們來講講如何在SpringBoot應用中使用MQTT。
<!--Spring集成MQTT-->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
rabbitmq:
mqtt:
url: tcp://localhost:1883
username: guest
password: guest
defaultTopic: testTopic
/**
* MQTT相關配置
* Created by macro on 2020/9/15.
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Component
@ConfigurationProperties(prefix = "rabbitmq.mqtt")
public class MqttConfig {
/**
* RabbitMQ連接用戶名
*/
private String username;
/**
* RabbitMQ連接密碼
*/
private String password;
/**
* RabbitMQ的MQTT默認topic
*/
private String defaultTopic;
/**
* RabbitMQ的MQTT連接地址
*/
private String url;
}
/**
* MQTT消息訂閱者相關配置
* Created by macro on 2020/9/15.
*/
@Slf4j
@Configuration
public class MqttInboundConfig {
@Autowired
private MqttConfig mqttConfig;
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}
@Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(mqttConfig.getUrl(), "subscriberClient",
mqttConfig.getDefaultTopic());
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
//設置消息質量:0->至多一次;1->至少一次;2->只有一次
adapter.setQos(1);
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}
@Bean
@ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
//處理訂閱消息
log.info("handleMessage : {}",message.getPayload());
}
};
}
}
/**
* MQTT消息發布者相關配置
* Created by macro on 2020/9/15.
*/
@Configuration
public class MqttOutboundConfig {
@Autowired
private MqttConfig mqttConfig;
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[] { mqttConfig.getUrl()});
options.setUserName(mqttConfig.getUsername());
options.setPassword(mqttConfig.getPassword().toCharArray());
factory.setConnectionOptions(options);
return factory;
}
@Bean
@ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("publisherClient", mqttClientFactory());
messageHandler.setAsync(true);
messageHandler.setDefaultTopic(mqttConfig.getDefaultTopic());
return messageHandler;
}
@Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
}
/**
* MQTT網關,通過接口將數據傳遞到集成流
* Created by macro on 2020/9/15.
*/
@Component
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {
/**
* 發送消息到默認topic
*/
void sendToMqtt(String payload);
/**
* 發送消息到指定topic
*/
void sendToMqtt(String payload, @Header(MqttHeaders.TOPIC) String topic);
/**
* 發送消息到指定topic并設置QOS
*/
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
}
/**
* MQTT測試接口
* Created by macro on 2020/9/15.
*/
@Api(tags = "MqttController", description = "MQTT測試接口")
@RestController
@RequestMapping("/mqtt")
public class MqttController {
@Autowired
private MqttGateway mqttGateway;
@PostMapping("/sendToDefaultTopic")
@ApiOperation("向默認主題發送消息")
public CommonResult sendToDefaultTopic(String payload) {
mqttGateway.sendToMqtt(payload);
return CommonResult.success(null);
}
@PostMapping("/sendToTopic")
@ApiOperation("向指定主題發送消息")
public CommonResult sendToTopic(String payload, String topic) {
mqttGateway.sendToMqtt(payload, topic);
return CommonResult.success(null);
}
}
2020-09-17 14:29:01.689 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 來自網頁上的消息
2020-09-17 14:29:06.101 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 來自網頁上的消息
2020-09-17 14:29:07.384 INFO 11192 --- [ubscriberClient] c.m.mall.tiny.config.MqttInboundConfig : handleMessage : 來自網頁上的消息
消息中間件應用越來越廣泛,不僅可以實現可靠的異步通信,還可以實現即時通訊,掌握一個消息中間件還是很有必要的。如果沒有特殊業務需求,客戶端或者前端直接使用MQTT對接消息中間件即可實現即時通訊,有特殊需求的時候也可以使用SpringBoot集成MQTT的方式來實現,總之消息中間件是實現即時通訊的一個好選擇!
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-mqtt
用php+swoole+redis 簡單實現網頁即時聊天,需要瀏覽器支持html5的websocket,
websocket是不同于http的另外一種網絡通信協議,能夠進行雙向通信,基于此,可開發出各種實時通信產品,簡單做了個聊天demo,順便分享一下
效果圖如下:
環境:
參考文檔:
IP與端口:
服務器端代碼 websocket.php
<?php class Server { private $serv; private $conn = null; private static $fd = null; public function __construct() { $this->redis_connect(); $this->serv = new swoole_websocket_server("0.0.0.0", 9502); $this->serv->set(array( 'worker_num' => 8, 'daemonize' => false, 'max_request' => 10000, 'dispatch_mode' => 2, 'debug_mode' => 1 )); echo "start \n"; $this->serv->on('Open', array($this, 'onOpen')); $this->serv->on('Message', array($this, 'onMessage')); $this->serv->on('Close', array($this, 'onClose')); $this->serv->start(); } function onOpen($server, $req) { echo "connection open: {$req->fd} \n"; // $server->push($req->fd, json_encode(33)); } public function onMessage($server, $frame) { //echo "received data $frame->data \n"; //$server->push($frame->fd, json_encode(["hello", "world"])); $pData = json_decode($frame->data,true); $fd=$frame->fd; if(empty($pData)){ echo "received data null \n"; return; } echo "received fd=>{$fd} message: {$frame->data}\n"; $data = []; if (isset($pData['content'])) { $f_fd = $this->getFd($pData['fid']); //獲取綁定的fd $data = $this->add($pData['uid'], $pData['fid'], $pData['content']); //保存消息 $server->push($f_fd, json_encode($data)); //推送到接收者 $json_data=json_encode($data); echo "推送到接收者 fd=>{$f_fd} message: {$json_data}\n"; } else { $this->unBind($pData['uid']); //首次接入,清除綁定數據 if ($this->bind($pData['uid'], $fd)) { //綁定fd $data = $this->loadHistory($pData['uid'], $pData['fid']); //加載歷史記錄 } else { $data = array("content" => "無法綁定fd"); } } $json_data=json_encode($data); echo "推送到發送者 fd=>{$fd} message: {$json_data}\n"; $server->push($fd, json_encode($data)); //推送到發送者 } public function onClose($server, $fd) { //$this->unBind($fd); echo "connection close: {$fd}\n"; } /*******************/ /** * redis * @param string $host * @param string $port * @return bool */ function redis_connect($host='127.0.0.1',$port='6379') { $this->conn = new Redis(); try{ $this->conn->connect($host, $port); }catch (\Exception $e){ user_error(print_r($e)); } return true; } /** * 保存消息 * @param $uid 發送者uid * @param $fid 接收者uid * @param $content 內容 * @return array */ public function add($uid, $fid, $content) { $msg_data=[]; $msg_data['uid']=$uid; $msg_data['fid']=$fid; $msg_data['content']=$content; $msg_data['time']=time(); $key=K::KEY_MSG; $data=$this->conn->get($key); if(!empty($data)){ $data=json_decode($data,true); }else{ $data=[]; } $data[]=$msg_data; $this->conn->set($key,json_encode($data)); $return_msg[]=$msg_data; return $return_msg; } /** * 綁定FD * @param $uid * @param $fd * @return bool */ public function bind($uid, $fd) { $key=K::KEY_UID."{$uid}"; $ret=$this->conn->set($key,$fd); if(!$ret){ echo "bind fail \n"; return false; } return true; } /** * 獲取FD * @param $uid * @return mixed */ public function getFd($uid) { $key=K::KEY_UID."{$uid}"; $fd=$this->conn->get($key); return $fd; } /** * 清除綁定 * @param $uid * @return bool */ public function unBind($uid) { $key=K::KEY_UID."{$uid}"; $ret=$this->conn->delete($key); if(!$ret){ return false; } return true; } /** * 歷史記錄 * @param $uid * @param $fid * @param null $id * @return array */ public function loadHistory($uid, $fid) { $msg_data=[]; $key=K::KEY_MSG; $this->conn->delete($key); $data=$this->conn->get($key); if($data){ echo $data; $json_data=json_decode($data,true); foreach ($json_data as $k=>$info){ if(($info['uid']==$uid&&$info['fid']==$fid)||($info['uid']==$fid&&$info['fid']==$uid)){ $msg_data[] = $info; } } } return $msg_data; } } //Key 定義 class K{ const KEY_MSG = 'msg_data'; const KEY_FD = 'fd_data'; const KEY_UID = 'uid'; } //啟動服務器 $server = new Server();
客戶端代碼 chat.html
<!DOCTYPE html> <html lang="en"> <html> <head> <title>CHAT A</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="jquery.min.js"></script> <script src="jquery.json.min.js"></script> <style type="text/css"> .talk_con{ width:600px; height:500px; border:1px solid #666; margin:50px auto 0; background:#f9f9f9; } .talk_show{ width:580px; height:420px; border:1px solid #666; background:#fff; margin:10px auto 0; overflow:auto; } .talk_input{ width:580px; margin:10px auto 0; } .whotalk{ width:80px; height:30px; float:left; outline:none; } .talk_word{ width:420px; height:26px; padding:0px; float:left; margin-left:10px; outline:none; text-indent:10px; } .talk_sub{ width:56px; height:30px; float:left; margin-left:10px; } .close{ width:56px; height:30px; float:left; margin-left:10px; } .atalk{ margin:10px; } .atalk span{ display:inline-block; background:#0181cc; border-radius:10px; color:#fff; padding:5px 10px; } .btalk{ margin:10px; text-align:right; } .btalk span{ display:inline-block; background:#ef8201; border-radius:10px; color:#fff; padding:5px 10px; } </style> <script type="text/javascript"> var uid = 'A'; //發送者uid var fid = 'B'; //接收者uid var wsUrl = 'ws://192.168.1.100:9502'; var webSocket = new WebSocket(wsUrl); //創建Socket webSocket.onopen = function (event) { console.log('onOpen=' + event.data); //webSocket.send("hello webSocket"); initData(); //初始化數據,加載歷史記錄 }; //接收數據事件 webSocket.onmessage = function (event) { console.log('onMessage=' + event.data); loadData($.parseJSON(event.data)); //導入消息記錄,加載新的消息 } //關閉socket webSocket.onclose = function (event) { console.log('close'); }; //socket連接錯誤 webSocket.onerror = function (event) { console.log('error-data:' + event.data); } //======================================================== //向服務器發送數據 function sendMsg() { var pData = { content: document.getElementById('content').value, uid: uid, fid: fid, } if (pData.content == '') { alert("消息不能為空"); return; } webSocket.send($.toJSON(pData)); //發送消息 } function initData() { //var Who = document.getElementById("who").value; console.log('initData uid:' + uid + ' fid:'+fid); var pData = { uid: uid, fid: fid, } webSocket.send($.toJSON(pData)); //獲取消息記錄,綁定fd var html = '<div class="atalk"><span id="asay">' + 'WebSocket連接成功' + '</div>'; $("#words").append(html); } function loadData(data) { for (var i = 0; i < data.length; i++) { if(data[i].uid=='A'){ var html = '<div class="atalk"><span id="asay">' + data[i].uid + '說: ' + data[i].content + '</div>'; }else{ var html = '<div class="btalk"><span id="asay">' + data[i].uid + '說: ' + data[i].content + '</div>'; } $("#words").append(html); } } //關閉連接 function closeWebSocket() { console.log('close'); webSocket.close(); var html = '<div class="atalk"><span id="asay">' + '已和服務器斷開連接' + '</div>'; $("#words").append(html); } </script> </head> <body> <div class="talk_con"> <div class="talk_show" id="words"> <!--<div class="atalk"><span id="asay">A說:吃飯了嗎?</span></div>--> <!--<div class="btalk"><span id="bsay">B說:還沒呢,你呢?</span></div>--> </div> <div class="talk_input"> <!--<select class="whotalk" id="who">--> <!--<option value="A" selected="selected">A說:</option>--> <!--<option value="B">B說:</option>--> <!--</select>--> <button class="close" onclick="closeWebSocket()">斷開</button> <input type="text" class="talk_word" id="content"> <input type="button" onclick="sendMsg()" value="發送" class="talk_sub" id="talksub"> </div> </div> </body> </html>
文件詳情:
使用方法:
安裝完php、redis和swoole擴展之后,直接執行:
并可以觀察下輸出,看看websocket服務器是否正常
最近想做一個Web版的即時聊天為后面開發的各項功能做輔助,就需要瀏覽器與服務器能夠實時通訊。而WebSocket這種雙向通信協議,就很合適用來實現這種需求。
本篇文章主要解決C#如何實現WebSocket服務端和Javascript客戶端基于wss協議的安全通信問題。
本文代碼已開源至Github:https://github.com/hxsfx/WebSocketServerTest
1、HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link href="Content/index.css" rel="stylesheet" />
</head>
<body>
<div id="ChatContainer">
<div class="tip "></div>
<div class="msgList"></div>
<div class="msgInput">
<textarea id="SendMsgContent"></textarea>
<button id="SendMsgButton">發送</button>
</div>
</div>
<script src="Scripts/index.js"></script>
</body>
</html>
2、JavaScript
window.onload = function () {
var TipElement = document.querySelector("#ChatContainer > div.tip");
var MsgListElement = document.querySelector("#ChatContainer > div.msgList");
var SendMsgContentElement = document.getElementById("SendMsgContent");
var SendMsgButton = document.getElementById("SendMsgButton");
window.wss = new WebSocket("wss://xxx.hxsfx.com:xxx");
//監聽消息狀態
wss.onmessage = function (e) {
var dataJson = JSON.parse(e.data);
loadData(dataJson.nickName, dataJson.msg, dataJson.date, dataJson.time, true);
}
//監聽鏈接狀態
wss.onopen = function () {
if (TipElement.className.indexOf("conn") < 0) {
TipElement.className = TipElement.className + " conn";
}
if (TipElement.className.indexOf("disConn") >= 0) {
TipElement.className = TipElement.className.replace("disConn", "");
}
}
//監聽關閉狀態
wss.onclose = function () {
if (TipElement.className.indexOf("conn") >= 0) {
TipElement.className = TipElement.className.replace("conn", "");
}
if (TipElement.className.indexOf("disConn") < 0) {
TipElement.className = TipElement.className + " disConn";
}
}
//監控輸入框回車鍵(直接發送輸入內容)
SendMsgContentElement.onkeydown = function () {
if (event.keyCode == 13 && SendMsgContentElement.value.trim() != "") {
if (SendMsgContentElement.value.trim() != "") {
SendMsgButton.click();
event.returnValue = false;
} else {
SendMsgContentElement.value = "";
}
}
}
//發送按鈕點擊事件
SendMsgButton.onclick = function () {
var msgDataJson = {
msg: SendMsgContentElement.value,
};
SendMsgContentElement.value = "";
var today = new Date();
var date = today.getFullYear() + "年" + (today.getMonth() + 1) + "月" + today.getDate() + "日";
var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
loadData("自己", msgDataJson.msg, date, time, false);
let msgDataJsonStr = JSON.stringify(msgDataJson);
wss.send(msgDataJsonStr);
}
//把數據加載到對話框中
function loadData(nickName, msg, date, time, isOther) {
let msgItemElement = document.createElement('div');
if (isOther) {
msgItemElement.className = "msgItem other";
} else {
msgItemElement.className = "msgItem self";
}
let chatHeadElement = document.createElement('div');
chatHeadElement.className = "chatHead";
chatHeadElement.innerHTML = "<svg viewBox=\"0 0 1024 1024\"><path d=\"M956.696128 512.75827c0 245.270123-199.054545 444.137403-444.615287 444.137403-245.538229 0-444.522166-198.868303-444.522166-444.137403 0-188.264804 117.181863-349.108073 282.675034-413.747255 50.002834-20.171412 104.631012-31.311123 161.858388-31.311123 57.297984 0 111.87909 11.128455 161.928996 31.311123C839.504032 163.650197 956.696128 324.494489 956.696128 512.75827L956.696128 512.75827M341.214289 419.091984c0 74.846662 38.349423 139.64855 94.097098 171.367973 23.119557 13.155624 49.151443 20.742417 76.769454 20.742417 26.64894 0 51.773154-7.096628 74.286913-19.355837 57.06467-31.113625 96.650247-96.707552 96.650247-172.742273 0-105.867166-76.664054-192.039781-170.936137-192.039781C417.867086 227.053226 341.214289 313.226864 341.214289 419.091984L341.214289 419.091984M513.886977 928.114163c129.883139 0 245.746984-59.732429 321.688583-153.211451-8.971325-73.739445-80.824817-136.51314-182.517917-167.825286-38.407752 34.55091-87.478354 55.340399-140.989081 55.340399-54.698786 0-104.770182-21.907962-143.55144-57.96211-98.921987 28.234041-171.379229 85.823668-188.368158 154.831344C255.507278 861.657588 376.965537 928.114163 513.886977 928.114163L513.886977 928.114163M513.886977 928.114163 513.886977 928.114163z\"></path></svg>";
let msgMainElement = document.createElement('div');
msgMainElement.className = "msgMain";
let nickNameElement = document.createElement('div');
nickNameElement.className = "nickName";
nickNameElement.innerText = nickName;
let msgElement = document.createElement('div');
msgElement.className = "msg";
msgElement.innerText = msg;
let timeElement = document.createElement('div');
timeElement.className = "time";
let time_date_Element = document.createElement('span');
time_date_Element.innerText = date;
let time_time_Element = document.createElement('span');
time_time_Element.innerText = time;
timeElement.append(time_date_Element);
timeElement.append(time_time_Element);
msgMainElement.append(nickNameElement);
msgMainElement.append(msgElement);
msgMainElement.append(timeElement);
msgItemElement.append(chatHeadElement);
msgItemElement.append(msgMainElement);
MsgListElement.append(msgItemElement);
MsgListElement.scrollTop = MsgListElement.scrollHeight - MsgListElement.clientHeight;
}
}
3、CSS
* {
padding: 0;
margin: 0;
}
html,
body {
font-size: 14px;
height: 100%;
}
body {
padding: 2%;
box-sizing: border-box;
background-color: #a3aebc;
}
#ChatContainer {
padding: 1% 25px 0 25px;
width: 80%;
max-width: 850px;
height: 100%;
background-color: #fefefe;
border-radius: 10px;
box-sizing: border-box;
margin: auto;
}
#ChatContainer .tip {
height: 30px;
line-height: 30px;
text-align: center;
align-items: center;
justify-content: center;
color: #999999;
}
#ChatContainer .tip:before {
content: "連接中";
}
#ChatContainer .tip.disConn {
color: red;
}
#ChatContainer .tip.disConn:before {
content: "× 連接已斷開";
}
#ChatContainer .tip.conn {
color: green;
}
#ChatContainer .tip.conn:before {
content: "√ 已連接";
}
#ChatContainer .msgList {
display: flex;
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
height: calc(100% - 100px);
}
#ChatContainer .msgList .msgItem {
display: flex;
margin: 5px;
}
#ChatContainer .msgList .msgItem .chatHead {
height: 36px;
width: 36px;
background-color: #ffffff;
border-radius: 100%;
}
#ChatContainer .msgList .msgItem .msgMain {
margin: 0 5px;
display: flex;
flex-direction: column;
}
#ChatContainer .msgList .msgItem .msgMain .nickName {
color: #666666;
}
#ChatContainer .msgList .msgItem .msgMain .msg {
padding: 10px;
line-height: 30px;
color: #333333;
}
#ChatContainer .msgList .msgItem .msgMain .time {
color: #999999;
font-size: 9px;
}
#ChatContainer .msgList .msgItem .msgMain .time span:first-child {
margin-right: 3px;
}
#ChatContainer .msgList .self {
flex-direction: row-reverse;
}
#ChatContainer .msgList .self .nickName {
text-align: right;
}
#ChatContainer .msgList .self .msg {
border-radius: 10px 0 10px 10px;
background-color: #d6e5f6;
}
#ChatContainer .msgList .self .time {
text-align: right;
}
#ChatContainer .msgList .other .msg {
border-radius: 0 10px 10px 10px;
background-color: #e8eaed;
}
#ChatContainer .msgInput {
margin: 15px 0;
display: flex;
}
#ChatContainer .msgInput textarea {
font-size: 16px;
padding: 0 5px;
width: 80%;
box-sizing: border-box;
height: 40px;
line-height: 40px;
overflow: hidden;
color: #333333;
border-radius: 10px 0 0 10px;
border: none;
outline: none;
border: 1px solid #eee;
resize: none;
}
#ChatContainer .msgInput button {
width: 20%;
text-align: center;
height: 40px;
line-height: 40px;
color: #fefefe;
background-color: #2a6bf2;
border-radius: 0 10px 10px 0;
border: 1px solid #2a6bf2;
}
創建控制臺程序(通過cmd命令調用,可修改源碼為直接運行使用),然后進入Gnet安裝fleck,其中的主要代碼如下(完整源碼移步github獲取):
//組合監聽地址
var loaction = webSocketProtocol + "://" + ListenIP + ":" + ListenPort;
var webSocketServer = new WebSocketServer(loaction);
if (loaction.StartsWith("wss://"))
{
webSocketServer.Certificate = new X509Certificate2(pfxFilePath, pfxPassword
, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
);
webSocketServer.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12;
}//當為安全鏈接時,將證書信息寫入鏈接
//開始偵聽
webSocketServer.Start(socket =>
{
var socketConnectionInfo = socket.ConnectionInfo;
var clientId = socketConnectionInfo.ClientIpAddress + ":" + socketConnectionInfo.ClientPort;
socket.OnOpen = () =>
{
if (!ip_scoket_Dic.ContainsKey(clientId))
{
ip_scoket_Dic.Add(clientId, socket);
}
Console.WriteLine(CustomSend("服務端", $"[{clientId}]加入"));
};
socket.OnClose = () =>
{
if (ip_scoket_Dic.ContainsKey(clientId))
{
ip_scoket_Dic.Remove(clientId);
}
Console.WriteLine(CustomSend("服務端", $"[{clientId}]離開"));
};
socket.OnMessage = message =>
{
//將發送過來的json字符串進行解析
var msgModel = JsonConvert.DeserializeObject<MsgModel>(message);
Console.WriteLine(CustomSend(clientId, msgModel.msg, clientId));
};
});
//出錯后進行重啟
webSocketServer.RestartAfterListenError = true;
Console.WriteLine("【開始監聽】" + loaction);
//服務端發送消息給客戶端
do
{
Console.WriteLine(CustomSend("服務端", Console.ReadLine()));
} while (true);
問題:WebSocket connection to 'wss://xxx.xxx.xxx.xxx:xxxx/' failed:
解決方法:要建立WSS安全通道,必須要先申請域名SSL證書,同時在防火墻中開放指定端口,以及前端WSS請求域名要跟SSL證書域名相同。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。