ฟังก์ชั่นการกดข้อความที่เสร็จสมบูรณ์โดย WebSocket และ Java ใช้งานโดย Server TomCat7.0 บางสิ่งได้รับการพิจารณาด้วยตัวเองและฉันไม่รู้ว่าพวกเขาเหมาะสมหรือไม่เหมาะสม โปรดยกโทษให้ฉันและชี้ให้พวกเขา
กล่าวง่ายๆว่าลูกค้า A สามารถส่งข้อความถึงลูกค้า B ได้ แต่มีหลายสถานที่ที่สามารถขยายได้
ตัวอย่างเช่น
1. หากหลังจากเข้าร่วมฐานข้อมูลเมื่อ A ส่งข้อความลูกค้า B ไม่ได้ออนไลน์เซิร์ฟเวอร์จะเก็บข้อความไว้ในฐานข้อมูล หลังจากลูกค้า B ออนไลน์ข้อความจะถูกเรียกคืนและส่งไปยังลูกค้า B.
2. เซิร์ฟเวอร์ยังสามารถส่งข้อความไปยังไคลเอนต์ใด ๆ
ภาพหน้าจอของเอฟเฟกต์การทำงานของโปรแกรมมีดังนี้ (ทดสอบภายใต้ Chrome, Sogou และ Firefox): รหัสจะได้รับในตอนท้าย
ก่อนอื่นเราเปิดเบราว์เซอร์และแสดงเพื่อป้อนชื่อของคุณ ที่นี่ฉันเข้าสู่ SOAR
เมื่อเปิดเบราว์เซอร์ที่สองที่นี่ฉันเข้าสู่บิล
นี่คือถ้าฉันส่ง Hello Bill ฉันทะยานไปยัง Bill คลิกส่ง
คุณสามารถดูได้บนเบราว์เซอร์อื่น
websocket
1. WebSocket คืออะไร?
WebSocket เป็นเทคโนโลยีที่สร้างขึ้นเพื่อแก้ปัญหาการสื่อสารแบบเรียลไทม์ระหว่างลูกค้าและเซิร์ฟเวอร์ สาระสำคัญคือการสร้างการเชื่อมต่อ TCP สำหรับการแลกเปลี่ยนข้อมูลหลังจากการจับมือผ่านโปรโตคอล HTTP/HTTPS
หลังจากนั้นเซิร์ฟเวอร์และไคลเอนต์สื่อสารแบบเรียลไทม์ผ่านการเชื่อมต่อ TCP นี้
2. ข้อดีของ WebSocket
ในอดีตเราใช้เทคโนโลยีการผลักดันโดยใช้การสำรวจ ในช่วงเวลาที่มีลักษณะเฉพาะเบราว์เซอร์จะออกคำขอโดยอัตโนมัติและดึงข้อความของเซิร์ฟเวอร์กลับโดยอัตโนมัติ ในกรณีนี้เราต้องส่งคำขอไปยังเซิร์ฟเวอร์อย่างต่อเนื่อง อย่างไรก็ตามส่วนหัวของคำขอ HTTP นั้นยาวมากและข้อมูลที่มีอยู่ในนั้นอาจเป็นเพียงค่าเล็กน้อยซึ่งจะครอบครองแบนด์วิดธ์และทรัพยากรเซิร์ฟเวอร์จำนวนมาก มันจะใช้แบนด์วิดท์และทรัพยากรเซิร์ฟเวอร์จำนวนมาก
สิ่งที่ยิ่งใหญ่ที่สุดเกี่ยวกับ WebSocket API คือเซิร์ฟเวอร์และไคลเอนต์สามารถส่งข้อมูลซึ่งกันและกันได้ตลอดเวลาภายในช่วงเวลาที่กำหนด หลังจากสร้างการเชื่อมต่อเซิร์ฟเวอร์สามารถส่งข้อมูลไปยังไคลเอนต์ได้อย่างแข็งขัน
นอกจากนี้ข้อมูลส่วนหัวที่แลกเปลี่ยนระหว่างเซิร์ฟเวอร์และไคลเอนต์มีขนาดเล็กมาก
WebSocket ไม่ได้ จำกัด อยู่ที่การสื่อสารในโหมด AJAX (หรือ XHR) เนื่องจากเทคโนโลยี AJAX กำหนดให้ลูกค้าเริ่มต้นคำขอในขณะที่เซิร์ฟเวอร์และลูกค้า WebSocket สามารถผลักดันข้อมูลให้กันและกันได้
สำหรับการแนะนำรายละเอียดเกี่ยวกับข้อความ AJAX, Comet, WebSocket และ WebSocket คุณสามารถอ้างถึง http://www.shaoqun.com/a/54588.aspx การออกแบบเว็บ] Ajax, Comet และ Websocket
ถ้าฉันมีเวลาในอนาคตฉันจะเขียนมันออกมา
3. วิธีใช้ WebSocket
ลูกค้า
ในเบราว์เซอร์ที่รองรับ WebSocket หลังจากสร้างซ็อกเก็ต คุณสามารถตอบสนองต่อซ็อกเก็ตผ่านสี่เหตุการณ์ ได้แก่ Onopen, OnMessage และ OnClose นั่นคือ Onerror
ตัวอย่างง่ายๆ
var ws = ใหม่ websocket (ws: // localhost: 8080); ws.onopen = function () {console.log (เปิด); ws.send (hello);}; ws.onmessage = function (evt) {console.log (evt.data)}; ws.onclose = ฟังก์ชั่น (evt) {console.log (websocketclosed!);}; ws.onerror = function (evt) {console.log1.VAR WS = ใหม่ WebSocket (ws: // localhost: 8080);
ในการสมัครวัตถุ WebSocket พารามิเตอร์เป็นที่อยู่ของเซิร์ฟเวอร์ที่ต้องเชื่อมต่อ เช่นเดียวกับโปรโตคอล HTTP URL ของโปรโตคอล WebSocket เริ่มต้นด้วย WS: // และโปรโตคอล Secure WebSocket เริ่มต้นด้วย WSS: //
ws.send (สวัสดี);
ใช้เพื่อส่งข้อความไปยังเซิร์ฟเวอร์
2.ws.onopen = function () {console.log (เปิด)};
เมื่อสร้าง WebSocket สำเร็จเหตุการณ์ Onopen จะถูกเรียกใช้
3.ws.onMessage = ฟังก์ชั่น (evt) {console.log (evt.data)};
เมื่อไคลเอนต์ได้รับข้อความที่ส่งโดยเซิร์ฟเวอร์เหตุการณ์ onMessage จะถูกเรียกใช้ พารามิเตอร์ evt.data มีข้อมูลที่ส่งโดยเซิร์ฟเวอร์
4.ws.onclose = function (evt) {console.log (websocketclosed!); -
เมื่อไคลเอน
5.Ws.onerror = ฟังก์ชั่น (evt) {console.log (websocketerror!); -
หากการเชื่อมต่อการประมวลผลการรับและการส่งข้อมูลล้มเหลวเหตุการณ์ onerror จะถูกเรียกใช้
เราจะเห็นได้ว่าการดำเนินการทั้งหมดจะถูกกระตุ้นโดยเหตุการณ์เพื่อให้ UI จะไม่บล็อกทำให้ UI มีเวลาตอบสนองที่เร็วขึ้นและประสบการณ์การใช้งานที่ดีขึ้น
ด้านเซิร์ฟเวอร์
ทุกวันนี้มีซอฟต์แวร์เซิร์ฟเวอร์จำนวนมากที่รองรับ WebSocket เช่น node.js, Jetty, Tomcat ฯลฯ
ที่นี่ฉันใช้ Tomat 7.0 และ Eclipse4.2
Tomcat ใช้ WebSocket ก่อนเพื่อนำเข้าขวดที่เกี่ยวข้อง
คลาสที่เกี่ยวข้องกับ WebSocket ที่จัดทำโดย TOMCAT7 ทั้งหมดอยู่ในแพ็คเกจ org.apache.catalina.websocket (การใช้งาน Package org.apache.catalina.websocket มีอยู่ในไฟล์ catalina.jar
ที่นี่เราเพิ่งนำเข้า Tomcat ทั้งหมด
ใน Build Path-> กำหนดค่า Path-> Librarise-> เพิ่ม Library-> Server Runtime-> Apache Tomcat v7.0
ภาพ
นอกจากนี้นำเข้าแพ็คเกจต่อไปนี้
นำเข้า org.apache.catalina.websocket.messageinbound;
นำเข้า org.apache.catalina.websocket.streaminbound;
นำเข้า org.apache.catalina.websocket.wsoutbound;
นำเข้า org.apache.catalina.websocket.websocketServlet;
เราต้องการสองคลาส
อันแรกใช้เพื่อจัดการคำขอ WebSocket
อันที่สองใช้เพื่อจัดการงาน WebSocket แต่ละรายการ
หมวดหมู่แรก
SocketServer ระดับสาธารณะขยาย WebSocketServlet {
ส่วนตัวคงที่สุดท้าย Long SerialVersionUid = 1L;
-
@Override
ได้รับการป้องกัน streaminbound createWebsocketInbound (String arg0,
httpservletrequest arg1) {
// todo วิธีการที่สร้างขึ้นอัตโนมัติสตับ
ส่งคืน Chatwebsocket ใหม่ (ผู้ใช้);
-
-
servlet นี้สืบทอดมาจาก WebSocketServlet และใช้วิธี CreateWebSocketInbound วิธีนี้ส่งคืนอินสแตนซ์ของคลาสที่สอง
หมวดที่สอง
ChatWebsocket ระดับสาธารณะขยาย MessageInbound {
@Override
ได้รับการป้องกันโมฆะ ontextMessage (ข้อความ Charbuffer) พ่น IOException {
-
@Override
Void Onopen (WSoutbound ขาออก) {
-
@Override
เป็นโมฆะป้องกัน onClose (สถานะ int) {
-
@Override
เป็นโมฆะที่ได้รับการป้องกัน onbinaryMessage (bytebuffer arg0) โยน ioexception {
-
// ที่เหลือเป็นเพียงเล็กน้อย
-
เป็นโมฆะที่ได้รับการป้องกัน ontextMessage (ข้อความ Charbuffer) พ่น IOException {}
การตอบกลับข้อความ
เป็นโมฆะที่ได้รับการป้องกัน onbinaryMessage (bytebuffer arg0) โยน ioexception {}
การตอบกลับข้อความไบนารี
Void Onopen ที่ได้รับการป้องกัน (WSOutbound Outbound) {}
เหตุการณ์ที่เกิดจากการสร้างการเชื่อมต่อ
เป็นโมฆะที่ได้รับการป้องกัน onClose (สถานะ int) {}
เหตุการณ์ยิงเมื่อปิดการเชื่อมต่อ
4. รหัสโปรแกรม
ส่วน HTML
<! doctype html>
<html>
<head>
<meta charset = utf-8>
<script type = text/javascript src = js/jQuery.js> </script>
<script type = text/javascript src = js/socket.js> </script>
<tite> เอกสารชื่อ UNT </title>
</head>
<ภาษาสคริปต์ = JavaScript>
</script>
<body>
<table>
<tr>
<td> ข้อความ </td>
<td> <อินพุตประเภท = ข้อความข้อความ = ข้อความ> </td>
</tr>
<tr>
<td> ชื่อ </td>
<td> <อินพุตประเภท = ข้อความข้อความ = ชื่ออื่น ๆ > </td>
</tr>
<tr>
<td> <อินพุต ID = sendButton type = button value = ส่ง onClick = คลิก disabled = true>
</input> </td>
</tr>
</table>
<script>
</script>
</body>
</html>
JS Part (ไม่มีคำอธิบายเกี่ยวกับ jQuery Part)
ชื่อผู้ใช้ var = window.prompt (ป้อนชื่อของคุณ :);
document.write (ยินดีต้อนรับ <p id =/username/>+ชื่อผู้ใช้+</p>);
if (! window.websocket && window.mozwebsocket)
window.websocket = window.mozwebsocket;
if (! window.websocket)
การแจ้งเตือน (ไม่สนับสนุน);
var ws;
$ (เอกสาร) .ready (function () {
$ (#sendbutton) .attr (ปิดใช้งาน, เท็จ);
$ (#sendbutton). click (sendmessage);
StartWebSocket ();
-
ฟังก์ชั่น sendmessage ()
-
var otherName = $ (#otherName) .val ();
var msg = msg/t+ชื่อผู้ใช้+_+otherName+_+$ (#ข้อความ) .val ();
ส่ง (ผงชูรส);
-
ฟังก์ชั่นส่ง (ข้อมูล)
-
console.log (ส่ง:+ข้อมูล);
ws.send (ข้อมูล);
-
ฟังก์ชั่น startwebsocket ()
-
WS = ใหม่ webSocket (ws: // + location.host +/websocket/socketserver);
ws.onopen = function () {
console.log (เปิดประสบความสำเร็จ);
$ (#sendbutton) .attr (ปิดใช้งาน, เท็จ);
-
ws.onMessage = ฟังก์ชั่น (เหตุการณ์)
-
console.log (รับ:+event.data);
HandleData (Event.data);
-
ws.onclose = function (เหตุการณ์) {
console.log (ซ็อกเก็ตที่ได้รับแจ้งจากไคลเอนต์ปิดเหตุการณ์);
-
-
ฟังก์ชั่น HandleData (ข้อมูล)
-
var vals = data.split (/t);
var msgtype = vals [0];
สวิตช์ (msgtype)
-
ชื่อกรณี:
var msg = vals [1];
var mes = name+/t+msg+_+ชื่อผู้ใช้;
ส่ง (mes);
หยุดพัก;
CASE MSG:
var val2s = vals [1] .split (_);
var จาก = val2s [0];
ข้อความ var = val2s [2];
การแจ้งเตือน (จาก+:+ข้อความ);
หยุดพัก;
ค่าเริ่มต้น:
หยุดพัก;
-
-
ส่วน Java
นำเข้า java.io.ioException;
นำเข้า java.nio.bytebuffer;
นำเข้า java.nio.charbuffer;
นำเข้า Javax.servlet.http.httpservletRequest;
นำเข้า java.util.set;
นำเข้า java.util.concurrent.copyonwritearrayset;
นำเข้า org.apache.catalina.websocket.messageinbound;
นำเข้า org.apache.catalina.websocket.streaminbound;
นำเข้า org.apache.catalina.websocket.wsoutbound;
นำเข้า org.apache.catalina.websocket.websocketServlet;
SocketServer ระดับสาธารณะขยาย WebSocketServlet {
ส่วนตัวคงที่สุดท้าย Long SerialVersionUid = 1L;
ชุดสุดท้ายของสาธารณะ <HatWebSocket> ผู้ใช้ = ใหม่ CopyOnWriteArRaySet <HatWebSocket> ();
ผู้ใช้งานคงที่สาธารณะ = 1;
@Override
ได้รับการป้องกัน streaminbound createWebsocketInbound (String arg0,
httpservletrequest arg1) {
// todo วิธีการที่สร้างขึ้นอัตโนมัติสตับ
ส่งคืน Chatwebsocket ใหม่ (ผู้ใช้);
-
ChatWebsocket ระดับสาธารณะขยาย MessageInbound {
ชื่อผู้ใช้สตริงส่วนตัว;
ชุดส่วนตัว <HatWebSocket> ผู้ใช้ = ใหม่ CopyOnWriteArRaySet <HatWebSocket> () ;;
Public Chatwebsocket () {
-
Public ChatWebsocket (ตั้งค่า <chatwebsocket> ผู้ใช้) {
this.users = ผู้ใช้;
-
@Override
ได้รับการป้องกันโมฆะ ontextMessage (ข้อความ Charbuffer) พ่น IOException {
// สิ่งที่ประมวลผลที่นี่คือข้อมูลข้อความ
-
โมฆะสาธารณะ onMessage (ข้อมูลสตริง) {
String [] val1 = data.split (// t);
if (val1 [0] .Equals (ชื่อ))
-
String [] val2 = val1 [1] .split (_);
สำหรับ (chatwebsocket ผู้ใช้: ผู้ใช้) {
if (user.username.equals (val2 [0])) {
user.username = val2 [1];
-
-
-
อื่นถ้า (val1 [0] .equals (msg))
-
String [] val2 = val1 [1] .split (_);
สำหรับ (chatwebsocket ผู้ใช้: ผู้ใช้) {
if (user.username.equals (val2 [1])) {
พยายาม {
charbuffer temp = charbuffer.wrap (ข้อมูล);
user.getWsoutbound (). writeTextMessage (อุณหภูมิ);
} catch (ioexception e) {
// todo catch block ที่สร้างอัตโนมัติ
E.PrintStackTrace ();
-
-
-
-
อื่น
-
System.out.println (ข้อผิดพลาด);
-
-
@Override
Void Onopen (WSoutbound ขาออก) {
// this.connection = การเชื่อมต่อ;
this.username = # + string.valueof (ผู้ใช้);
ผู้ใช้ ++;
พยายาม {
String message = name + /t + this.username;
charbuffer buffer = charbuffer.wrap (ข้อความ);
this.getWsoutbound (). writeTextMessage (บัฟเฟอร์);
} catch (ioexception e) {
// todo catch block ที่สร้างอัตโนมัติ
E.PrintStackTrace ();
-
users.add (นี่);
-
@Override
เป็นโมฆะป้องกัน onClose (สถานะ int) {
users.remove (นี่);
-
@Override
เป็นโมฆะที่ได้รับการป้องกัน onbinaryMessage (bytebuffer arg0) โยน ioexception {
-
-
-
อธิบาย
ความคิดของฉันนี่คือ
1 เมื่อเข้าถึงผู้ใช้แต่ละคนต้องป้อนชื่อของเขาหรือเธอก่อนจากนั้นส่งคำขอการเชื่อมต่อไปยังเซิร์ฟเวอร์
2 หลังจากเซิร์ฟเวอร์ยอมรับคำขอการเชื่อมต่อของไคลเอ็นต์แล้วจะมี Chatwebsocket ใหม่ (ผู้ใช้); ในการประมวลผลคำขอนี้และเพิ่มลงในรายการผู้ใช้ออนไลน์ ในเวลานี้เซิร์ฟเวอร์ยังไม่ทราบชื่อของลูกค้า มันจะถือว่าชื่อสำหรับผู้ใช้รายนี้ #1 และเซิร์ฟเวอร์จะส่งชื่อ + /t + #1 ไปยังไคลเอนต์คุณชื่ออะไร?
3 เมื่อไคลเอนต์ได้รับข้อความนี้จะรู้ว่าเซิร์ฟเวอร์กำลังถามว่าชื่อคืออะไรดังนั้นไคลเอนต์จะส่งชื่อ+/t+#1+_+ชื่อของตัวเองไปยังเซิร์ฟเวอร์ (ชื่อของฉันคือ xxx)
4 หลังจากได้รับข้อความนี้เซิร์ฟเวอร์จะค้นหาในรายชื่อผู้ใช้ออนไลน์ในปัจจุบันตาม #1 และแทนที่ #1 ด้วยชื่อของลูกค้าเพื่อให้เซิร์ฟเวอร์ทราบชื่อของลูกค้า
5 เมื่อลูกค้าออกไปเซิร์ฟเวอร์จะทริกเกอร์เหตุการณ์ onClose และเซิร์ฟเวอร์จะลบผู้ใช้ปัจจุบันออกจากรายการออนไลน์
ใช้รูปภาพเพื่อวาดอะไรแบบนี้ (วาดไม่ดี -_- !!)
รหัส
JS
WS = ใหม่ webSocket (ws: // + location.host +/websocket/socketserver);
เชื่อมต่อกับเซิร์ฟเวอร์
ชวา
ได้รับการป้องกัน streaminbound createWebsocketInbound (String arg0,
httpservletrequest arg1) {
// todo วิธีการที่สร้างขึ้นอัตโนมัติสตับ
ส่งคืน Chatwebsocket ใหม่ (ผู้ใช้);
-
สร้าง chatwebsocket เพื่อจัดการคำขอนี้และเรียกเหตุการณ์ Onopen ของวัตถุ Chatwebsocket
@Override
Void Onopen (WSoutbound ขาออก) {
// this.connection = การเชื่อมต่อ;
this.username = # + string.valueof (ผู้ใช้);
ผู้ใช้ ++;
พยายาม {
String message = name + /t + this.username;
charbuffer buffer = charbuffer.wrap (ข้อความ);
this.getWsoutbound (). writeTextMessage (บัฟเฟอร์);
} catch (ioexception e) {
// todo catch block ที่สร้างอัตโนมัติ
E.PrintStackTrace ();
-
users.add (นี่);
-
สมมติว่าชื่อสำหรับลูกค้านี้ส่งชื่อชื่อ+/t+ ไปยังไคลเอนต์และเพิ่มไคลเอนต์ไปยังรายการไคลเอนต์ที่เชื่อมต่อในปัจจุบัน
JS
ฟังก์ชั่น HandleData (ข้อมูล)
-
var vals = data.split (/t);
var msgtype = vals [0];
สวิตช์ (msgtype)
-
ชื่อกรณี:
var msg = vals [1];
var mes = name+/t+msg+_+ชื่อผู้ใช้;
ส่ง (mes);
หยุดพัก;
-
-
-
ยอมรับและประมวลผลข้อความที่ส่งโดยเซิร์ฟเวอร์และพบว่าเซิร์ฟเวอร์ถามว่าชื่อคืออะไรดังนั้นจึงส่งชื่อ+/t+ชื่อที่สันนิษฐาน+_+ไปยังเซิร์ฟเวอร์
ชวา
โมฆะสาธารณะ onMessage (ข้อมูลสตริง) {
String [] val1 = data.split (// t);
if (val1 [0] .Equals (ชื่อ))
-
String [] val2 = val1 [1] .split (_);
สำหรับ (chatwebsocket ผู้ใช้: ผู้ใช้) {
if (user.username.equals (val2 [0])) {
user.username = val2 [1];
-
-
-
-
-
ประมวลผลและยอมรับข้อความที่ส่งโดยลูกค้าและพบว่าลูกค้าตอบกลับชื่อของมันดังนั้นค้นหาในรายการไคลเอนต์ที่เชื่อมต่อในปัจจุบันตามชื่อที่สันนิษฐานไว้ก่อนหน้านี้และเปลี่ยนนามแฝงเป็นชื่อจริง
JS
ฟังก์ชั่น sendmessage ()
-
var otherName = $ (#otherName) .val ();
var msg = msg/t+ชื่อผู้ใช้+_+otherName+_+$ (#ข้อความ) .val ();
ส่ง (ผงชูรส);
-
ไคลเอนต์เริ่มการสนทนากับบุคคลอื่นและรูปแบบข้อความคือ: msg+ชื่อของตัวเอง+_+ชื่อตรงข้าม+_+ข้อความ
ชวา
โมฆะสาธารณะ onMessage (ข้อมูลสตริง) {
-
อื่นถ้า (val1 [0] .equals (msg))
-
String [] val2 = val1 [1] .split (_);
สำหรับ (chatwebsocket ผู้ใช้: ผู้ใช้) {
if (user.username.equals (val2 [1])) {
พยายาม {
charbuffer temp = charbuffer.wrap (ข้อมูล);
user.getWsoutbound (). writeTextMessage (อุณหภูมิ);
} catch (ioexception e) {
// todo catch block ที่สร้างอัตโนมัติ
E.PrintStackTrace ();
-
-
-
-
-
-
พบว่าลูกค้าถูกส่งข้อความ ตามชื่อของอีกฝ่ายค้นหาในรายชื่อลูกค้าที่เชื่อมต่อในปัจจุบันและส่งข้อความถึงเขา
JS
ฟังก์ชั่น HandleData (ข้อมูล)
-
var vals = data.split (/t);
var msgtype = vals [0];
สวิตช์ (msgtype)
-
-
CASE MSG:
var val2s = vals [1] .split (_);
var จาก = val2s [0];
ข้อความ var = val2s [2];
การแจ้งเตือน (จาก+:+ข้อความ);
หยุดพัก;
ค่าเริ่มต้น:
หยุดพัก;
-
-
พบว่ามันเป็นข้อความที่ส่งโดยลูกค้ารายอื่นและมันถูกแสดงผ่านการแจ้งเตือน
ชวา
@Override
เป็นโมฆะป้องกัน onClose (สถานะ int) {
users.remove (นี่);
-
การค้นพบลูกค้าที่เหลืออยู่ลบลูกค้าออกจากรายชื่อลูกค้าที่เชื่อมต่อ
จะปรับปรุงได้อย่างไร
1. หากไคลเอนต์ A ส่งข้อความไปยัง B และ B ไม่ได้ออนไลน์ข้อความสามารถเก็บไว้ในฐานข้อมูล เมื่อพบว่า B ออนไลน์จะถูกดึงออกจากฐานข้อมูลและส่งไปยัง B
2 เมื่อเซิร์ฟเวอร์ส่งชื่อของคุณสามารถเพิ่มกลไกการหมดเวลา หากลูกค้าไม่ตอบกลับสิ่งที่เรียกว่าภายในระยะเวลาหนึ่งลูกค้าสามารถลบลูกค้าออกจากรายการออนไลน์