Top

JAVA SE02 Unit05

  1. 测试使用ExecutorService实现线程池
  2. 测试BlockingQueue的使用
  3. 聊天室案例V1
  4. 聊天室案例V2

1 测试使用ExecutorService实现线程池

1.1 问题

使用ExecutorService实现线程池,详细要求如下:

1) 线程池要执行的任务为每隔一秒输出一次当前线程的名字,总计输出10次。

2) 创建一个线程池,该线程池中只有两个空线程。

3) 使线程池执行5次步骤一的任务。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建类

首先,创建名为TestExecutorService的类;然后,在该类相同的文件中创建名为Handler的类,该类实现了Runnable接口,是线程池要执行的任务,代码如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试线程池
 */
public class TestExecutorService {
public static void main(String[] args) {
		
}
}
class Handler implements Runnable{
public void run(){
		
	 }
}

步骤二:覆盖run方法

在Handler类中覆盖run方法,在该方法中实现每隔一秒输出一次当前线程的名字,总计输出10次,代码如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试线程池
 */
public class TestExecutorService {
	public static void main(String[] args) {
	}
}
class Handler implements Runnable{
	public void run(){
#cold_bold		String name = Thread.currentThread().getName();
#cold_bold		System.out.println("执行当前任务的线程为:"+name);
#cold_bold		for(int i=0;i<10;i++){		
#cold_bold			System.out.println(name+":"+i);
#cold_bold			try {
#cold_bold				Thread.sleep(1000);
#cold_bold			} catch (InterruptedException e) {
#cold_bold				e.printStackTrace();
#cold_bold			}
#cold_bold		}
#cold_bold		System.out.println(name+":任务完毕");
	}
}

步骤三:创建线程池

使用Executors类的静态方法newFixedThreadPool,创建一个包含两个空线程的线程池,代码如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试线程池
 */
