项目介绍
该项目是一个用TCP协议实现的多人聊天室,存在一个服务器和多个客户端,总体的思路为-开启一个服务器用于等待其他客户端的连接,有客户端连接之后我们将客户端的socket保存在一个公共的列表中,开启一个线程用于接收和转发给各个连接到服务器的用户,创建一个客户端开启两个线程一个线程用于发送给服务器数据另一个线程用于接收服务器的数据。
项目前知识
当然要是想搞明白该项目我们需要一定的TCP知识:
socket:
- 用于创建客户端的对象
- socket(String host,int port) -需要传入连接的ip 和 端口
- close 用于释放资源
Serversocket:
- 用于创建服务端对象
- accept() 用于监听客户端的连接,返回连接的客户端的socket对象
- getinputStream() 用于获取输入流,读取接受到的数据
- close() 用于释放资源
在使用了socket连接之后,剩下的操作就和数据流之间的操作没有什么区别。
DataInputStream 和 DataOutputStream:
- 功能: 实现八种基本类型数据的输入/输出。 同时,也可实现字符串的输入/输出。
- DataInputStream 数据的字节输入流; DataOutputStream 数据的字节输出流
- 特点: 八种基本类型的数据在输入/输出时,会保持类型不变。
- 这种流特别适合在网络上实现基本类型数据和字符串的传递。
- readUTF() 方法接受String值作为参数,并使用修改后的UTF-8编码将其写入当前输出流.
- writeWTF() 方法将经过修改的UTF-8编码的数据读取到String中并返回
项目代码
聊天室一共分为两个类:
- Server_chat (用于创建服务器,用于接收其它客户端的消息,和分发给每个连接的客户端)
- client_chat (用于创建用户端 ,用于连接服务器,给服务器发送消息和接收服务器返回的消息)
sever_chat.java:
public class Sever_chat {
DataInputStream in;
DataOutputStream out;
Socket accept;
List<Socket> outlist;
public Sever_chat() {}
public Sever_chat(Socket accept, List<Socket> outlist) throws IOException {
this.accept = accept;
in = new DataInputStream(accept.getInputStream());
this.outlist = outlist;
}
public void service() {
outlist.add(accept);
new Thread(() -> {
try {
while (true) {
String s = in.readUTF();
System.out.println(s);
send_ms(s);
}
} catch (IOException e) {
outlist.remove(accept);
send_ms(accept.getInetAddress() + "离开了聊天室");
System.out.println(accept.getInetAddress() + "离开了聊天室");
} finally {
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public void send_ms(String s) {
try {
for (Socket socket : outlist) {
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF(s);
outputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
List<Socket> outlist = new ArrayList<Socket>();
ServerSocket serverSocket = new ServerSocket(9999);
while (true) {
Socket accept = serverSocket.accept();
Sever_chat server_file = new Sever_chat(accept, outlist);
System.out.println(accept.getInetAddress() + "进入聊天室");
server_file.service();
}
}
}
client_file.java
public class client_file {
private String ip;
private int port;
private Socket socket;
private String name;
public client_file(String ip, int port,String name) {
this.ip = ip;
this.port = port;
this.name=name;
}
public void client(){
try {
socket = new Socket(ip, port);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(socket.getInputStream());
Scanner scanner = new Scanner(System.in);
new Thread(()->{
try {
while (true) {
String s = scanner.nextLine();
out.writeUTF(name+":"+s);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
while (true) {
String s = in.readUTF();
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new client_file("localhost",9999,"小马").client();
}
}
如果想运行多个客户端只需要new client_file()对象就可以如我们创建一个新的类模拟创建一个新的用户:
package com.ithei.UDP_manychat;
public class feng_user {
public static void main(String[] args) {
new client_file("192.168.43.35",9999,"小冯").client();
}
}
运行效果:
服务器显示: 客户端:小冯界面 客户端:小马界面
总结:
该项目只要理解了socket之间的通信之后,就变得非常的简单就和我们平时操作流文件没有区别,唯一写的时候有一些困惑的是,创建多个客户端去连接服务器,我将服务器接收到的客户端socket的输出流对象保存在列表中然后在将消息转发给每个客户端的时候便利每个客户端的时候每次都讲消息多次转发给同一个客户端,我试用hashcode查看他们的值是否相同显示不相同但是还是每次将消息发送给了同一个用户,后来我给列表中保存每个连接对象的socket,在给每个客户端发送消息的时候使用socket获取到输出流对象然后发送就解决了这个问题。
|