0、TCP通信
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,
如果使用了 WireShark 工具,可以看到一次TCP连接建立时的整个过程。
1、单向通信
单向通信中,一方固定为信息发送方,另外一方则固定为信息的接收方。
1.1 单向通信中的服务端
服务端即为信息的接收方。
使用 ServerSocket 类创建服务端,并将服务的端口设置为 9527;
serverSocket.accept() 方法用于监听对 9527 端口的连接,该方法为阻塞式方法,当接收到数据后,程序才会继续向下执行,否则一直处于等待状态;
当接收到数据后,因是使用字节流传输,这里使用 使用 InputStreamReader 的转换流将字节数据转换为字符串,并使用 BufferedReader 进行读取和输出;
当服务端接收到客户端的请求后,需要向客户端发出响应数据,使用 PrintWriter 发送响应报文,需要使用 flush() 方法,将消息发出;
当客户端发出的消息为“再见”时,服务端即退出通信,关闭服务。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
System.out.println("服务器启动!");
try {
ServerSocket serverSocket = new ServerSocket(9527);
Socket socket = serverSocket.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw = new PrintWriter(socket.getOutputStream());
System.out.println("接收客户端消息");
while (true){
String in = br.readLine();
System.out.println("接收到客户端发来的请求:" + in);
if("再见".equals(in)){
break;
}
pw.print(in + "回报");
pw.flush();
}
} catch (IOException e) {
System.out.println("服务启动失败!");
e.printStackTrace();
}
}
}
启动后,服务端输出为:
服务器启动!
1.2 单向通信中的客户端
客户端即为信息的发送方。
- 创建 Socket 对象,
Socket("localhost", 9527)
和端口为 9527 的服务建立通信; - 接收和发送消息的方法,和服务端相同;
- 为了能够向服务端循环发送消息,使用了死循环,当用户输入“再见”时,终止循环;
- 使用 Scanner 对象接收键盘输入。
import java.io.BufferedReader;
import java.io.IOException;
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", 9527);
//读取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取输出流
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//从键盘获取输入
Scanner scanner = new Scanner(System.in);
while (true){
//从控制台获取向服务端发送的消息
String next = scanner.next();
pw.println(next);
pw.flush();
String s = br.readLine();
System.out.println("收到服务器响应:" + s);
if("再见".equals(next)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.3 建立通信
启动客户端后,并在客户端的控制台输入“你好”,观察服务端和客户端的控制台
在客户端的控制台输入“再见”,观察服务端和客户端的控制台
2、双向通信
双向通信中,双方都可以既是信息的发送方,也可以是信息的接收方。
2.1 双向通信中的服务端
在服务端设置发消息和收消息。
在服务端也使用了 Scanner ,用来接收控制台输入,并将其发送给客户端。
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(9528);
Socket socket = serverSocket.accept();
//获取客户端请求
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取键盘输入
Scanner scanner = new Scanner(System.in);
//发送消息到客户端
PrintWriter pw = new PrintWriter(socket.getOutputStream());
while (true){
String input = br.readLine();
System.out.println("收到客户端请求: " + input);
String output = scanner.nextLine();
pw.println(output);
pw.flush();
if("再见".equals(input)){
break;
}
}
}catch (Exception e){
e.printStackTrace();
System.out.println("服务启动失败!");
}
}
}
2.2 双向通信中的客户端
在客户端设置发消息和收消息
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("127.0.0.1", 9528);
//获取服务端响应
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取客户端用户输入
Scanner scanner = new Scanner(System.in);
//向服务端发送请求
PrintWriter pw = new PrintWriter(socket.getOutputStream());
System.out.println("准备接收请求……");
while (true){
String output = scanner.next();
pw.println(output);
pw.flush();
String input = br.readLine();
System.out.println("来自服务端的响应: " + input);
if("再见".equals(output)){
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
2.3 建议通信
启动服务端及客户端,并在控制台输入内容:
存在的问题:
这样的通信,需要客户端发一次消息 -》 服务端回一次消息,如果客户端同时发送两条消息,就会导致消息不同步。
比如下边的情况:
这个问题可以先思考下,应该如何解决,后边将会给出解决方案。