本人博客全部迁至个人博客,柒情博客:http://www.ityw.club ,后续所有博客将在柒情博客上进行更新。
一、相遇
某天发现某个网站有可以检测IP的功能,能检测出IP是否高匿、延时、地区,恰好前不久做了能提取IP的项目,正好可以利用这个接口提高我们使用IP的质量,咋办呢?
打开我们的Fiddler盘它!
fd:努力工作中!
我:啥? 没数据 没数据 fd坏了? 打开浏览器调试一波
浏览器:努力工作中!
我:啥? 还是没数据? 今天见鬼了?
让我郁闷了好久,平时所向无敌的FD大法失效了,本来还以为是那个网站随机返回的假数据,于是机智的我看了下源码,嘿嘿嘿,总算让我抓住了今天我们的主角 “webSocket”,原来那个网站在进页面的时候就直接用webSocket和服务器建立了长连接,所以我们就抓不到包啦,主角找出来了 大结局吧! 不不不 害我郁闷这么久 继续盘它。
二、相知
于是开始疯狂百度谷歌中,终于发现了一篇通俗易懂有意思的文章,也发现了一位发型很有型的实战大佬。
想和webSocket相知的 进去和webSocket交流下感情吧,通道如下:
有趣的灵魂大佬:https://www.zhihu.com/question/20215561
实战大佬:https://www.cnblogs.com/xdp-gacl/p/5193279.html
三、相爱
简单的和webSocket交流了下感情,发现webSocket还是挺有魅力的,于是我决定继续盘它,毕竟滴水之恩须涌泉相报嘛。
下面我们开始用webSocket做个小小的聊天室吧,来上我们的祖传代码!
1.项目结构图
2.在pom.xml中引入我们的jar包
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
3.后端代码
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket")
public class WebSocket {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
//更新页面在线人数
for(WebSocket item: webSocketSet){
try {
item.sendMessage(String.valueOf(getOnlineCount()));
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
//更新页面在线人数
for(WebSocket item: webSocketSet){
try {
item.sendMessage(String.valueOf(getOnlineCount()));
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for(WebSocket item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
}
}
4.前端代码
<%@ page language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>柒情</title>
<script src="static/base/jquery.min.js" charset="utf-8"></script>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" rel="stylesheet">
<link rel="stylesheet" href="static/socket/css/reset.min.css">
<link rel="stylesheet" href="static/socket/css/style.css">
<link rel="stylesheet" href="static/layui/css/layui.css" media="all">
<script type="text/javascript" src="static/layui/layui.all.js"></script>
<script src="static/socket/js/index.js"></script>
<style type="text/css">
.msg-text-color{
color:#8097C2;
margin-bottom:5px;
}
.msg-text-margin{
margin-top: 13%;
}
.msg-text-me-margin{
margin-top: 10%;
margin-left:59%;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="container">
<div class="left">
<div class="top">
<input type="text" placeholder="Search" />
<a href="javascript:;" class="search"></a>
</div>
<ul class="people">
<li class="person" data-chat="person6">
<img src="static/socket/img/drake.jpg" alt="" />
<span class="name">Drake</span>
<span class="time">2:09 PM</span>
<span class="preview">howdoyoudoaspace</span>
</li>
</ul>
</div>
<div class="right">
<div class="top"><span>To: <span class="name" id="menNum">Dog Woofson</span></span></div>
<div class="chat" data-chat="person2" id="message" style="height:78%;overflow:auto">
<div class="conversation-start">
<span>hi, 柒情</span>
</div>
</div>
<div class="write">
<a href="javascript:;" class="write-link attach"></a>
<input type="text" id="text"/>
<a href="javascript:;" class="write-link smiley"></a>
<a href="javascript:;" class="write-link send" onclick="send()"></a>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript">
var websocket = null;
var intervaler = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//这里需要改成自己项目的地址
websocket = new WebSocket("ws://www.lingchang.club/websocket");
getName();
//历史消息
var storage=window.localStorage;
for(var i=0;i<storage.length;i++){
var key=storage.key(i);
var value=storage.getItem(key);
if(value.indexOf("{")>-1){
var obj = JSON.parse(value);
if(null!=key&&key.indexOf("bubble")>-1){
if(key.indexOf("You")>-1){
setMessageInnerHTML(obj,"bubble you");
}else{
setMessageInnerHTML(obj,"bubble me");
}
}
}
}
lastBomScrollBar();
}
else {
alert('当前浏览器 Not support websocket')
}
function creatData(name,time,msg) {
var data=new Object();
data.name=name;
data.time=time;
data.msg=msg;
return data;
}
function lastBomScrollBar() {
//滚动条定位在最下面
$('.chat').scrollTop($('.chat')[0].scrollHeight);
}
//连接发生错误的回调方法
websocket.onerror = function () {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
console.log("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
if(event.data.indexOf("{")>-1){
var dt = JSON.parse(event.data);
var storage=window.localStorage;
var name=storage.getItem("ltName");
if(""==name||null==name||name=="undfined"){
return false;
}
//这里是根据聊天者的名字区分是否为发送者的,假如两人名字一样会有bug,需要正式使用的话建议名字用用户的输入的名称和时间戳拼接
if(name==dt.name){
storage.setItem(dt.time +"-bubbleMe",event.data);
setMessageInnerHTML(dt,"bubble me");
}else{
storage.setItem(dt.time +"-bubbleYou",event.data);
setMessageInnerHTML(dt,"bubble you");
var msg='【您有新的消息】';
showTips(msg);
}
}else{
$("#menNum").html("当前在线人数:"+event.data+"人");
}
}
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
}
//获取发送人的名字
function getName(){
var storage=window.localStorage;
var name=storage.getItem("ltName");
if(""==name||null==name||name=="undfined"){
//prompt层
layer.prompt({title: '输入昵称,并确认', formType: 2,offset: 'auto'}, function(pass, index){
layer.close(index);
if(null==pass||""==pass){
layer.msg('昵称不能为空鸭,重新输入吧。');
getName();
return false;
}
var storage=window.localStorage;
storage.setItem("ltName",pass);
name=pass;
});
}
return name;
}
//将消息显示在网页上
function setMessageInnerHTML(data,clas) {
if(clas.indexOf("you")>-1){
var msg="<div class='msg-text-color msg-text-margin'> "+data.name+" "+ format(data.time)+" </div> <div class='"+clas+"'> "+data.msg+" </div>";
document.getElementById('message').innerHTML += msg + '<br/>';
lastBomScrollBar();
}else{
var msg="<div class='msg-text-color msg-text-me-margin'> "+format(data.time)+" "+ data.name+" </div> <div class='"+clas+"'> "+data.msg+" </div>";
document.getElementById('message').innerHTML += msg + '<br/>';
lastBomScrollBar();
}
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
var storage=window.localStorage;
var name=storage.getItem("ltName");
var data=creatData(name,(new Date()).getTime(),message);
websocket.send(JSON.stringify(data));
$("#text").val("");
}
//回车发送
document.onkeydown = function (e) {
if (!e) e = window.event;
if ((e.keyCode || e.which) == 13) {
send();
}
}
function add0(m){return m<10?'0'+m:m }
//时间戳转换为时间类型
function format(shijianchuo)
{
var time = new Date(shijianchuo);
var y = time.getFullYear();
var m = time.getMonth()+1;
var d = time.getDate();
var h = time.getHours();
var mm = time.getMinutes();
var s = time.getSeconds();
return y+'-'+add0(m)+'-'+add0(d)+' '+add0(h)+':'+add0(mm)+':'+add0(s);
}
//进入页面关闭消息提示
document.addEventListener('visibilitychange', function() {
var isHidden = document.hidden;
if (!isHidden) {
clearInterval(intervaler);
showTips("柒情");
intervaler = null;
}
});
//有消息浏览器标签处提示
function showTips(tip){
intervaler = setInterval(function(){
if(document.title === tip&&"柒情"!=document.title){
document.title = '【 】';
}else{
document.title = tip;
}
},300);
}
</script>
</html>
四、相杀
啥? 你还想看戏? no no no,接下来就是你们的舞台,这个项目coding中肯定还会遇见各种问题的,有问题就在评论区展示出来吧,解决了也可以展示出来,方便后人嘛,哈哈哈哈哈哈
项目源码地址:https://gitee.com/liuyuzhijia/willProject
项目在线体验地址:http://www.lingchang.club/webSocket.jsp
这dome和理论来大部分来源于上面两位大佬,再此对他们表示感谢,我是站在巨人肩膀上的小码农。