Java如何实现不同局域网TCP通信+群聊+私聊(云服务器实现)
繼上一篇實(shí)現(xiàn)群聊,現(xiàn)在記錄私聊的實(shí)現(xiàn),下一篇記錄一下實(shí)現(xiàn)文件的傳輸。
socket keepalive理解 - 小小小小濤 - 博客園 (cnblogs.com)
目錄
一、實(shí)現(xiàn)思路
1.實(shí)現(xiàn)思路一
2.實(shí)現(xiàn)思路二
二、實(shí)現(xiàn)代碼
1.思路一(服務(wù)端代碼)
2.思路二(服務(wù)端代碼)
三、運(yùn)行效果
一、實(shí)現(xiàn)思路
用戶輸入@name的方式發(fā)送消息(類似于QQ你@別人),但是想通過(guò)服務(wù)器端從信息中解析出這個(gè)@的人的名字不太可能,因?yàn)榉?wù)器端不知道截多長(zhǎng)才是用戶的名字。所以只能判斷信息中是否包含了@name。String類的contains()方法可以做到。
服務(wù)端有以下兩種思路去實(shí)現(xiàn)私聊
1.實(shí)現(xiàn)思路一
先判斷用戶是否@人了。
如果@人了,則發(fā)送消息的用戶的服務(wù)套接字讓其他的服務(wù)套接字判斷這個(gè)@的名字是不是自己服務(wù)的用戶的名字,如果是就轉(zhuǎn)發(fā),不是就不轉(zhuǎn)發(fā)。
如果沒(méi)@,則全部轉(zhuǎn)發(fā)。
2.實(shí)現(xiàn)思路二
先判斷用戶是否@人了。
如果@人了,發(fā)送消息的用戶的服務(wù)套接字根據(jù)@的名字讓該名字的服務(wù)套接字轉(zhuǎn)發(fā)。(哈希表)
如果沒(méi)@,則全部轉(zhuǎn)發(fā)。
二、實(shí)現(xiàn)代碼
我們的用戶端只負(fù)責(zé)輸入,因此只需要改變服務(wù)端的代碼。(這里也只記錄服務(wù)器端的代碼,用戶端的代碼和工具類代碼在上一篇)。
1.思路一(服務(wù)端代碼)
import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List;import com.csi.qunliaoTest.OpenClient.ReceiveMsgThread;public class OpenServer {//一個(gè)列表存儲(chǔ)服務(wù)SocketList<ClientSocket> clientSockets =new ArrayList<>(); ServerSocket serverSocket = null; //構(gòu)造方法中創(chuàng)建服務(wù)器ServerSocketpublic OpenServer() {try {//因?yàn)楸敬a是在云服務(wù)器運(yùn)行,直接用InetAddress.getLocalHost()方法綁定云服務(wù)器的IP地址及端口號(hào)serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost()); System.out.println("----------服務(wù)器----------");} catch (IOException e) {//發(fā)生異常。調(diào)用自己寫的Utils類關(guān)閉服務(wù)器Utils.close(serverSocket);}//ServerSocket綁定成功,開始等待用戶接入if(serverSocket.isBound())acceptClient();}//等待用戶,并為他生成服務(wù)Socketprivate void acceptClient(){//while循環(huán)讓服務(wù)器一直可以接入用戶,一個(gè)用戶接入服務(wù)器,服務(wù)器就生成一個(gè)為該用戶服務(wù)的Socketwhile(true) {Socket socket=null;try {//ServerScoket的accept()方法是一個(gè)阻塞方法,他會(huì)在這里等用戶接入,直到有用戶接入,才會(huì)運(yùn)行下面的代碼socket=serverSocket.accept();System.out.println("一個(gè)用戶接入.....");//用ClientSocket類包裝Scoket類,ClientSocket類是自己寫的內(nèi)部線程類,該類實(shí)現(xiàn)了接收和轉(zhuǎn)發(fā)用戶消息ClientSocket clientSocket=new ClientSocket(socket);//開啟服務(wù)線程clientSocket.start();//將這個(gè)包裝了服務(wù)Scoket的對(duì)象添加進(jìn)列表clientSockets.add(clientSocket);} catch (IOException e) {Utils.close(socket);} }}public static void main(String[] args) {//執(zhí)行服務(wù)器代碼new OpenServer();}class ClientSocket extends Thread{Socket socket=null;DataInputStream dataInputStream=null;DataOutputStream dataOutputStream=null;String name = null;public ClientSocket(Socket socket) {this.socket = socket;//包裝服務(wù)Socket的輸入輸出流,異常就調(diào)用closeScoket()方法try {dataInputStream=new DataInputStream(socket.getInputStream());} catch (IOException e) {closeSocket(dataInputStream,socket);}try {dataOutputStream=new DataOutputStream(socket.getOutputStream());} catch (IOException e) {closeSocket(dataOutputStream,socket);}}//上面的代碼我們關(guān)閉的都是裝飾流,因?yàn)殛P(guān)閉裝飾流會(huì)將內(nèi)部流也關(guān)閉,Socket也會(huì)因此關(guān)閉,同時(shí)我們也要將列表里對(duì)應(yīng)Socket的刪除public void closeSocket(Closeable...closeables) {Utils.close(closeables);System.out.println("一位用戶退出");clientSockets.remove(this);}@Overridepublic void run() {//讀取用戶姓名,并讓列表所有用戶轉(zhuǎn)發(fā)歡迎信息//數(shù)據(jù)流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是將數(shù)據(jù)以UTF-8的編碼方式發(fā)出或者接收try {//readUTF()方法也是阻塞方法,讀取用戶發(fā)來(lái)的名字name=dataInputStream.readUTF();} catch (IOException e1) {closeSocket(dataInputStream);}//循環(huán)列表,除了自己,其他服務(wù)Scoket全部轉(zhuǎn)發(fā)消息for(ClientSocket clientSocket:clientSockets) {if(clientSocket!=this) {try {clientSocket.dataOutputStream.writeUTF("歡迎"+name+"進(jìn)入聊天室");clientSocket.dataOutputStream.flush();//清空緩存區(qū),讓緩存區(qū)的數(shù)據(jù)全部出來(lái)} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}//知道名字后就一直等待接收用戶端發(fā)的消息,異常就關(guān)閉Scoket并跳出循環(huán)while(true) { String msg = null;//在讀取到的消息前加上姓名try {msg = name+":"+dataInputStream.readUTF();} catch (IOException e) {closeSocket(dataInputStream);break;}//判斷信息中是否@人了,如果@人了,其他每個(gè)服務(wù)Socket判斷@的名字里是不是自己服務(wù)的用戶名字;如果沒(méi)有@,則全部轉(zhuǎn)發(fā)if(msg.contains("@")) {for(ClientSocket clientSocket:clientSockets) {if(clientSocket!=this) {try {if(msg.contains("@"+clientSocket.name)) { //找到了是自己的用戶端名字//將@名字替換為空,replace返回一個(gè)心得字符串clientSocket.dataOutputStream.writeUTF(msg.replace("@"+clientSocket.name, ""));clientSocket.dataOutputStream.flush();}} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}}else {for(ClientSocket clientSocket:clientSockets) {if(clientSocket!=this) {try {clientSocket.dataOutputStream.writeUTF(msg);clientSocket.dataOutputStream.flush();} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}}}}}}2.思路二(服務(wù)端代碼)
用戶名不要寫一樣的名字,他是根據(jù)名字找套接字,當(dāng)查找到這個(gè)名字就去哈希表找對(duì)應(yīng)的套接字,如果名字相同,他只會(huì)找第一個(gè)與這個(gè)名字相同的套接字。
package com.csi.siliaoTest;import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.HashMap; import java.util.List;import com.csi.siliaoTest.OpenServer.ClientSocket;public class OpenServer2 {//一個(gè)哈希表存儲(chǔ)用戶名字和服務(wù)套接字HashMap<String, ClientSocket> map= new HashMap<>();//一個(gè)列表存儲(chǔ)服務(wù)SocketList<ClientSocket> clientSockets =new ArrayList<>();//一個(gè)列表存儲(chǔ)用戶名字List<String> names = new ArrayList<>();ServerSocket serverSocket = null; //構(gòu)造方法中創(chuàng)建服務(wù)器ServerSocketpublic OpenServer2() {try {//因?yàn)楸敬a是在云服務(wù)器運(yùn)行,直接用InetAddress.getLocalHost()方法綁定云服務(wù)器的IP地址及端口號(hào)serverSocket = new ServerSocket(7777, 50, InetAddress.getLocalHost()); System.out.println("----------服務(wù)器----------");} catch (IOException e) {//發(fā)生異常。調(diào)用自己寫的Utils類關(guān)閉服務(wù)器Utils.close(serverSocket);}//ServerSocket綁定成功,開始等待用戶接入if(serverSocket.isBound())acceptClient();}//等待用戶,并為他生成服務(wù)Socketprivate void acceptClient(){//while循環(huán)讓服務(wù)器一直可以接入用戶,一個(gè)用戶接入服務(wù)器,服務(wù)器就生成一個(gè)為該用戶服務(wù)的Socketwhile(true) {Socket socket=null;try {//ServerScoket的accept()方法是一個(gè)阻塞方法,他會(huì)在這里等用戶接入,直到有用戶接入,才會(huì)運(yùn)行下面的代碼socket=serverSocket.accept();System.out.println("一個(gè)用戶接入.....");//用ClientSocket類包裝Socket類,ClientSocket類是自己寫的內(nèi)部線程類,該類實(shí)現(xiàn)了接收和轉(zhuǎn)發(fā)用戶消息ClientSocket clientSocket=new ClientSocket(socket);//開啟服務(wù)線程clientSocket.start();//將這個(gè)包裝了服務(wù)Socket的對(duì)象添加進(jìn)列表clientSockets.add(clientSocket);} catch (IOException e) {Utils.close(socket);} }}public static void main(String[] args) {//執(zhí)行服務(wù)器代碼new OpenServer2();}class ClientSocket extends Thread{Socket socket=null;DataInputStream dataInputStream=null;DataOutputStream dataOutputStream=null;String name = null;public ClientSocket(Socket socket) {this.socket = socket;//包裝服務(wù)Socket的輸入輸出流,異常就調(diào)用closeScoket()方法try {dataInputStream=new DataInputStream(socket.getInputStream());} catch (IOException e) {closeSocket(dataInputStream,socket);}try {dataOutputStream=new DataOutputStream(socket.getOutputStream());} catch (IOException e) {closeSocket(dataOutputStream,socket);}}//上面的代碼我們關(guān)閉的都是裝飾流,因?yàn)殛P(guān)閉裝飾流會(huì)將內(nèi)部流也關(guān)閉,Socket也會(huì)因此關(guān)閉,同時(shí)我們也要將列表里對(duì)應(yīng)Socket的刪除public void closeSocket(Closeable...closeables) {Utils.close(closeables);System.out.println("一位用戶退出");clientSockets.remove(this);names.remove(name);map.remove(name);}@Overridepublic void run() {//讀取用戶姓名,并讓列表所有用戶轉(zhuǎn)發(fā)歡迎信息//數(shù)據(jù)流DataIn/OutputStream的readUTF()和writeUTF(String data)要一起用,是將數(shù)據(jù)以UTF-8的編碼方式發(fā)出或者接收try {//readUTF()方法也是阻塞方法,讀取用戶發(fā)來(lái)的名字并添加哈希表和列表name=dataInputStream.readUTF();names.add(name);map.put(name, this);} catch (IOException e1) {closeSocket(dataInputStream);}//循環(huán)列表,除了自己,其他服務(wù)Socket全部轉(zhuǎn)發(fā)消息for(ClientSocket clientSocket:clientSockets) {if(clientSocket!=this) {try {clientSocket.dataOutputStream.writeUTF("歡迎"+name+"進(jìn)入聊天室");clientSocket.dataOutputStream.flush();//清空緩存區(qū),讓緩存區(qū)的數(shù)據(jù)全部出來(lái)} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}//知道名字后就一直等待接收用戶端發(fā)的消息,異常就關(guān)閉Scoket并跳出循環(huán)while(true) { String msg = null;//在讀取到的消息前加上姓名try {msg = name+":"+dataInputStream.readUTF();} catch (IOException e) {closeSocket(dataInputStream);break;}//判斷信息中是否@人了,如果@人了,查找是否有@的這個(gè)名字的用戶if(msg.contains("@")) {ClientSocket clientSocket = null;for(String aname: names) {if (msg.contains("@"+aname)) {try {clientSocket =map.get(aname);clientSocket.dataOutputStream.writeUTF(msg.replace("@"+aname, ""));clientSocket.dataOutputStream.flush();} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}}else {for(ClientSocket clientSocket:clientSockets) {if(clientSocket!=this) {try {clientSocket.dataOutputStream.writeUTF(msg);clientSocket.dataOutputStream.flush();} catch (IOException e) {clientSocket.closeSocket(clientSocket.dataOutputStream);}}}}}}}}注意:不要寫一樣的用戶名字,他是在哈希表里根據(jù)名字找套接字,如果名字相同,他只會(huì)找第一個(gè)與這個(gè)名字相同的套接字。如果寫一樣得名字就只有一個(gè)用戶會(huì)收到@的信息,并且會(huì)收到好幾條(有幾個(gè)重名就有幾條)。如下圖,我寫了兩個(gè)張三。
三、運(yùn)行效果
總結(jié)
以上是生活随笔為你收集整理的Java如何实现不同局域网TCP通信+群聊+私聊(云服务器实现)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 网站死链查询检测方法(seo的优化工作全
- 下一篇: Joomla 插件 attachment