处理多线程与网络编程最为经典的例子莫过于聊天室,那我就聊天室案例作为一个回顾。
首先,我们来看以下代码:
package MultiTCP;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 必须先启动再连接
* 1、创建服务器 指定端口 ServerSocket(int port)
* 2、接收客户端的连接 阻塞式
* 3、发送数据+接收数据
*
* 接收多个客户端
*/
@SuppressWarnings("all")
public class MultiServer {
public static void main(String[] args) throws IOException {
//1、创建服务器 指定端口
ServerSocket server = new ServerSocket(8888);
while(true)//死循环 一个accept 一个客户端
{
//2、接收客户端的连接
Socket socket = server.accept();
System.out.println("一个客户端建立连接");
//2、发送数据
String msg = "欢迎使用";
//3、输出流
/*BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()));
bw.write(msg);
bw.newLine();//一定要加行结束符,不然读不到数据
bw.flush();*/
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(msg);
dos.flush();
}
}
}
package MultiTCP;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 1、创建客户端 必须指定服务器+端口 此时就在连接
* Socket(String host,int port)
* 2、接收数据+发送数据
*/
@SuppressWarnings("all")
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、创建客户端 必须指定服务器+端口 此时就在连接
Socket client = new Socket("localhost",8888);
//2、接收数据
/*BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
String echo = br.readLine();//阻塞式方法
System.out.println(echo);*/
DataInputStream dis = new DataInputStream(client.getInputStream());
String echo = dis.readUTF();
System.out.println(echo);
}
}
以上代码存在的问题
服务器为一个客户端发送数据完毕才能连接下一个客户端。
因此,为了解决上述的问题,我们需要为服务器端创建多线程操作。
首先我们需要为聊天室添加发送数据和接收数据。
package CSNet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 创建服务器
*/
@SuppressWarnings("all")
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
Socket client = server.accept();
//写出数据
//输入流
DataInputStream dis = new DataInputStream(client.getInputStream());
String msg = dis.readUTF();
System.out.println("服务器收到"+msg);
//输出流
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("服务器发送给客户端"+msg);
dos.flush();
}
}
package CSNet;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 创建客户端:发送数据+接收数据
* 写出数据:输出流
* 读取数据:输入流
* 输入流与输出流在同一个线程内,因此我们应该让 输入流与输出流彼此独立
*/
@SuppressWarnings("all")
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket client = new Socket("localhost",9999);
//控制台的输入流
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
DataInputStream dis = new DataInputStream(client.getInputStream());
while(true)
{
String info = console.readLine();
//输出流
dos.writeUTF(info);
dos.flush();
//输入流
String msg = dis.readUTF();
System.out.println(msg);
}
}
}
以上的代码存在输入流与输出流在同一个线程内问题,因此我们应该让输入流与输出流彼此独立。
接下来我们是需要实现多线程,让输入流与输出流分离。对客户端实现多线程。
客户端发送数据
package ThreadNet;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 发送数据线程:用于发送数据
*/
public class Send implements Runnable{
//控制台的输入流
private BufferedReader console;
//管道的数据输出流
private DataOutputStream dos;
//控制线程标识
private boolean isRunning = true;
//初始化
public Send() {
console = new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket client)
{
this();
try {
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
}
}
//1、从控制台接收数据
private String getMsgFromConsole()
{
try {
return console.readLine();
} catch (IOException e) {
}
return "";
}
/**
* 1、从控制台接收数据
* 2、发送数据
*/
public void send()
{
String msg = getMsgFromConsole();
try {
if(null!=msg&& !msg.equals(""))
{
dos.writeUTF(msg);
dos.flush();//强制刷新
}
} catch (IOException e) {
isRunning = false;//发送失败,提示关闭线程
CloseUtil.closeAll(dos,console);//如果不能发送成功,直接关闭流。
}
}
@Override
public void run() {
//线程体
while(isRunning)
{
send();
}
}
}
客户端接收数据
package ThreadNet;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 接收线程:用于接收数据
*/
public class Receive implements Runnable{
//管道的数据输入流
private DataInputStream dis ;
//线程标识
private boolean isRunning = true;
public Receive() {
}
public Receive(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
}
//接收数据的方法
public String receive()
{
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
return msg;
}
@Override
public void run() {
//线程体
while(isRunning){
System.out.println(receive());
}
}
}
客户端
package ThreadNet;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 创建客户端:发送数据+接收数据
* 写出数据:输出流
* 读取数据:输入流
* 输入流与输出流在同一个线程内 应该独立出来
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket client = new Socket("localhost",9999);
new Thread(new Send(client)).start();//一条路径
new Thread(new Receive(client)).start();//一条路径
}
}
服务器
package ThreadNet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 创建服务器
*/
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
while(true){
Socket client = server.accept();
//写出数据
//输入流
DataInputStream dis = new DataInputStream(client.getInputStream());
//输出流
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
while(true)
{
String msg = dis.readUTF();
//System.out.println(msg);
dos.writeUTF("服务器收到数据并返回"+msg);
dos.flush();
}
}
}
}
关闭流
package ThreadNet;
import java.io.Closeable;
import java.io.IOException;
/**
* 关闭流的方法
*/
public class CloseUtil {
public static void closeAll(Closeable ... io)
{
for(Closeable temp:io)
{
if(null==temp)
{
try {
temp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
这个方仍然存在问题,服务器端只能够一个一个的接收,必须要等到上一条执行完,才能进入下一条,存在所谓的先后顺序,并不具备多线程的功能。因此我们也需要对服务器进行多线程。
服务器
package MultiServer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 创建服务器
*/
public class Server {
private List<MyChannel> all = new ArrayList<>();
public static void main(String[] args) throws IOException {
new Server().start();
}
public void start() throws IOException
{
ServerSocket server = new ServerSocket(9999);
while(true)
{
Socket client = server.accept();
MyChannel channel = new MyChannel(client);
all.add(channel);//统一管理
new Thread(channel).start();//一条道路
}
}
/**
* 内部类
* 一个客户端 一条道路
* 1、输入流
* 2、输出流
* 3、接收数据
* 4、发送数据
* @author Administrator
*
*/
class MyChannel implements Runnable
{
private DataInputStream dis;
private DataOutputStream dos;
private boolean isRunning = true;
private String name;
public MyChannel() {
}
//初始化
public MyChannel(Socket client)
{
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name = dis.readUTF();
//System.out.println(this.name);
this.send("欢迎进入聊天室");
sendOthers(this.name+"进入了聊天室",true);
} catch (IOException e) {
CloseUtil.closeAll(dos,dis);
isRunning = false;
}
}
//接收数据
private String receive()
{
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
CloseUtil.closeAll(dis);
isRunning = false;
all.remove(this);//移除自身
}
return msg;
}
//发送数据
private void send(String msg)
{
if(null==msg||msg.equals(""))
{
return;
}
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
CloseUtil.closeAll(dos);
isRunning = false;
all.remove(this);//移除自身
}
}
//发送给其他客户端
private void sendOthers(String msg,boolean sys)
{
//是否为私聊 约定
if(msg.startsWith("@")&& msg.indexOf(":")>-1)
{
//获取name
String name = msg.substring(1,msg.indexOf(":"));
String contant = msg.substring(msg.indexOf(":")+1);
for(MyChannel other:all)
{
if(other.name.equals(name))
{
other.send(this.name+"对你悄悄的说:"+contant);
}
}
}
else {
for(MyChannel other:all)
{
if(other ==this)
{
continue;
}
if(sys==true)//系统信息
{
other.send("系统信息:"+msg);
}
else {
//发送其它客户端
other.send(this.name+"对所有人说"+msg);
}
}
}
/*
//遍历容器
for(MyChannel others:all)
{
if(others == this)
{
continue;
}
//发送给其他客户端
others.send(msg);
}*/
}
@Override
public void run() {
while(isRunning)
{
sendOthers(receive(),false);
}
}
}
}
发送信息(供客服端使用)
package MultiServer;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 发送数据线程:用于发送数据
*/
public class Send implements Runnable{
//控制台输入流
private BufferedReader console;
//管道输出流
private DataOutputStream dos;
//控制线程标识
private boolean isRunning = true;
//名称
private String name;
//初始化
public Send() {
console = new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket client,String name)
{
this();
try {
dos = new DataOutputStream(client.getOutputStream());
this.name = name;
send(this.name);
} catch (IOException e) {
//e.printStackTrace();
}
}
//1、从控制台接收数据
private String getMsgFromConsole()
{
try {
return console.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* 1、从控制台接收数据
* 2、发送数据
*/
public void send(String msg)
{
try {
if(null!=msg&& !msg.equals(""))
{
dos.writeUTF(msg);
dos.flush();//强制刷新
}
} catch (IOException e) {
//e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dos,console);
}
}
@Override
public void run() {
while(isRunning)
{
send( getMsgFromConsole());
}
}
}
接收信息(供客服端使用)
package MultiServer;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 接收线程:用于接收数据
*/
public class Receive implements Runnable{
//管道的数据输入流
private DataInputStream dis ;
//线程标识
private boolean isRunning = true;
public Receive() {
}
public Receive(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
}
//接收数据的方法
public String receive()
{
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
return msg;
}
@Override
public void run() {
//线程体
while(isRunning){
System.out.println(receive());
}
}
}
客户端
package MultiServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 创建客户端:发送数据+接收数据
* 写出数据:输出流
* 读取数据:输入流
* 输入流与输出流在同一个线程内 应该独立出来
* 加入名称
*/
public class Client {
public static void main(String[] args) throws IOException
{
System.out.println("请输入用户名:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String name = br.readLine();
if(name.equals(""))
{
return;
}
Socket client = new Socket("localhost",9999);
new Thread(new Send(client,name)).start();//一条路径
new Thread(new Receive(client)).start();//一条路径
}
}
关闭流
package MultiServer;
import java.io.Closeable;
import java.io.IOException;
/**
* 关闭流的方法
*/
public class CloseUtil {
public static void closeAll(Closeable ... io)
{
for(Closeable temp:io)
{
if(null==temp)
{
try {
temp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
Client1
Client2