Client:客户端
package socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* 聊天室的客户端
*/
public class Client {
/*
java.net.Socket 套接字
Socket封装了TCP协议的通讯细节,使用它可以和远端计算机建立连接
并且用两个流读写完成与远端计算机的数据交互
*/
private Socket socket;
/**
* 构造方法,用于初始化客户端的
*/
public Client(){
try {
/*
实例化Socket时需要传入两个参数:
参数1:服务端的IP地址信息,"localhost"是一种特殊的写法
表示本机地址
参数2:服务端打开的端口,数字取值范围1-65535之间
通过IP我们可以找到网络上的服务端所在计算机,通过端口可以
连接到该计算机的服务端应用程序
*/
System.out.println("正在连接服务端");
socket = new Socket("localhost",8088); //localhost
System.out.println("与服务端建立连接");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 客户端开始工作的方法
*/
public void start(){
try {
//启动用于读取服务端发送过来消息的线程
ServerHandler handler = new ServerHandler();
Thread t = new Thread(handler);
t.setDaemon(true);
t.start();
/*
Socket提供的方法
OutputStream getOutputStream()
该方法会返回一个字节输出流,通过这个输出流写出的字节
会发送给远端计算机
*/
OutputStream out = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw,true);
Scanner scanner = new Scanner(System.in);
while (true){
String line = scanner.nextLine();
if("exit".equals(line)){
break;
}
pw.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
/**
* 该线程负责循环读取服务端发送过来的消息
*/
private class ServerHandler implements Runnable{
public void run(){
try {
//通过socket获取输入流,读取服务端发送过来的消息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) !=null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
ServerSocket:服务端
package socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* 聊天室的服务端
*/
public class Server {
/*
运行在服务端的java.net.ServerSocket主要有两个工作:
1:向系统申请服务端口,客户端就是通过这个端口与服务端建立连接的
2:监听该服务端,一旦一个客户端连接就会自动创建一个Socket
相当于"接起一个电话".通过这个Socket与客户端交互
我们可以把Socket想象成电话,而ServerSocket则是总机
*/
private ServerSocket server;
/*
存所有客户端输出流的数组,供所有ClientHandler转发消息使用
*/
// private PrintWriter[] allOut = {};
private Collection<PrintWriter> allOut = new ArrayList<>();
public Server(){
try {
/*
实例化ServerSocket的同时需要指定服务端口,如果该端口
已经被其他程序占用时会抛出异常:
java.net.BindException:Address already in use
*/
System.out.println("正在启动服务端...");
server = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void start(){
try {
/*
ServerSocket提供的方法
Socket accept()
这个方法是一个阻塞方法,调用后就"卡住了",直到一个客户端连接
该方法会立刻返回一个Socket,通过这个Socket就可以
和刚建立连接的客户端交互了
*/
while (true) {
System.out.println("等待客户端连接...");
Socket socket = server.accept();
System.out.println("一个客户端连接了");
//启动一个线程来处理该客户端
ClientHandler handler = new ClientHandler(socket);
Thread t = new Thread(handler);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
/*
定义线程任务,该任务与指定的客户端进行互交
每当一个客户端连接,就会启动一个线程执行这个任务与该客户端互交
*/
private class ClientHandler implements Runnable{
private Socket socket;
private String host; //当前客户端的地址信息
public ClientHandler(Socket socket){
this.socket=socket;
//通过socket获取远端计算机的IP地址的字符串格式
host = socket.getInetAddress().getHostAddress();
}
public void run(){
PrintWriter pw = null;
try{
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
pw = new PrintWriter(bw,true);
//将该输出流存入共享数组allOut
synchronized (server) {
//1对allOut数组扩容
// allOut = Arrays.copyOf(allOut, allOut.length + 1);
// //将该输出流存入数组最后一个位置
// allOut[allOut.length - 1] = pw;
allOut.add(pw);
}
// System.out.println(host+"上线了当前在线人数:"+allOut.length);
System.out.println(host+"上线了当前在线人数:"+allOut.size());
String line;
while ((line = br.readLine()) != null) {
System.out.println(host+"说:" + line);
//遍历allOut数组,给所有客户端回复
synchronized (server) {
// for (int i = 0; i < allOut.length; i++) {
// allOut[i].println(host + "说:" + line);
// }
for (PrintWriter o : allOut){
o.println(host+"说"+line);
}
}
}
}catch (IOException e){
}finally {
//处理当客户端关闭的操作
//将当前客户端的输出流从共享数组allOut中删除
synchronized (server){
// for (int i=0;i<allOut.length;i++) {
// if (allOut[i] == pw) {
// allOut[i] = allOut[allOut.length - 1];
// allOut = Arrays.copyOf(allOut, allOut.length - 1);
// break;
// }
// }
allOut.remove(pw);
}
// System.out.println(host+"下线了,当前在线人数"+allOut.length);
System.out.println(host+"下线了,当前在线人数"+allOut.size());
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}