1.UDP数据报套接字
1.1.DatagramSocket API
DatagramSocket 是 UDP Socket,用于发送和接收UDP数据报(DatagramPacket).
构造方法
构造方法 | 方法作用 |
---|
无参( DatagramSocket() ) | 创建一个UDP数据报套接字的Socket, 绑定到本机任意一个随机端口(一般用于客户端) |
一个int型参数( DatagramSocket(int port) ) | 创建一个UDP数据报套接字的Socket, 绑定到本机指定的端口(一般用于服务端) |
常用方法
常用方法 | 方法作用 |
---|
void receive(DatagramPacket p) | 从此套接字接收数据报.接收到的数据报会存储到参数p中(如果没有接收到数据报, 该方法会阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报.(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
1.2.DatagramPacket API
DatagramPacket是UDP Socket发送和接收的数据报.
构造方法
构造方法 | 方法作用 |
---|
DatagramPacket(byte[] buf,int length) | 构造一个DatagramPacket用来接收数据报, 第一个参数buf 用于接收数据(字节数组), 第二个参数指定接收最大长度 length |
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address) | 构造一个DatagramPacket以用来发送数据报, 第一个参数buf为发送的数据(字节数组), 第二个和第三个参数用于指定长度,buf数组从offset到length用于存放接受到的数据.address中存放了一组IP和端口号(此处指目的主机的IP和端口号) |
常用方法
常用方法 | 方法作用 |
---|
InetAddress getAddress() | 从接收到的数据报中,获取发送端主机的IP地址; 或从发送的数据报中, 获取接收端主机IP地址 |
int getPort() | 从接收到的数据报中,获取发送端主机的端口号; 或从发送的数据报中, 获取接收端主机的端口号 |
byte[] getData() | 获取数据报中存放的数据 |
1.3.InetSocketAddress API
构造UDP发送的数据报时,需要传入的SocketAddress可以使用 InetSocketAddress 来创建.
构造方法
构造方法 | 方法作用 |
---|
InetSocketAddress(InetAddress addr,int port) | 创建一个Socket地址, 包含IP地址和端口号 |
1.4.根据 UDP Socket 模拟回显服务器和客户端
回显服务器:原样返回接收到的数据,不进行逻辑运算.
Server
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPEchoServer {
private DatagramSocket socket = null;
public UDPEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("启动服务器");
while (true) {
DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "UTF-8");
String response = process(request);
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());
socket.send(responsePacket);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UDPEchoServer server = new UDPEchoServer(9090);
server.start();
}
}
Client
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;
public class UDPEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
public UDPEchoClient(String ip, int port) throws SocketException {
socket = new DatagramSocket();
this.serverIp = ip;
this.serverPort = port;
}
public void start() throws IOException {
System.out.println("client上线");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("client->");
String request = scanner.nextLine();
if ("exit".equals(request)) {
break;
}
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
socket.send(requestPacket);
DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, requestPacket.getLength(), "UTF-8");
System.out.println("server->" + response);
System.out.println();
}
System.out.println("client下线");
}
public static void main(String[] args) throws IOException {
UDPEchoClient client = new UDPEchoClient("127.0.0.1", 9090);
client.start();
}
}
2.TCP数据报套接字
2.1.ServerSocket API
ServerSocket 是创建TCP服务端Socket的API
构造方法
构造方法 | 方法作用 |
---|
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
常用方法
常用方法 | 方法作用 |
---|
Socket accept() | 开始监听指定端口(创建时绑定的端口), 有客户端连接后, 返回一个服务端Socket 对象, 并基于该Socket建立与客户端的连接, 否则阻塞等待 |
void close() | 关闭此套接字 |
2.2.Socket API
Socket 是双方建立连接以后,保存双方信息.用于收发数据.
构造方法
构造方法 | 方法作用 |
---|
Socket(String host, int port) | 创建一个流套接字Socket,并与对应IP的主机上对应的端口进程建立连接 |
常用方法
常用方法 | 方法作用 |
---|
InetAddress getInetAddress() | 返回套接字所连接的地址 |
InputStream getInputStream() | 返回此套接字的输入流,用于获取对端的输入信息 |
OutputStream getOutputStream() | 返回此套接字的输出流,用于给对端输出信息 |
2.3.根据 TCP Socket 模拟回显服务器和客户端
由于TCP建立连接后会保持.所以这里使用线程池来解决多次建立连接的问题.
Server
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TCPThreadPoolEchoServer {
private ServerSocket serverSocket = null;
public TCPThreadPoolEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动~");
ExecutorService pool = Executors.newCachedThreadPool();
while (true) {
Socket clientSocket = serverSocket.accept();
pool.submit(() -> processConnection(clientSocket));
}
}
private void processConnection(Socket clientSocket) {
System.out.printf("[%s:%d] 客户端建立连接~\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
try (InputStream inputStream = clientSocket.getInputStream()) {
try (OutputStream outputStream = clientSocket.getOutputStream()) {
Scanner scanner = new Scanner(inputStream);
while (true) {
if (!scanner.hasNext()) {
System.out.printf("[%s:%d] 客户端断开连接~\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
break;
}
String request = scanner.next();
String response = process(request);
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(response);
printWriter.flush();
System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TCPThreadPoolEchoServer tcpEchoServer = new TCPThreadPoolEchoServer(10101);
tcpEchoServer.start();
}
}
Client
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TCPThreadPoolEchoClient {
private Socket socket = null;
public TCPThreadPoolEchoClient(String serverIp, int port) throws IOException {
socket = new Socket(serverIp, port);
}
public void start() {
System.out.println("和服务器连接成功");
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream()) {
try (OutputStream outputStream = socket.getOutputStream()) {
while (true) {
System.out.print("->");
String request = scanner.next();
if ("exit".equals(request)) {
break;
}
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
printWriter.flush();
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
System.out.printf("req: %s, resp: %s\n", request, response);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TCPThreadPoolEchoClient tcpEchoClient = new TCPThreadPoolEchoClient("127.0.0.1", 10101);
tcpEchoClient.start();
}
}