服务端:
package JAVA_API.num18_socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Server {
private ServerSocket serverSocket ;
//建一个集合用来存放通过流写出给的客户数量,这个集合应是一个线程安全的.
private List allOutput = Collections.synchronizedList(new ArrayList<>());
//进行服务端的初始化:
public Server(){
try {
System.out.println("正在启动服务端...");
//初始化时给成员变量赋值,在()里填入数字申请端口号,用来连接服务器
//IllegalArgumentException: Port value out of range: 65536
//serverSocket = new ServerSocket(65536);
serverSocket = new ServerSocket(5050);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
try {
while (true) {
System.out.println("等待客户端连接...");
//然后在main方法里调用方法,先启服务端,在启动客户端,accept()获取到的socket的返回值是连接当前服务端的客户端的socket。客户端的socket是Output来的。
Socket socket = serverSocket.accept();//用来监听
System.out.println("一个客户端连接了...");
//启动一个线程来负责与客户端交互的操作:
//1.创建线程任务,传入的参数就是监听到的Socket,
ClientHandler handler = new ClientHandler(socket);
//2.创建一个线程并执行该任务
Thread thread1 = new Thread(handler);
//3.启动线程
thread1.start();
}
} catch(IOException e){
e.printStackTrace();
}
}
//写一个内部类来定义任务
private class ClientHandler implements Runnable {
//内部类可以有成员方法和变量
//声明一个成员变量和构造方法
private Socket socket;
private String host;//当前线程处理的客户端的地址信息
//通过传入的socket获取客户端的信息。
public ClientHandler(Socket socket){
this.socket = socket;
//通过socket获取远端计算机地址信息(对于服务器而言,远端就是客户端),地址可以用来拼上谁谁说的话
//获取客户端地址就是start()方法中监听到的socket即是客户端ip及端口,
host = socket.getInetAddress().getHostAddress();
}
@Override
public void run() {
//将pw放在这里是为了能在finally中看到并处理
PrintWriter pw = null;
//socket能获取的只有字节流
try {
InputStream inputstream = socket.getInputStream();
//想要读,先转换
InputStreamReader isr = new InputStreamReader(inputstream,"UTF-8");
BufferedReader br = new BufferedReader(isr);
//用这个流来写出数据给客户端
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8")),true);
allOutput.add(pw);
//host是客户地址,allOutput.size()客户上线人数
System.out.println(host + "上线了,当前的在线人数:" + allOutput.size());
//调用发送消息的方法把消息发送给所有客户
sendMessage(host + "上线了,当前的在线人数:" + allOutput.size());
String line ;
while((line = br.readLine()) != null){
//host是读到的客户端地址,读到的内容是line
System.out.println(host + "说:" + line);
//将消息回复给所有的客户端,想要实现群发的效果,可以把写出到这一句放到集合,需要把客户端放到集合里
//pw.println(line);不能实现群发
//将客户输入的信息发给所有用户
sendMessage(host + "说:" + line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//处理客户端断开后的操作
//1.从集合中移除
allOutput.remove(pw);
//移除之后给一个下线的通知
//host是客户地址,allOutput.size()客户上线人数
//2.广播下线通知
System.out.println(host + "下线了,当前的在线人数:" + allOutput.size());
try {
//3.关闭socket释放资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//发送消息的方法
private void sendMessage(String message){
allOutput.forEach(pw -> pw.println(message));
}
}
//main方法用于测试
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}
客户端:
package JAVA_API.num18_socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private Socket socket;
public Client(){
try {
System.out.println("正在连接服务器...");
socket = new Socket("192.168.43.196",5050);
System.out.println("成功连接服务器!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void start(){
//在写之前也需要
//客户端启动后,先启动一个线程来读取服务端发送过来的消息
ServerHandler handler = new ServerHandler();
Thread thread = new Thread(handler);
thread.start();
//把流放在try()
try(PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8")),true);
){
//定义一个输出流用于写出数据到
//OutputStream outputStream = socket.getOutputStream();
//PrintWriter按行写出,BufferedWriter缓冲字符输出流加快写出数据,OutputStreamWriter将字符转换成字节,
System.out.println("开始聊天吧!单独输入exit是退出!");
Scanner scanner = new Scanner(System.in);
while(true){
String line = scanner.nextLine();
if ("exit".equals(line)){
System.out.println("聊天结束");
break;
}
//将客户端控制台输入的信息发送到服务端,通过pw.Println()写出到服务端
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(){
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();
}
}



