1.网络通信协议
- TCP协议
TCP协议是一种面向连接的协议,就是在通信之前需要先建立连接。举个列子,就像打电话一样,需要对方先接通,才能进行通话。 - UDP协议
UDP是面向无连接的协议,在通信的时候不需要建立连接,只需要直接将数据发出即可。 - 安全性分析
两者相比,TCP更加安全,更加可靠,TCP提供可靠传输服务,无差错,不丢失,不重复。UDP通信则可靠性低,因为是无连接的,同时不管信息是否已经传输成功,芷尽最大努力实现交付。 - 效率比较
TCP相对来说效率较低
UDP传输效率更高,适用于高速传输和实时传输。
2.WebSocket
- 什么是webSocket?
WebSocket是一中在TCP连接上进行全双工通信的协议。 - 什么是全双工通信
在通信时双方可同时进行信息的传输,不需要等待另一方传输完毕再进行传输。
3.主要代码
- 主要前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室</title>
<script src="/jquery.js" type="text/javascript"></script>
<style>
#message{
width: 910px;
height: 610px;
}
#box{
float: left;
width: 600px;
height: 610px;
display: none;
}
#box1{
float: left;
background-color: gray;
width: 300px;
height: 600px;
}
#chatArea{
width: 600px;
height: 300px;
background-color: gainsboro;
}
#sendArea{
width: 600px;
height: 300px;
background-color: coral;
}
#content{
width: 500px;
}
#haoyou{
height: 300px;
}
#userList{
list-style: none;
}
#broadList{
list-style: none;
}
#msgRight{
width: 100%;
text-align: right;
color: coral;
}
#msgLeft{
width: 100%;
color: aqua;
}
</style>
<script>
var toName;
var username;
function showCat(name){
toName = name;
//现在聊天对话框
$("#box").css("display","block");
//清空聊天区
$("#chatArea").html("");
$("#chatMes").html("正在和<font face=\"楷体\">"+toName+"</font>聊天");
//存储聊天记录 sessionStorge
var chatData = sessionStorage.getItem(toName);
if(chatData != null){
//将聊天记录渲染到聊天去
$("#chatArea").html(chatData);
}
}
$(function (){
$.ajax({
url:"/user/getUsername",
success:function (res){
username = res;
// $("#username").html("用户:"+res+"<span style='float: right;color: green'>在线</span>");
},
async:false
});
if("WebSocket" in window){
//创建一个websocket
var ws = new WebSocket("ws://localhost:8080/chat");
}else{
alert("该浏览器不支持websocket");
}
//给ws绑定事件
ws.onopen = function (){
//在建立连接后需要做什么事?
//1.显示在线信息
$("#username").html("用户:"+username+"<span style='float: right;color: green'>在线</span>");
}
//接收到服务端推送的消息后,触发这个事件
ws.onmessage = function (evt){
//获取服务端推送过来的消息
var dataStr = evt.data;
//将dataStr转换为json对象
var res = JSON.parse(dataStr);
console.log(res);
//判断是否时系统消息
if(res.system){
//系统消息
//1.好友列表展示
//2.系统广播展示
var names = res.message;
var userListStr = "";
var broadListStr = "";
for (var name of names) {
if(name != username){
userListStr += "<li><a οnclick='showCat(\""+name+"\")'>"+name+"</a></li>";
broadListStr +="<li>您的好友"+name+"已上线</li>";
}
}
//渲染好友列表和广播
$("#userList").html(userListStr);
$("#broadList").html(broadListStr);
}else{
//不是系统消息
//将服务端推送的消息进行展示
var str = " <div id=\"msgLeft\">"+res.message+"</div>"
if(toName != res.fromName){
$("#chatArea").append(str);
}
$("#chatArea").append(str)
//存储数据
var chatData = sessionStorage.getItem(res.fromName);
if(chatData != null){
//拿到聊天记录
str = chatData+str;
}
sessionStorage.setItem(res.fromName,str);
}
}
ws.onclose = function (){
$("#username").html("用户:"+username+"<span style='float: right;color: red'>离线</span>");
}
//点击发送按钮
$("#send").click(function (){
//获取输入的内容
var data = $("#content").val();
//清空输入区的内容
$("#content").val("");
//将消息数据展示在聊天区
var str = "<div id=\"msgRight\">"+data+"</div>"
$("#chatArea").append(str);
var json = {"toName":toName,"message":data};
//将聊天内容保存
//先拿到用户,看是否存在聊天记录
//如果不存在聊天记录,将将当前的聊天存起来
var chatData = sessionStorage.getItem(toName);
if(chatData != null){
//拿到聊天记录
str = chatData+str;
}
sessionStorage.setItem(toName,str);
//将数据发送给服务端
ws.send(JSON.stringify(json));
})
})
</script>
</head>
<body>
<span id="username"></span>
<div id="message">
<div id="box">
<span id="chatMes"></span>
<div id="chatArea">
</div>
<div id="sendArea">
<textarea id="content"></textarea>
<button id="send">发送</button>
</div>
</div>
<div id="box1">
<div id="haoyou">
<span style="background-color: aqua;font-size: 20px">好友列表</span>
<ul id="userList">
</ul>
</div>
<div>
<span style="background-color: aqua;font-size: 20px">广播列表</span>
<ul id="broadList">
</ul>
</div>
</div>
</div>
</body>
</html>
- 主要的后端代码
实体类
package com.dfc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
private String toName;
private String message;
}
package com.dfc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//登录使用
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private boolean flag;
private String message;
}
package com.dfc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultMessage {
private boolean isSystem;
private String fromName;
private Object message;//如果是系统推送消息是数组
}
package com.dfc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String username;
private String password;
}
controller层
package com.dfc.controller;
import com.dfc.pojo.Result;
import com.dfc.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public Result login(User user, HttpSession session){
Result result = new Result();
if(user != null && "123".equals(user.getPassword())){
result.setFlag(true);
//将用户保存到session中
session.setAttribute("user",user.getUsername());
}else{
result.setFlag(false);
result.setMessage("登录失败");
}
return result;
}
@RequestMapping("/getUsername")
public String getUsername(HttpSession session){
String username = (String) session.getAttribute("user");
return username;
}
}
WebSocket主要实现代码
package com.dfc.ws;
import com.dfc.pojo.Message;
import com.dfc.utils.MessageUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value="/chat",configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {
//定义一个对象用来存储客户端对象对应的ChatEndpoint 双列
private static Map<String,ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();
//声明session对象,通过该对象可以发送消息给指定用户
private Session session;
//声明一个HttpSession对象,因为我们之前在HttpSession中存储了用户名
private HttpSession httpSession;
//连接建立时被调用
@OnOpen
public void onOpen(Session session, EndpointConfig config){
//将局部session对象赋值给成员session
this.session = session;
//获取HttpSession对象
HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
this.httpSession = httpSession;
//从httpSession对象中获取用户名
String username = (String) httpSession.getAttribute("user");
//将当前对象存储到容器中
onlineUsers.put(username,this);
//将当前在线用户名推送给所有的客户端
//1.获取消息
String message = MessageUtils.getMessage(true,null,getNames());
//2.调用方法进行系统消息的推送
broadAllUsers(message);
}
//从onlineUsers中得到在线用户名字
private Set<String> getNames(){
return onlineUsers.keySet();
}
private void broadAllUsers(String message){
try{
//要将该消息推送给所有的客户端
Set<String> names = onlineUsers.keySet();
for (String name : names) {
ChatEndpoint chatEndpoint = onlineUsers.get(name);
chatEndpoint.session.getBasicRemote().sendText(message);
}
}catch (Exception e){
e.printStackTrace();
}
}
//接收到客户端发送的数据被调用
@OnMessage
public void onMessage(String message,Session session){
//将message转换成message对象
ObjectMapper mapper = new ObjectMapper();
try {
Message mess = mapper.readValue(message, Message.class);
//获取接收数据的用户
String toName = mess.getToName();
//获取消息数据
String data = mess.getMessage();
//获取当前登录的用户
String username = (String) httpSession.getAttribute("user");
//获取推送给指定用户的消息格式的数据
String resultMessage = MessageUtils.getMessage(false,username,data);
//发送数据
onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
//连接关闭时被调用
@OnClose
public void onClose(Session session){
String username = (String) httpSession.getAttribute("user");
//处理用户已经下线
//从容器中删除指定的用户
onlineUsers.remove(username);
//获取推送的消息
String message = MessageUtils.getMessage(true,null,getNames());
broadAllUsers(message);
}
}
//需要将这个类托管给spring来管理
package com.dfc.ws;
import lombok.val;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
//将httpSession对象存储到配置对象中
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
}
}
//消息处理工具类
package com.dfc.utils;
import com.dfc.pojo.ResultMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
public class MessageUtils {
public static String getMessage(boolean isSystemMessage,String fromName,Object message){
try {
ResultMessage resultMessage = new ResultMessage();
resultMessage.setSystem(isSystemMessage);
resultMessage.setMessage(message);
if(fromName != null){
resultMessage.setFromName(fromName);
}
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(resultMessage);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
4.效果展示
再浏览器中登录一个用户名为 123的用户,这是自己的用户名,然后再用另一个浏览器登录另一个账户,将会再好友列表中展示处已经上线的用户。这是服务器推送的广播消息
之后点击想要发送消息的用户就可以发送消息了
页面做的比较丑,请忽略
做了一个稍微能看的下去的
![在这里插入图片描述](
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D-F</title>
<script src="js/jquery.js"></script>
<style>
#box{
display: flex;
flex-direction: row;
}
#userList{
width: 140px;
height: 600px;
border-radius: 10px;
border: 1px solid gainsboro;
}
#chatArea{
width: 300px;
height: 600px;
border-radius: 10px;
border: 1px solid gainsboro;
}
#boradArea{
width: 200px;
height: 600px;
border-radius: 10px;
border: 1px solid gainsboro;
}
#userList-head{
display: flex;
flex-direction: row;
align-items: center;
border-radius: 10px;
background-color: antiquewhite;
}
#boradArea-head{
display: flex;
flex-direction: row;
/* width: 100%;
height: 20px; */
border-radius: 10px;
align-items: center;
background-color: antiquewhite;
box-sizing: border-box;
}
#userList-head img{
width: 17px;
height: 17px;
}
#common{
display: none;
width: 140px;
height: 583px;
border-radius: 10px;
}
#userInfo{
background-color: antiquewhite;
border-radius: 10px;
text-align: center;
}
/* 聊天区域 */
#chatContent{
width: 100%;
height: 450px;
/* border-bottom:1px solid black; */
}
#right{
width: 100%;
display: flex;
justify-content: end;
}
#right-item img{
width: 20px;
height: 20px;
}
/* 接收消息展示 */
#left{
width: 100%;
display: flex;
justify-content: start;
}
#left-item img{
width: 20px;
height: 20px;
}
#message-box{
display: flex;
flex-direction: row;
background-color: rgb(252, 251, 251);
}
#message{
width: 80%;
height: 30px;
border-radius: 20px;
}
#submit{
width: 15%;
height: 20px;
margin-top: 8px;
background-color: rgb(48, 250, 48);
text-align: center;
border-radius: 20px;
}
#diy{
display: flex;
flex-direction: row;
justify-content: space-around;
background-color: rgb(252, 251, 251);
}
#diy-item{
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
#diy-item img{
width: 20px;
height: 20px;
}
/* 展示好友列表 */
#flist{
list-style: none;
line-height: 40px;
}
#flist a{
text-decoration: none;
}
#myfont{
position: fixed;
width: 200px;
height: 100px;
background-color: azure;
top: 350px;
left: 200px;
display: none;
}
</style>
<script>
//点击好友进行聊天
function show(name){
$("#name").html(name);
$("#action").css("display","block");
$("#other").css("display","none");
}
//弹出显示框
function change(index){
if(index == 1){
$("#myfont").css("display","block");
}
}
$(function(){
var flag = true;
$("#down").click(function(){
if(flag){
//获取下拉图标id
$("#common").css("display","block");
$("#down").attr("src","img/up.png");
flag = false;
}else{
//点击上拉按钮收回好友列表
$("#common").css("display","none");
$("#down").attr("src","img/down.png");
flag = true;
}
});
//点击返回返回首页
$("#back").click(function(){
$("#action").css("display","none");
$("#other").css("display","block");
});
//点击改变字体
$("#commit").click(function(){
var data = $("#fontValue").val();
$("#myfont").css("display","none");
$("#msg").css("font-size",data+"px");
$("#msg1").css("font-size",data+"px");
})
});
</script>
</head>
<body>
<div id="box">
<div id="userList">
<div id="userList-head">
<span id="title">在线好友</span>
<img src="img/down.png"/ id="down">
</div>
<!-- 展示好友列表 -->
<div id="common">
<ul id="flist">
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('张三丰')">张三丰</a></li>
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('张无忌')">张无忌</a></li>
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('赵无极')">赵无极</a></li>
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('赵敏')">赵敏</a></li>
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('迪丽热巴')">迪丽热巴</a></li>
<li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('邓超')">邓超</a></li>
</ul>
</div>
</div>
<div id="chatArea">
<!-- 聊天区域 -->
<!-- 聊天头部 -->
<div id="action" style="display: none;">
<div id="userInfo">
<img src="img/back.png" style="width: 20px;height:20px; float: left;" id="back"/>
正在和<font style="font-weight:200;color: chartreuse;" id="name"></font>聊天
</div>
<div id="chatContent">
<div id="right">
<div id="right-item">
<span id="msg">你好</span>
<img src="img/boy.png"/>
</div>
</div>
<div id="left">
<div id="left-item">
<img src="img/girl.png"/>
<span id="msg1">你好</span>
</div>
</div>
</div>
<!-- 消息输入框 -->
<div id="message-box">
<textarea id="message"></textarea>
<div id="submit">发送</div>
</div>
<!-- 用户自定义选项 -->
<div id="diy">
<div id="diy-item" onclick="change(1)">
<img src="img/font.png"/>
<span>字体</span>
</div>
<div id="diy-item" onclick="change(2)">
<img src="img/pifu.png"/>
<span>颜色</span>
</div>
<div id="diy-item" onclick="change(3)">
<img src="img/add.png"/>
<span>更多</span>
</div>
</div>
</div>
<!-- 没有进行聊天的时候展示页面-->
<div id="other">欢迎来到D-f聊天室</div>
<!-- 选择字体 -->
<div id="myfont">
<input type="number" max="40" min="5" id="fontValue" placeholder="输入字体大小"/>
<input type="submit" value="确认" id="commit"/>
</div>
</div>
<div id="boradArea">
<div id="boradArea-head">
<span id="title">广播消息</span>
</div>
</div>
</div>
</body>
</html>
`