Java中TCP通信的实现之双向通信2——多轮会话
精选
原创
©著作权归作者所有:来自51CTO博客作者mb15c685e188b8a的原创作品,请联系作者获取转载授权,否则将追究法律责任
之前的文章(Java中TCP通信的实现:https://blog.51cto.com/u_113754/6066848 )在研究 TCP 通信的双向通信时,客户端和服务端必须交替请求,才能保持正常会话。
现在就来对这个问题进行解决吧。
1 思路
解决思路是使用多线程的方式:
一个线程用于发送消息;
一个线程用于接收消息。
其中,发送消息的线程,具有的特点是:接收键盘输入内容,并将内容通过 Socket 对象发送到客户端或服务端;
接收消息的线程,特点是:接收从客户端/服务端发来的请求。
2 服务端
2.1 创建接收线程
接收线程,用于接收处理客户端请求。
当收到客户端发来结束请求的 “再见”时,设置 ReceiveBufferSize 值为1,并跳出循环。
//接收消息
class Receive extends Thread{
private Socket socket;
Receive(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.receiveMessage();
}
//接收消息的方法
private void receiveMessage(){
try{
BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true){
String line = bw.readLine();
System.out.println("接收到客户端请求 " + line);
if("再见".equals(line)){
socket.setReceiveBufferSize(1);
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.2 创建发送线程
发送线程,用于服务端向客户端发送消息。
在发送线程里,需要接收键盘输入,以便和客户端进行互动。
当判断 ReceiveBufferSize 的值是否为1,如果设置为了1,结束当前会话并退出循环。
//发送消息
class Send extends Thread{
private Socket socket;
public Send(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.sendMessage();
}
//发送消息的方法
private void sendMessage(){
try{
//创建向客户端发送消息的输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//创建接收键盘输入对象
Scanner scanner = new Scanner(System.in);
while (true) {
if(socket.getReceiveBufferSize() == 1){
socket.close();
break;
}
String output = scanner.nextLine();
pw.println(output);
pw.flush();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.3 创建服务端主线程
在服务端主线程中创建 ServerSocket 对象及接收客户端连接的 Socket 对象,并分别启动接收消息、发送消息线程。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static void main(String[] args) {
try{
ServerSocket serverSocket = new ServerSocket(10001);
System.out.println("服务端启动,等待客户端连接……");
while (true) {
Socket socket = serverSocket.accept();
new Send(socket).start();
new Receive(socket).start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
3 客户端
3.1 创建接收线程
当收到服务端发来的结束请求“再见”时,关闭 Socket 连接并退出循环。
//接收消息
class ClientReceive extends Thread{
private Socket socket;
ClientReceive(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.receiveMessage();
}
//接收消息的方法
private void receiveMessage(){
try{
BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true){
String line = bw.readLine();
System.out.println("接收到服务端信息 " + line);
//当收到客户端发来结束请求时,关闭会话
if("再见".equals(line)){
socket.close();
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
3.2 创建发送线程
当客户端发出“再见”时,即退出循环。
//发送消息
class ClientSend extends Thread{
private Socket socket;
public ClientSend(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.sendMessage();
}
//发送消息的方法
private void sendMessage(){
try{
//创建向客户端发送消息的输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//创建接收键盘输入对象
Scanner scanner = new Scanner(System.in);
while (true) {
String output = scanner.nextLine();
pw.println(output);
pw.flush();
if("再见".equals(output)){
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
3.3 创建客户端主线程
在客户端主线程中分别启动接收消息、发送消息线程。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try{
Socket socket = new Socket("localhost", 10001);
new ClientSend(socket).start();
new ClientReceive(socket).start();
}catch (Exception e){
e.printStackTrace();
}
}
}
以上就实现了客户端与服务端的互动通信,且不受接收顺序的限制。
4 启动服务端和客户端并相互发送消息
客户端和服务端可以发送多轮消息,并在相互发出“再见”时,结束了客户端的会话。