服务器端
package com.atguigu.example;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
public class Server4 {
private static ArrayList<Socket> online = new ArrayList<Socket>();
public static void main(String[] args) throws IOException {
//1开启服务器
ServerSocket server = new ServerSocket(9999);
while (true){
//2、接收客户端连接
Socket socket = server.accept();
//客户端加到online
online.add(socket);
//每个客户端单独一个线程
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
}
//信息处理类
private static class MessageHandler extends Thread{
private Socket socket;
private String ip;
//构造器
public MessageHandler(Socket socket){
super();
this.socket = socket;
this.ip = socket.getInetAddress().getHostAddress();
}
public void run(){
sendToOthers(ip+"上线了");
//(1)接收当前的客户端发送的消息
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String content;
//br可以看做生成的缓存文件,而readLine用于从中提取出内容
while ((content = br.readLine()) != null){
//收到一句转发一句
sendToOthers(ip+"说:"+content);
if ("bye".equals(content)){
//给自己发一句bye
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println("bye");
break;
}
}
sendToOthers(ip+"下线了");
} catch (IOException e) {
sendToOthers(ip+"掉线了");
}
}
public void sendToOthers(String str){
//遍历所有的online客户端
Iterator<Socket> iterator = online.iterator();
//hasnext()方法阻塞等待用户输入,无参情况下永远返回true
while(iterator.hasNext()){
Socket on = iterator.next();
try {
if(!on.equals(socket)){//!只给其它客户端发送
OutputStream out = on.getOutputStream();
PrintStream ps = new PrintStream(out);
ps.println(str);
}
} catch (IOException e) {
//说明on这个客户可能下线或者掉线了
iterator.remove();
}
}
}
}
}
/*
Server的理解:
1、首先就得开服务器接口ServerSocket
2、之后创建Socket为ServerSocket的accept
3、socket的run()是我们的核心需求,可以对此创建一个专门的handler子类,继承Thread线程
4、在run里面需要的方法可以在类内构建,这里用的是输入流InputStream
5、InputStream(代表socket的输入流)→InputStreamReader(解码器,从字节流到字符流)→BufferedReader(看作缓存为文件)→br.readLine()(相当于读取文件中的内容)
6、如何发送内容给客户端?OutputStream→PrintStream(out) (打印流,将out的内容存入ps) → ps.println(str) 发送内容
7、注意可能的错误!用try catch来避免输入流和输出流可能的报错
*/
客户端
package com.atguigu.example;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client4 {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",9999);
//开启双线程,一个接收,一个发送
SendThread st = new SendThread(socket);
ReceiveThread rt = new ReceiveThread(socket);
st.start();
rt.start();
//join()方法是Thread类中的一个方法,该方法的定义是等待该线程终止。
//其实就是join()方法将挂起调用线程的执行,直到被调用的对象完成它的执行。这段话难理解,后面我会用实例做讲解。
try {
st.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
rt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
socket.close();
}
//发送线程
static class SendThread extends Thread{
private Socket socket;
public SendThread(Socket socket){
super();
this.socket = socket;
}
public void run(){
try {
Scanner input = new Scanner(System.in);
OutputStream out = socket.getOutputStream();//输出流,发送
PrintStream ps = new PrintStream(out);//打印流
while (true){
System.out.println("输入你想要输入的消息:");
String content = input.nextLine();
ps.println(content);
//输入bye结束发送
if("bye".equals(content)){
break;
}
}
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//接收线程
static class ReceiveThread extends Thread{
private Socket socket;
public ReceiveThread(Socket socket){
super();
this.socket = socket;
}
public void run(){
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
while (true){
String line = br.readLine();
if("bye".equals(line)){
break;
}
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
Client理解:客户端的发送和接收更为重要
1、开始我们就创建接口Socket,host代表客户,而端口对应我们要交互的服务器
2、发送和接收创建两个单独的类,通过设置构造器连接Socket,功能核心在于类内的run()
*/