制作一个在同局域网下能够互相连接并实现群聊的程序:
以下是Client:
package FYX_Month02_Week02.day02.No3_Socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* 2021.09.28,星期二
* Author: BaoYang
* StudyPoint: 客户端聊天室
*/
public class Client {
/*
java.net.Socket:套接字
Socket封装了TCP协议的通讯细节,使得我们使用它可以与服务端建立网络连接,
并通过它获取两个流(一个输入流,一个输出流),然后使用这两个流的读写操作完成与服务端的数据交互。
*/
private Socket socket;
//构造方法:初始化客户端
public Client(){
try {
/*
实例化Socket时通常需要传入两个参数:
参数1:服务端的地址信息(IP地址,如果连接本机可以用localhost)。
参数2:服务端打开的服务端口,即服务端ServerSocket申请的端口。
*/
System.out.println("正在连接服务器……");
socket = new Socket("192.168.43.215",2222);
System.out.println("服务器连接成功!"+socket.getInetAddress());
} catch (IOException e) {
e.printStackTrace();
}
}
public void start(){
//客户端启动后,向县启动一个线程用来读取服务器端发送的消息
ServerHandler handler = new ServerHandler();
Thread t = new Thread(handler);
t.start();
try(OutputStream out = socket.getOutputStream();
PrintWriter pw =
new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,"UTF-8")),true);) {
/*
Socket提供的方法:[OutputStream] getOutputStream()
该方法会获取一个字节输出流,通过这个输出流写出的字节数据会通过网络发送给对方。
*/
Scanner sc = new Scanner(System.in);
System.out.println("开始聊天吧!单独输入88|拜拜则退出聊天室~");
while (true){
String line = sc.next();
if(line.equals("88")|line.equals("拜拜")){
System.out.println("本次聊天已结束,感谢使用聊天室~");
break;
}
pw.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class ServerHandler implements Runnable{
@Override
public void run() {
/*
Socket提供的方法:[InputStream] getInputStream():
通过该方法获取的字节输入流取的时远端计算机发送过来的数据。
这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。
*/
try (
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
){
String message;
while ((message= br.readLine())!=null){
System.out.println(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
}
以下是Server:
package FYX_Month02_Week02.day02.No3_Socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.stream.Stream;
/**
* 2021.09.28,星期二
* Author: BaoYang
* StudyPoint:
*/
public class Server {
/*
运行在服务器端的ServerSocket主要有两个作用:
1、向系统申请服务端口,客户端的Socket就是通过这个端口与服务器建立连接的。
2、监听服务端口,一旦一个客户通过该端口建立连接会自动创建一个Socket,服务端就可以通过这个Socket与客户端交互了。
如果我们把Socket比喻为电话,那么ServerSocket相当于是某客服中心的总机。
*/
private ServerSocket serverSocket;
/*
创建一个线程安全的集合,其中add,remove等方法上都直接使用了synchronized修饰,
锁个线程是不能同事调用一个集合的这些方法的,保证同步执行。
*/
private List<PrintWriter> c = Collections.synchronizedList(new ArrayList<>());
public Server() {
try {
System.out.println("正在启动服务端……");
/*
实例化时需要指定服务器端口,如果该端口被当前系统其他应用程序占据时,会抛出异常;
*/
serverSocket = new ServerSocket(222);
System.out.println("服务端连接成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
//构造方法:初始化客户端
public void start() {
try {
/*
ServerSocket提供的方法:
[Socket] accept():是一个阻塞方法,调用后程序进入阻塞状态,直到一个客户端
实例化Socket与当前服务端建立连接,此时accept方法会立即返回一个Socket实例,服务端通过它就可以与客户端交互了。
---
可以理解为这个动作相当于是总机的“接电话”操作。
*/
int i = 1;
while (true) {
System.out.println("等待客户端连接……");
Socket socket = serverSocket.accept();
System.out.println("第" + i + "个客户端连接成功!" + socket.getInetAddress());
i++;
//启动一个线程用来与客户端交互:
//1、创建线程任务:
ClientHandler handler = new ClientHandler(socket);
//2、创建一个线程执行该任务
Thread t = new Thread(handler);
//3、启动线程
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
该线程任务用于与指定的客户端进行交互
每个连接服务器的客户端都是通过线程进行交互的。
即:一个客户端靠一个线程进行交互
*/
private class ClientHandler implements Runnable {
private Socket socket;
private String address;//处理当前线程处理的客户端地址
public ClientHandler(Socket socket) {
this.socket = socket;
//通过socket获取远端计算机地址信息(对于服务器而言,远端就是客户端)
this.address = socket.getInetAddress().getHostAddress();
}
@Override
public void run() {
PrintWriter pw =null;
try {
/*
Socket提供的方法:[InputStream] getInputStream():
通过该方法获取的字节输入流取的时远端计算机发送过来的数据。
这里相当于是读取当前服务端中这个Socket对应的远端(客户端)那边Socket获取的输出流写出的字节数据。
*/
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true);
c.add(pw);
System.out.println(address+"上线了,当前在线人数:"+c.size());
//广播上线通知:
sendMessage(address+"上线了,当前在线人数:"+c.size());
BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String line;
while ((line = bw.readLine()) != null) {
System.out.println(address + "说:" + line);
//将消息回复给所有客户端
sendMessage(address + "说:" + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//下线时:移除对象并且广播
try {
c.remove(pw);
sendMessage(address+"下线了,当前在线人数:"+c.size());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将消息转发给所有客户端
* @param message:要发送的内容
*/
public void sendMessage(String message){
c.forEach(pw -> pw.println(message));
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}