上一篇:Java Socket 单线程的简单入门案例
1. 目录结构
2. StartClient.java源码
package socket.thread;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class StartClient {
/**
* 需要先启动服务端StartServer,再运行客户端StartClient
* 请注意,由于线程运行的不确定性,打印输出语句的次序是不确定的。。。
*
* 但是,不论输出语句的次序如何变化,客户端总有三个线程请求到服务端,
* 并且服务器端在处理完请求后会关闭Socker和IO。
*
* @param args
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException {
int threadNo = 0;
// 设置连接地址类
InetAddress addr = InetAddress.getByName("localhost");
// 从循环中生成3个客户端,每个客户端会开启一个通讯线程,每个线程首先将先向服务器端发送"Hello Server,My id is "的字符串,然后发送”byebye”,终止该线程的通讯。
for (threadNo = 0; threadNo < 3; threadNo++) {
new SocketClient(addr);
}
}
}
3. SocketClient.java源码
package socket.thread;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class SocketClient extends Thread {
// 客户端的socket
private Socket socket;
// 线程统计数,用来给线程编号
private static int cnt = 0;
// 客户端id
private int clientId = cnt++;
// io对象
private BufferedReader in;
private PrintWriter out;
// 构造函数,在创建该实体时执行
public SocketClient(InetAddress inetAddress) {
try {
// 通过inetAddress和端口号初始化Socket对象
socket = new Socket(inetAddress, 3333);
// 实例化IO对象
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
// 开启线程
start();
} catch (IOException e) {
e.printStackTrace();
try {
// 出现异常,关闭socket
socket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
// 线程主体方法
public void run() {
try {
// 向Socket信道上传输本客户端的ID号
out.println("Hello Server,My id is " + clientId);
// 收到服务端发来的信息,打印出来
String str = in.readLine();
System.out.println(str);
// 向服务器端发送“byebye”结束本次通信
out.println("byebye");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
try {
// 出现异常,关闭socket
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4. StartServer.java源码
package socket.thread;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端运行类
*/
public class StartServer {
// 服务器监听口号
static final int portNo = 3333;
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 根据端口号,创建一个ServerSocket类型的服务器端的Socket,用来同客户端通讯。
ServerSocket serverSocket = new ServerSocket(portNo);
System.out.println("The Server is start: " + serverSocket);
try {
for (;;) {
// 阻塞,直到有客户端连接
Socket socket = serverSocket.accept();
// 通过构造函数,启动线程
new SocketServer(socket);
}
} finally {
// 关闭socket
serverSocket.close();
}
}
}
5. SocketServer.java源码
package socket.thread;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
/**
* 使用套接字连接多个客户机
* 如果我们在通过程序里引入多线程的机制,可让一个服务器端同时监听并接收多个客户端的请求,并同步地为它们提供通讯服务。
* 基于多线程的通讯方式,将大大地提高服务器端的利用效率,并能使服务器端能具备完善的服务功能。
*
* 这个类通过继承Thread类来实现线程的功能,也就是说,在其中的run方法里,定义了该线程启动后要执行的业务动作。
*/
public class SocketServer extends Thread {
// 客户端的socket
private Socket clientSocket;
// IO句柄
private BufferedReader in;
private PrintWriter out;
// 默认的构造函数
public SocketServer() {
}
// 带参数构造函数,初始化了本类里的Socket对象,同时实例化了两类IO对象。
public SocketServer(Socket socket) throws IOException {
clientSocket = socket;
// 初始化in和out句柄
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())), true);
// 通过start方法,启动定义在run方法内的本线程的业务逻辑。
start();
}
// 线程执行的主体内容
public void run() {
try {
// 用循环来监听通讯内容。for(;;) 等同于while(true),死循环
for (;;) {
// 读取从Socket信道上传输过来的客户端发送的通讯信息。
String str = in.readLine();
// 如果得到的信息为“byebye”,则表明本次通讯结束,退出for循环。
if (str.equals("byebye")) {
break;
}
// 将字符串发送给客户端
System.out.println("In Server reveived the info: " + str);
out.println(str);
}
System.out.println("closing the server socket!");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
System.out.println("close the Server socket and the io.");
// 关闭客户端的Socket句柄。
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}