概要
- 由于我的破电脑同时开浏览器和IDEA会卡死,只能把正文写到代码注释里了,感兴趣可以把代码复制下来运行一下。
- 一共有三个文件:服务器,客户端,工具类。
- 主要知识点:
- 1.多线程:runnable,thread
- 2.匿名表达式lambda
- 3.遍历容器:迭代器,foreach
- 4.基于TCP的简单编程
- 5.IO流的对接操作
- 6.最重要的面向对象封装思想
**
服务器
**
package com.sxt.chatForMy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.sxt.chatForMy.Server.group;
/**
* 服务器端:使用本地端口(8888)
*
* 主要功能:
* 1.接收消息
* 2.发送消息
* 3.自动释放资源
* 4.多人聊天与私聊
*
* 其中我们可以利用封装的思想:
* 1.封装服务对象(每个客户端即一个对象)
* >接收发送消息
* >私聊与多人聊天
* >借助工具类释放资源
* 2.封装一个工具类(用来释放资源)
* >有一个释放资源的方法
*/
public class Server {
public static CopyOnWriteArrayList<Channel> group = new CopyOnWriteArrayList<>();
public static void main(String[] args) throws IOException{
//创建本地服务器,端口8888
ServerSocket serverSocket = new ServerSocket(8888);
while (true){
Socket client = serverSocket.accept();//阻塞式等待客户端连接
System.out.println("当前有"+(group.size()+1)+"名用户连接");
Channel current = new Channel(client);
group.add(current);//加入容器
new Thread(current).start();//启动线程
}
}
}
//封装用户对象
class Channel implements Runnable{
private String username = "";//用户名
private DataInputStream dis;//从客户端输入到服务器的流
private DataOutputStream dos;//从服务器输出到客户端的流
private Socket client;//客户端
private boolean flag = true;//每个用户进来就会开辟一个线程,这个标志用来结束用户的线程
//构造器
public Channel(Socket client){
this.client = client;
//建立流
try{
this.dis = new DataInputStream(client.getInputStream());
this.dos = new DataOutputStream(client.getOutputStream());
}catch (IOException e){System.out.println("与客户端对接时错误");}
}
//从服务端向客户端发送消息
public void send(String msg){
try {
dos.writeUTF(msg);//发送信息
dos.flush(); //刷新缓存
}catch (IOException e){System.out.println("从服务端向客户端发送信息时错误");}
}
/**
*
*多人聊天实现思路:
* >将所有客户端装入容器,遍历容器后将
* 消息发送给除自己以外的其他所有人
* >对send()方法进行封装
* 私聊实现思路:
* >约定以 @用户名:消息内容 对该用户发送私密消息
* >封装send()方法
* 容器的选择:
* >见行26,有下面这条语句
* public static CopyOnWriteArrayList<Channel> group = new CopyOnWriteArrayList<>();
* >记得一定要静态导入
*/
public void sendOthers(String msg){
/**
* 这里分别演示了迭代器与foreach俩种方法遍历容器
*/
//私聊模式的实现
if(msg.startsWith("@")){
int flag = 0;
//清洗数据
msg = msg.substring(1);
System.out.println(msg);
String[] datas = msg.split(":");
System.out.println(datas[0]);
System.out.println(datas[1]);
//利用迭代器遍历容器
Iterator<Channel> groupList = group.iterator();
while (groupList.hasNext()){
Channel temp = groupList.next();
if (temp.username.equals(datas[0])){
temp.send(this.username+"@"+temp.username+":"+datas[1]);
flag = 1;
return ;
}
}if(0 == flag){
send("没有"+datas[0]+"这个用户");
}
return;
}
//多人聊天的实现
//foreach遍历容器
for (Channel temp:group) {
if(temp.username == this.username)
continue;
else
temp.send(this.username+":"+msg);//图片里这里有点问题,应该是加temp
}
}
//从客户端接收消息
//简单的IO流操作,没什么好说的
public String recive(){
String msg = "";
try {
msg = dis.readUTF();
}catch (IOException e){
System.out.println("接收消息错误");
return null;
}
return msg;
}
public void moveAll(){
new ToolClass().relaseAll(client,dis,dos);
flag = false;
group.remove(this);
}
/**
* 让程序实现Runnable接口,重写run方法
* 与客户端对接
*/
@Override
public void run() {
getUsername();
while (flag){
String msg = recive();
if(msg!=null){
sendOthers(msg);
}else {
moveAll();
break;
}
}
}
public void getUsername() {
try {
this.username = dis.readUTF();
}catch (IOException e){
System.out.println("错误的用户名");
}
}
}
客户端
package com.sxt.chatForMy;
import java.io.*;
import java.net.Socket;
/**
* 客户端与服务器建立连接
*/
public class Client001 {
public static void main(String[] args) throws IOException {
//连接服务器
Socket client = new Socket("localhost",8888);
//创建流
DataInputStream dis = new DataInputStream(client.getInputStream());
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//获取用户名
System.out.print("请输入一个用户名:");
String username = br.readLine();
dos.writeUTF(username);
dos.flush();
//挂起接收消息的线程
new Thread(()->{
try {
while (true){
System.out.println(dis.readUTF());
}
} catch (IOException e) {
System.out.println("连接服务器错误");
}
}).start();
//发消息的线程
while (true){
String msg = "";
msg = br.readLine();
dos.writeUTF(msg);
dos.flush();
}
}
}
工具类
package com.sxt.chatForMy;
import java.io.Closeable;
import java.io.IOException;
/**
* 可变参数个数传入
* 关闭时一定要判断是否为null
*/
public class ToolClass {
public void relaseAll(Closeable... targets){
for (Closeable temp:targets) {
try {
if(null!=temp){
temp.close();
}
}catch (IOException e){e.printStackTrace();}
}
}
}