package day05;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
/**
* 聊天室服务端
* @author Administrator
*
*/
public class Server {
/**
* 运行在服务端的java.net.ServerSocket
* 主要有两个作用
* 1:向系统申请服务端口,客户端就是通过这个端口
* 与服务端建立连接的。
* 2:监听服务端口,等待客户端连接。一旦一个客户端
* 通过服务端口与服务端建立连接,那么ServerSocket
* 会主动创建一个Socket与客户端进行通讯。
*/
private ServerSocket server;
/*
* 由于ClientHandler是Server的内部,那么
* 在Server上定义的属性可以被ClientHandler的
* 实例访问,从而可以用于让所有ClientHandler
* 共享数据使用。
*/
private PrintWriter[] allOut = {};
/**
* 用来初始化服务端
*/
public Server(){
try{
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{
//当前线程通过这个Socket与对应客户端交互
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
PrintWriter pw = null;
try{
System.out.println("启动了一个线程处理客户端交互");
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
BufferedReader br = new BufferedReader(isr);
/*
* 通过Socket获取输出流,用于给客户端发送消息
*/
pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream(),"UTF-8"
)
),true
);
/*
* 将该输出流存入allOut,以便其他的ClientHandler
* 可以将消息发送给当前客户端
*/
synchronized(allOut) {
//1:先扩容allOut
allOut = Arrays.copyOf(allOut, allOut.length+1);
//2:将当前客户端输出流存入数组最后
allOut[allOut.length-1] = pw;
System.out.println("当前在线人数:"+allOut.length);
}
/*
* 当客户端与服务端断开连接时,不同系统的客户端
* 在服务端这边体现的不太一样。
* windows的客户端断开时,服务端这里br.readLine
* 方法通常会直接抛出异常。
* linux的客户端断开时,服务端br.readLine方法会
* 返回null.
*/
String message = null;
while((message = br.readLine()) != null) {
System.out.println("客户端说:"+message);
//将消息发送给客户端
pw.println("客户端所:"+message);
synchronized (allOut) {
//遍历allOut,转发消息
for(int i=0;i<allOut.length;i++){
allOut[i].println("客户端说:"+message);
}
}
}
}catch(Exception e){
}finally{
//处理客户端断开连接的操作
//将该客户端的输出流从共享数组中删除
synchronized(allOut) {
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;
}
}
}
System.out.println("当前在线人数:"+allOut.length);
//关闭socket,释放资源
if(socket != null) {
try {
socket.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
}
}
package day05;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 聊天室客户端
* @author Administrator
*
*/
public class Client {
/*
* java.net.Socket 套接字
* 封装了TCP协议通讯。使用它可以很方便的以TCP
* 协议为通讯方式进行数据传输。
*/
private Socket socket;
/**
* 构造方法,用来初始化客户端
*/
public Client(){
try {
/*
* 实例化Socket时需要传入两个参数
* 1:服务端IP地址
* 2:服务端端口号
* 通过IP地址可以找到服务端计算机,通过
* 端口可以找到运行在服务端计算机上的服务端
* 应用程序。
* 实例化的过程就是连接的过程,若连接失败
* 那么这里会抛出异常。
*/
System.out.println("正在连接服务器端...");
socket = new Socket("localhost",8088);
System.out.println("已连接!");
} catch(UnknownHostException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* 客户端开始工作的方法
*/
public void start() {
try{
//先启动读取服务端消息的线程
ServerHandler handler = new ServerHandler();
Thread t = new Thread(handler);
t.start();
BufferedReader br = new BufferedReader(
new InputStreamReader(
System.in
)
);
OutputStream out = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
BufferedWriter bs = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bs,true);
System.out.println("开始聊天吧!");
String message = null;
while(true) {
message = br.readLine();
pw.println(message);
}
}catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
/**
* 该线程负责读取服务端发送过来的消息
* @author adminitartor
*
*/
private class ServerHandler implements Runnable{
@Override
public void run() {
try{
//创建输入流读取服务端消息
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream(),"UTF-8"
)
);
String message = null;
while((message = in.readLine()) != null) {
System.out.println(message);
}
}catch(Exception e){
}
}
}
}