1.收获
今天的课没有学新的知识,是接着昨天的demo做的,在昨天的的demo的基础上来实现实现群聊和私聊(带上昵称),这样就更加接近于我们生活中常用的 QQ,只是没有传递文件的功能,这个demo也更加贴近于我们的生活。这也体现了我们写的东西是有利于生活,使生活更加方便。知识改变生活,但是知识更能改变命运,知识会改变一个人思想和行为,使人变得文明。尽管自己的在课上没有听得太懂,但是自己还是能够去花些时间去理解,而不是得过且过,迷迷糊糊,只有这样才能达到自己当初想要的。
2.技术
(1)集合Map
(2)接口
(3)多线程
(4)Socket通信模式
3.技术的实际应用和实践
(1)集合Map
在这个demo中由于我们的昵称和Socket是两个不同的部分,但是我们有一种需求就要他们变成一一对应关系,但是他们并不是同一种类型的数据,将昵称和发送的内容放在一个集合里面Map<String,Socket>即可形成一一对应关系,当我们需要取出对应的东西我们可以利用Map里面的函数进行。
在我们这个demo中的应用
//保存所有用户信息(集合)
private Map<String, Socket>users=new HashMap<>();
(2)接口
我们在聊天时,群聊就是群聊,私聊就是私聊,当用户选择群聊时,就应该按照群聊的方式来发送消息,当选择私聊的时候就应该按照私聊的方式来发送消息,这种方式就相当于是一种规定,我们可以用接口来实现。
在我们这个demo中的应用
public interface Chat {
//登陆
String LOGIN="u+";
//私聊
String PRIVATE_FLAG="p+";
//群聊
String PUBLIC_FLAG="a+";
//分隔符
String SPLIT_FLAG="♥";
//成功的状态
String SUCCESS="1";
String FAILURE="-1";
}
(3)多线程
在处理接收和发送消息的时候怎末么处理两者的先后顺序
由于两者的先后顺序不是清楚的,所以就可以利用多线程来解决这个问题,可以把发消息当做主线程,也可当作子线程,接收消息也是一样的。由于在服务器端和客服端也要接收和发送消息,所以两者都要用多线程。
在我们这个demo中的应用(只是客户端,服务器端也是一样的,在这就不举出来了)
public class Client {
public static void main(String[] args) {
BufferedReader br=null;
PrintStream ps;
BufferedReader bufferedReader;
try {
Socket socket = new Socket("10.129.25.253", 8888);
///接收终端的输入流
br=new BufferedReader(new InputStreamReader(System.in));
//发送给服务器端
ps=new PrintStream(socket.getOutputStream());
//接收服务器端的返回的结果
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//登陆
while (true){
//接收终端输入信息
String line=JOptionPane.showInputDialog("请输入用户名:");
String loginStr=Chat.LOGIN+line+Chat.LOGIN;
ps.println(loginStr);
//接收服务器端的返回的结果
String result=bufferedReader.readLine();
//判断登陆结果
if(result.equals(Chat.SUCCESS)){
System.out.println("登陆成功!");
break;
}else{
System.out.println("用户名已存在 请重新登陆");
}
}
//登陆成功
//开启线程 处理服务器端传来的信息
new ClientThread(socket).start();
//接收终端输入 发给服务器端
String line=null;
while ((line=br.readLine())!=null){
//发送给服务器
ps.println(line);
}
}catch (IOException e){
}
}
}
创建子线程
class ClientThread extends Thread{
private Socket socket;
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=br.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络出错!");
}finally {
try {
if(br!=null) {
br.close();
}
if(socket!=null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
super.run();
}
}
我们这里将接收服务器传来的消息作为子线程,
(4)Socket通信模式
由于该项目是建立在网络编程的基础上的所以要用到Socket通信模式
首先我们要按照这个模式创建一个Server服务器
public class Server {
//用于保存每一个用户对应的姓名和Socket(也可以用单例设计模式)
public static UserManager manager=new UserManager();
public static void main(String[] args) {
try(ServerSocket serverSocket=new ServerSocket(8888)){
//监听所有的来连接的客服端
while(true){
Socket socket=serverSocket.accept();
//让子线程处理Socket
new ServerThread(socket).start();
}
}catch (IOException e){
}
}
}
在这个服务器中我们也需要由到多线程来处理接受和发送数据,在这里的子线程是由来接收用户的操作并判断用户的操作以及给用户发送消息,而主线程是用来监听所有的用户。
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader bufferedReader=null;
PrintStream ps=null;
try {
//登陆
//1.得到对应的客户端传来的输入流
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到对应输出流
ps=new PrintStream(socket.getOutputStream());
String line=null;
while((line=bufferedReader.readLine())!=null){
if(line.startsWith(Chat.LOGIN)&&line.endsWith(Chat.LOGIN)){
//获取名字
String name=line.substring(2,line.length()-2);
判断用户的操作
//判断这个用户是否已经登陆
if(Server.manager.islogin(name)==true){
ps.println(Chat.FAILURE);
}else{
//没有登陆
//保存当前的用户信息
ps.println(Chat.SUCCESS);
Server.manager.save(name,socket);
}
}
//判断是是否为私聊
else if(line.startsWith(Chat.PRIVATE_FLAG)&&line.endsWith(Chat.PRIVATE_FLAG)){
//获取信息
String msg=line.substring(2,line.length()-2);
//分割
String[] items=msg.split(Chat.SPLIT_FLAG);
//用户名
String name=items[0];
//聊天内容
String message=items[1];
//通过用户名找到对应的socket
Socket socket=Server.manager.socketbyname(name);
PrintStream printStream=new PrintStream(socket.getOutputStream());
//获取当前用户的名称
String currentName=Server.manager.namebysocket(socket);
printStream.println(currentName+"向你发来私聊:"+message);
}else if(line.startsWith(Chat.PUBLIC_FLAG)&&line.endsWith(Chat.PUBLIC_FLAG)){
//群聊
//处理数据
String msg=line.substring(2,(line.length()-2));
//获取当前用户的名称
String currentName=Server.manager.namebysocket(socket);
//遍历所有的用户
Collection<Socket> sockets=Server.manager.allUsers();
for(Socket s:sockets){
PrintStream tempps=new PrintStream(s.getOutputStream());
tempps.println(currentName+"发来的群聊:"+msg);
//tempps.close();
}
}else{
PrintStream printStream=new PrintStream(socket.getOutputStream());
printStream.println("发送消息的格式有误 请重新输入:");
}
}
} catch (IOException e) {
e.printStackTrace();
}
super.run();
}
}
这样我们服务器就创建好了,自需要我们来创建几个客服端就可以实现群聊了,但是要注意的是必须先运行服务器端,然后再是客服端。
class Client1 {
public static void main(String[] args) {
BufferedReader br=null;
PrintStream ps;
BufferedReader bufferedReader;
try {
Socket socket = new Socket("10.129.25.253", 8888);
//接收终端的输入流
br=new BufferedReader(new InputStreamReader(System.in));
//发送给服务器端
ps=new PrintStream(socket.getOutputStream());
//接收服务器端的返回的结果
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//登陆
while (true){
//接收终端输入
String line=JOptionPane.showInputDialog("请输入用户名:");
String loginStr=Chat.LOGIN+line+Chat.LOGIN;
ps.println(loginStr);
String result=bufferedReader.readLine();
//判断登陆结果
if(result.equals(Chat.SUCCESS)){
System.out.println("登陆成功!");
break;
}else{
System.out.println("用户名已存在 请重新登陆");
}
}
//登陆成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();
//接收终端输入
String line=null;
while ((line=br.readLine())!=null){
//发送给服务器
ps.println(line);
}
}catch (IOException e){
}
}
}
class Client2 {
public static void main(String[] args) {
BufferedReader br=null;
PrintStream ps=null;
BufferedReader bufferedReader=null;
try {
Socket socket = new Socket("10.129.25.253", 8888);
//接收终端的输入流
br=new BufferedReader(new InputStreamReader(System.in));
//发送给服务器端
ps=new PrintStream(socket.getOutputStream());
//接收服务器端的返回的结果
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//登陆
while (true){
//接收终端输入
String line=JOptionPane.showInputDialog("请输入用户名:");
String loginStr=Chat.LOGIN+line+Chat.LOGIN;
ps.println(loginStr);
//接收服务器端
String result=bufferedReader.readLine();
//判断登陆结果
if(result.equals(Chat.SUCCESS)){
System.out.println("登陆成功!");
break;
}else{
System.out.println("用户名已存在 请重新登陆");
}
}
//登陆成功
//开启线程处理服务器端的输入
new ClientThread(socket).start();
//接收终端输入
String line=null;
while ((line=br.readLine())!=null){
//发送给服务器
ps.println(line);
}
}catch (IOException e){
}
}
}
实现效果:
提示用户输入
群聊
私聊