public class TestExecutorService {
	public static void main(String[] args) {
#cold_bold		ExecutorService threadPool = Executors.newFixedThreadPool(2);
	}
}
class Handler implements Runnable{
	public void run(){
		String name = Thread.currentThread().getName();
		System.out.println("执行当前任务的线程为:"+name);
		for(int i=0;i<10;i++){		
			System.out.println(name+":"+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(name+":任务完毕");
	}
}

步骤四:设置线程池要执行的任务

使用ExecutorService类的execute方法设置线程池要执行的任务,在此线程池要执行的任务即为Handler类中创建的任务;另外,要执行5次该任务,那么循环5次即可,代码如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试线程池
 */
public class TestExecutorService {
	public static void main(String[] args) {
		ExecutorService threadPool = Executors.newFixedThreadPool(2);
#cold_bold		for(int i=0;i<5;i++){
#cold_bold			Handler handler = new Handler();
#cold_bold			threadPool.execute(handler);
#cold_bold		}
	}
}
class Handler implements Runnable{
	public void run(){
		String name = Thread.currentThread().getName();
		System.out.println("执行当前任务的线程为:"+name);
		for(int i=0;i<10;i++){		
			System.out.println(name+":"+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(name+":任务完毕");
	}
}

运行上述代码,控制台输出的结果如下:

执行当前任务的线程为:pool-1-thread-1
执行当前任务的线程为:pool-1-thread-2
pool-1-thread-2:0
pool-1-thread-1:0
pool-1-thread-1:1
pool-1-thread-2:1
pool-1-thread-1:2
pool-1-thread-2:2
pool-1-thread-2:3
pool-1-thread-1:3
pool-1-thread-2:4
pool-1-thread-1:4
pool-1-thread-1:5
pool-1-thread-2:5
pool-1-thread-1:6
pool-1-thread-2:6
pool-1-thread-1:7
pool-1-thread-2:7
pool-1-thread-2:8
pool-1-thread-1:8
pool-1-thread-2:9
pool-1-thread-1:9
pool-1-thread-1:任务完毕
执行当前任务的线程为:pool-1-thread-1
pool-1-thread-1:0
pool-1-thread-2:任务完毕
执行当前任务的线程为:pool-1-thread-2
pool-1-thread-2:0
pool-1-thread-1:1
pool-1-thread-2:1
pool-1-thread-1:2
pool-1-thread-2:2
pool-1-thread-1:3
pool-1-thread-2:3
pool-1-thread-1:4
pool-1-thread-2:4
pool-1-thread-1:5
pool-1-thread-2:5
pool-1-thread-1:6
pool-1-thread-2:6
pool-1-thread-1:7
pool-1-thread-2:7
pool-1-thread-1:8
pool-1-thread-2:8
pool-1-thread-1:9
pool-1-thread-2:9
pool-1-thread-1:任务完毕
执行当前任务的线程为:pool-1-thread-1
pool-1-thread-1:0
pool-1-thread-2:任务完毕
pool-1-thread-1:1
pool-1-thread-1:2
pool-1-thread-1:3
pool-1-thread-1:4
pool-1-thread-1:5
pool-1-thread-1:6
pool-1-thread-1:7
pool-1-thread-1:8
pool-1-thread-1:9
pool-1-thread-1:任务完毕

从输出结果可以看出,线程池每次启动两个线程来执行任务。由于要求执行5次任务,所以线程池分三次执行,前两次各执行2个任务,最后一次执行一个任务。

1.3 完整代码

本案例中,类TestExecutorService的完整代码如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 测试线程池
 */
public class TestExecutorService {
	public static void main(String[] args) {
		ExecutorService threadPool = Executors.newFixedThreadPool(2);
		for(int i=0;i<5;i++){
			Handler handler = new Handler();
			threadPool.execute(handler);
		}
	}
}
class Handler implements Runnable{
	public void run(){
		String name = Thread.currentThread().getName();
		System.out.println("执行当前任务的线程为:"+name);
		for(int i=0;i<10;i++){		
			System.out.println(name+":"+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(name+":任务完毕");
	}
}

2 测试BlockingQueue的使用

2.1 问题

测试BlockingQueue的使用,详细要求如下:

1) 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,循环20次向队列queue中添加元素,如果5秒内元素仍没有入队到队列中,则返回false。

2) 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,将0到9,10个数字加入到队列queue中;最后,循环20次从队列queue中取元素,如果5秒内还没有元素可取出则返回null。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建双缓冲队列

首先,创建TestBlockingQueue类;然后在该类中添加测试方法testOffer;最后,

使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue,代码如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * 测试双缓冲队列
 */
public class TestBlockingQueue {
	/**
	 * 测试入队方法
	 */
	@Test
	public void testOffer() {
		BlockingQueue <Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
	}
}

步骤二:测试offer方法

使用双缓冲队列BlockingQueue的offer方法向队列queue中添加元素,代码如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * 测试双缓冲队列
 */
public class TestBlockingQueue {
	/**
	 * 测试入队方法
	 */
	@Test
	public void testOffer() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
#cold_bold		for(int i=0;i<20;i++){
#cold_bold			try {
#cold_bold				//设置5秒超时,5秒内元素仍没有入队到队列中,则返回false
#cold_bold				boolean b = queue.offer(i,5,TimeUnit.SECONDS);
#cold_bold				System.out.println("存入是否成功:"+b);
#cold_bold			} catch (InterruptedException e) {
#cold_bold				e.printStackTrace();
#cold_bold			}
#cold_bold		}	
	 }
}

上述代码中的offer方法的三个参数分别表示入队元素、超时时长以及超时的时间单位。

运行上述代码,控制台输出结果如下所示:

存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:true
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false
存入是否成功:false

从输出结果可以看出,输出了10次“存入是否成功:true”、10次“存入是否成功:false”,这是因为双缓冲队列的大小为10,入队10个元素后,则不能再有元素入队了。

步骤三:创建测试方法testPull

首先在TestBlockingQueue类中添加测试方法testPull;然后,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue,代码如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * 测试双缓冲队列
 */
public class TestBlockingQueue {
	/**
	 * 测试入队方法
	 */
	@Test
	public void testOffer() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
		for(int i=0;i<20;i++){
			try {
				//设置5秒超时,5秒内元素仍没有入队到队列中,则返回false
				boolean b = queue.offer(i,5,TimeUnit.SECONDS);
				System.out.println("存入是否成功:"+b);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
	}
	/**
	 * 测试出队方法
	 */
#cold_bold	@Test
#cold_bold	public void testPull() {
#cold_bold		BlockingQueue<Integer> queue 
#cold_bold			= new ArrayBlockingQueue<Integer>(10);
#cold_bold	}
}

步骤四:测试poll方法

首先,使0到9,10个数字加入到队列queue中;然后,使用双缓冲队列BlockingQueue的poll方法使queue中的元素出队,如果5秒内还没有元素可取出则返回null,代码如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * 测试双缓冲队列
 */
public class TestBlockingQueue {
	/**
	 * 测试入队方法
	 */
	@Test
	public void testOffer() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
		for(int i=0;i<20;i++){
			try {
				//设置5秒超时,5秒内元素仍没有入队到队列中,则返回false
				boolean b = queue.offer(i,5,TimeUnit.SECONDS);
				System.out.println("存入是否成功:"+b);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
	}
	/**
	 * 测试出队方法
	 */
	@Test
	public void testPull() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
#cold_bold		for(int i=0;i<10;i++){
#cold_bold			queue.offer(i);
#cold_bold		}	
#cold_bold		for(int i =0;i<20;i++){
#cold_bold			//获取元素,设置5秒超时,5秒内还没有元素可取出则返回null
#cold_bold			try {
#cold_bold				Integer num = queue.poll(5, TimeUnit.SECONDS);
#cold_bold				System.out.println("元素:"+num);
#cold_bold			} catch (InterruptedException e) {
#cold_bold				e.printStackTrace();
#cold_bold			}
#cold_bold		}
	}
}

运行上述代码,控制台输出的结果如下所示:

元素:0
元素:1
元素:2
元素:3
元素:4
元素:5
元素:6
元素:7
元素:8
元素:9
元素:null
元素:null
元素:null
元素:null
元素:null
元素:null
元素:null
元素:null
元素:null
元素:null

从输出结果可以看出,输出了10次“元素:+数字”、10次“元素:null”,这是因为双缓冲队列中只有10个元素,全部取出后,再取则返回null。

2.3 完整代码

本案例中,类TestBlockingQueue的完整代码如下所示:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * 测试双缓冲队列
 */
public class TestBlockingQueue {
	/**
	 * 测试入队方法
	 */
	@Test
	public void testOffer() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
		for(int i=0;i<20;i++){
			try {
				//设置5秒超时,5秒内元素仍没有入队,则返回false
				boolean b = queue.offer(i,5,TimeUnit.SECONDS);
				System.out.println("存入是否成功:"+b);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
	}
	/**
	 * 测试出队方法
	 */
	@Test
	public void testPull() {
		BlockingQueue<Integer> queue 
			= new ArrayBlockingQueue<Integer>(10);
		for(int i=0;i<10;i++){
			queue.offer(i);
		}	
		for(int i =0;i<20;i++){
			//获取元素,设置5秒超时,5秒内还没有元素可取出则返回null
			try {
				Integer num = queue.poll(5, TimeUnit.SECONDS);
				System.out.println("元素:"+num);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

3 聊天室案例V1

3.1 问题

使用Java的Socket实现客户端和服务器端之间的连接,并使客户端向服务端发送一条消息。

通信过程如表-1所示:

表- 1 客户端与服务器端通信过程

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建客户端类

新建名为com.tarena.part1的包,并在包下新建名为Client的类,用于表示客户端,代码如下所示:

package com.tarena.part1;

/**
 * 客户端应用程序
 * 第一步:实现向服务器发送一条信息
*/
public class Client {
}

步骤二:创建Socket类的对象

在Client 类中声明全局变量 socket 表示一个客户端Socket对象,并在实例化 Client 类时使用构造方法“Socket(String ip,int port)”来创建Socket类的对象。此时,需要进行异常处理。代码如下所示:

package com.tarena.part1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 客户端应用程序
 * 第一步:实现向服务器发送一条信息
*/
public class Client {
	//客户端Socket
#cold_bold	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
#cold_bold	public Client(){
#cold_bold		try {
#cold_bold			socket = new Socket("localhost",8088);
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		}
#cold_bold	}
}

步骤三:创建客户端工作方法 start()

创建客户端工作方法 start(),并添加代码实现连接服务器端并发送信息。

首先使用Socket类的getOutputStream方法获取对应Socket对象的网络字节输出流对象;然后,为了写出数据,构造缓冲字符输出流PrintWriter类的对象,使用该对象的println方法向服务器发送数据。

注意,这里需要进行异常处理,并在 finally 语句中,关闭 Socket 对象。

Client 类的代码如下所示:

package com.tarena.part1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 客户端应用程序
 * 第一步:实现向服务器发送一条信息
*/
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
#cold_bold	public void start(){
#cold_bold		try {
#cold_bold			OutputStream out = socket.getOutputStream();
#cold_bold			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
#cold_bold			PrintWriter pw = new PrintWriter(osw,true);
#cold_bold			pw.println("你好!服务器");
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		} finally{
#cold_bold			if(socket != null){
#cold_bold				try {
#cold_bold					socket.close();
#cold_bold				} catch (IOException e) {
#cold_bold					e.printStackTrace();
#cold_bold				}
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤四:为客户端类定义 main() 方法

为类 Client 定义 main() 方法,并在 main() 方法中,创建 Client 对象,调用上一步中所创建的 start() 方法。代码如下所示:

package com.tarena.part1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 客户端应用程序
 * 第一步:实现向服务器发送一条信息
*/
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
	public void start(){
		try {
			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
			PrintWriter pw = new PrintWriter(osw,true);
			pw.println("你好!服务器");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
#cold_bold	public static void main(String[] args) {
#cold_bold		Client client = new Client();
#cold_bold		client.start();
#cold_bold	}
}

步骤五:创建服务器端类

新建名为Server的类,用于表示服务器端,代码如下所示:

package com.tarena.part1;

/**
 * 服务端应用程序
 */
public class Server {
}

步骤六:创建ServerSocket类的对象

在Server 类中声明全局变量 serverSocket 表示一个服务器端ServerSocket对象,并在实例化 Server 类时使用构造方法“ServerSocket(int port)”来创建对象。此时,需要进行异常处理。代码如下所示:

package com.tarena.part1;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
#cold_bold	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
#cold_bold	public Server(){
#cold_bold		try {
#cold_bold			serverSocket = new ServerSocket(8088);
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		}
#cold_bold	}
}

步骤七:创建服务器端工作方法 start()

创建服务器端工作方法 start(),用于读取客户端发来的信息。

首先监听客户端的连接,得到Socket 对象,并使用Socket类的getInputStream方法获取对应Socket对象的网络字节输入流对象;然后,为了读取数据,构造缓冲字符输入流BufferedReader类的对象,并调用该对象的 readLine 方法获取客户端发来的数据。

注意,这里需要进行异常处理,并在 finally 语句中,关闭 Socket 对象。

Server 类的代码如下所示:

package com.tarena.part1;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
#cold_bold	public void start(){
#cold_bold		try {
#cold_bold			System.out.println("等待客户端连接...");
#cold_bold			//监听客户端的连接
#cold_bold			Socket socket = serverSocket.accept();
#cold_bold			System.out.println("客户端已连接!");
#cold_bold			InputStream in = socket.getInputStream();
#cold_bold			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
#cold_bold			BufferedReader br = new BufferedReader(isr);
#cold_bold			System.out.println("客户端说:"+br.readLine());
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		}
#cold_bold	}
}

步骤八:为服务器端类定义 main 方法

为类 Server 定义 main 方法,并在 main 方法中,创建 Server 对象,调用上一步中所创建的 start 方法。代码如下所示:

package com.tarena.part1;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
	public void start(){
		try {
			System.out.println("等待客户端连接...");
			//监听客户端的连接
			Socket socket = serverSocket.accept();
			System.out.println("客户端已连接!");
			InputStream in = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
			BufferedReader br = new BufferedReader(isr);
			System.out.println("客户端说:"+br.readLine());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
#cold_bold	public static void main(String[] args) {
#cold_bold		Server server = new Server();
#cold_bold		server.start();
#cold_bold	}
}

步骤九:测试

首先,启动服务器端;然后再启动客户端。

服务器端控制台运行输出如下:

等待客户端连接...
客户端已连接!
客户端说:你好!服务器

3.3 完整代码

本案例中,Client类的完整代码如下所示:

package com.tarena.part1;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 客户端应用程序
 * 第一步:实现向服务器发送一条信息
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
	public void start(){
		try {
			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
			PrintWriter pw = new PrintWriter(osw,true);
			pw.println("你好!服务器");
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
}

Server类的完整代码如下所示:

package com.tarena.part1;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
	public void start(){
		try {
			System.out.println("等待客户端连接...");
			//监听客户端的连接
			Socket socket = serverSocket.accept();
			System.out.println("客户端已连接!");
			InputStream in = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
			BufferedReader br = new BufferedReader(isr);
			System.out.println("客户端说:"+br.readLine());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
}

4 聊天室案例V2

4.1 问题

改善聊天室案例V1,实现客户端重复发送数据到服务器端的功能。即,用户可以在控制台不断输入内容,并将内容逐一发送给服务端。

4.2 方案

此案例在上一个案例的基础上修改部分功能即可。

首先,对于客户端而言,为了能够重复发送信息,需要构建循环,并在循环中,不断读入控制台录入的数据并发送。代码如下所示:

//创建Scanner读取用户输入内容
Scanner scanner = new Scanner(System.in);
while(true){
		//pw 为PrintWriter 对象
		pw.println(scanner.nextLine());
}

其次,对于服务器端,也需要构建循环,并在循环中不断读取客户端发来的数据并打印显示。代码如下所示:

//循环读取客户端发送的信息
while(true){
		//br 为BufferedReader 对象
		System.out.println("客户端说:"+br.readLine());
}

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建客户端类

新建名为com.tarena.part2的包,并在包下新建名为Client的类,用于表示客户端。在Client 类中声明全局变量 socket 表示一个客户端Socket对象,并在实例化 Client 类时创建Socket类的对象。代码如下所示:

package com.tarena.part2;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 * 第二步:实现向服务器循环发送信息
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

步骤二:创建客户端工作方法 start()

创建客户端工作方法 start(),并添加代码实现连接服务器端并发送信息。为了能够重复发送信息,需要构建循环,并在循环中,不断读入控制台录入的数据并发送。

Client 类的代码如下所示:

package com.tarena.part2;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 * 第二步:实现向服务器循环发送信息
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
#cold_bold	public void start(){
#cold_bold		try {
#cold_bold			OutputStream out = socket.getOutputStream();
#cold_bold			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
#cold_bold			PrintWriter pw = new PrintWriter(osw,true);
#cold_bold			
#cold_bold			//创建Scanner读取用户输入内容
#cold_bold			Scanner scanner = new Scanner(System.in);
#cold_bold			while(true){
#cold_bold				pw.println(scanner.nextLine());
#cold_bold			}
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		} finally{
#cold_bold			if(socket != null){
#cold_bold				try {
#cold_bold					socket.close();
#cold_bold				} catch (IOException e) {
#cold_bold					e.printStackTrace();
#cold_bold				}
#cold_bold			}
#cold_bold		}
#cold_bold	}
}

步骤三:为客户端类定义 main() 方法

为类 Client 定义 main() 方法,并在 main() 方法中,创建 Client 对象,调用上一步中所创建的 start() 方法。代码如下所示:

package com.tarena.part2;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 * 第二步:实现向服务器循环发送信息
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
	public void start(){
		try {
			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
			PrintWriter pw = new PrintWriter(osw,true);
			
			//创建Scanner读取用户输入内容
			Scanner scanner = new Scanner(System.in);
			while(true){
				pw.println(scanner.nextLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
#cold_bold	public static void main(String[] args) {
#cold_bold		Client client = new Client();
#cold_bold		client.start();
#cold_bold	}
}

步骤四:创建服务器端类

新建名为Server的类,用于表示服务器端,并在Server 类中声明全局变量 serverSocket 表示一个服务器端ServerSocket对象,并在实例化 Server 类时创建该对象。此时,需要进行异常处理。代码如下所示:

package com.tarena.part2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

步骤五:创建服务器端工作方法 start()

创建服务器端工作方法 start(),用于读取客户端发来的信息。需要构建循环,并在循环中不断读取客户端发来的数据并打印显示。

Server 类的代码如下所示:

package com.tarena.part2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
	public void start(){
#cold_bold		try {
#cold_bold			System.out.println("等待客户端连接...");
#cold_bold			//监听客户端的连接
#cold_bold			Socket socket = serverSocket.accept();
#cold_bold			System.out.println("客户端已连接!");
#cold_bold			InputStream in = socket.getInputStream();
#cold_bold			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
#cold_bold			BufferedReader br = new BufferedReader(isr);
#cold_bold			
#cold_bold			//循环读取客户端发送的信息
#cold_bold			while(true){
#cold_bold				System.out.println("客户端说:"+br.readLine());
#cold_bold			}
#cold_bold		} catch (Exception e) {
#cold_bold			e.printStackTrace();
#cold_bold		}
	}	
}

步骤六:为服务器端类定义 main 方法

为类 Server 定义 main 方法,并在 main 方法中,创建 Server 对象,调用上一步中所创建的 start 方法。代码如下所示:

package com.tarena.part2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
	public void start(){
		try {
			System.out.println("等待客户端连接...");
			//监听客户端的连接
			Socket socket = serverSocket.accept();
			System.out.println("客户端已连接!");
			InputStream in = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
			BufferedReader br = new BufferedReader(isr);
			
			//循环读取客户端发送的信息
			while(true){
				System.out.println("客户端说:"+br.readLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
#cold_bold	public static void main(String[] args) {
#cold_bold		Server server = new Server();
#cold_bold		server.start();
#cold_bold	}
}

步骤七:测试

首先,启动服务器端。此时,服务器端将等待客户端的连接和数据发送。服务器端控制台输出如图-1所示:

图-1

然后再启动客户端,并在客户端重复输入文本后回车。界面效果如图-2所示:

图-2

查看服务器端的控制台输出,界面效果如图-3所示:

图-3

4.4 完整代码

本案例中,Client类的完整代码如下所示:

package com.tarena.part2;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端应用程序
 * 第二步:实现向服务器循环发送信息
 */
public class Client {
	//客户端Socket
	private Socket socket;
	/**
	 * 构造方法,用于初始化
	 */
	public Client(){
		try {
			socket = new Socket("localhost",8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端工作方法
	 */
	public void start(){
		try {
			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
			PrintWriter pw = new PrintWriter(osw,true);
			
			//创建Scanner读取用户输入内容
			Scanner scanner = new Scanner(System.in);
			while(true){
				pw.println(scanner.nextLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			if(socket != null){
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
}

Server类的完整代码如下所示:

package com.tarena.part2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务端应用程序
 */
public class Server {
	//服务端Socket
	private ServerSocket serverSocket;
	/**
	 * 构造方法,用于初始化
	 */
	public Server(){
		try {
			serverSocket = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 服务端开启方法
	 */
	public void start(){
		try {
			System.out.println("等待客户端连接...");
			//监听客户端的连接
			Socket socket = serverSocket.accept();
			System.out.println("客户端已连接!");
			InputStream in = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(in,"UTF-8");
			BufferedReader br = new BufferedReader(isr);
			
			//循环读取客户端发送的信息
			while(true){
				System.out.println("客户端说:"+br.readLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
}