JavaWeb第1篇网络编程

TCP编程

通过Socket技术(它是计算机之间进行通信的一种约定或一种方式),我们就可以实现两台计算机之间的通信,Socket也被翻译为套接字,是操作系统底层提供的一项通信技术,它支持TCP和UDP。而Java就对socket底层支持进行了一套完整的封装,我们可以通过Java来实现Socket通信

使用Java进行TCP编程时,需要使用Socket模型

  • 服务器端用ServerSocket监听指定端口
  • 客户端使用Socket(InetAddress, port)连接服务器
  • 服务器端用accept()接收连接并返回Socket
  • 双方通过Socket打开InputStream/OutputStream读写数据
  • 服务器端通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率
  • flush()用于强制输出缓冲区到网络

TCP案例

Socket双向通信:服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class ServiceMain {
public static void main(String[] args) throws IOException {
InputStreamReader isr;
BufferedReader br;
OutputStreamWriter osw;
BufferedWriter bw;
String str;
Scanner in = new Scanner(System.in);
try {
ServerSocket server = new ServerSocket(4444);// 在本机的4444端口开放Server
Socket socket = server.accept();// 只要产生连接,socket便可以代表所连接的那个物体,同时这个server.accept()只有产生了连接才会进行下一步操作。
System.out.println(socket.getInetAddress());// 输出连接者的IP。
System.out.println("建立了一个连接!");
while (true) {
isr = new InputStreamReader(socket.getInputStream());
br = new BufferedReader(isr);
System.out.println(socket.getInetAddress() + ":" + br.readLine());
osw = new OutputStreamWriter(socket.getOutputStream());
bw = new BufferedWriter(osw);
System.out.print("回复:");
str = in.nextLine();
bw.write(str + "\n");
bw.flush();
}
} catch (IOException e) {
System.out.println("连接异常!");
}
}
}

Socket双向通信:客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class ServerMain {
public static void main(String[] args) throws IOException {
InputStreamReader isr;
BufferedReader br;
OutputStreamWriter osw;
BufferedWriter bw;
String str;
Scanner in = new Scanner(System.in);
try {
Socket socket = new Socket("localhost", 4444);
System.out.println("成功连接服务器");
while (true) {
osw = new OutputStreamWriter(socket.getOutputStream());
bw = new BufferedWriter(osw);
System.out.print("回复:");
str = in.nextLine();
bw.write(str + "\n");
bw.flush();
isr = new InputStreamReader(socket.getInputStream());
br = new BufferedReader(isr);
System.out.println(socket.getInetAddress() + ":" + br.readLine());
}
} catch (IOException e) {
System.out.println("连接异常!");
}
}
}

UDP编程

在Java中使用UDP编程,仍然需要使用Socket,因为应用程序在使用UDP时必须指定网络接口(IP)和端口号。注意:UDP端口和TCP端口虽然都使用0~65535,但他们是两套独立的端口,即一个应用程序用TCP占用了端口1234,不影响另一个应用程序用UDP占用端口1234

使用UDP协议通信时,服务器和客户端双方无需建立连接:

  • 服务器端用DatagramSocket(port)监听端口
  • 客户端使用DatagramSocket.connect()指定远程地址和端口
  • 双方通过receive()和send()读写数据
  • DatagramSocket没有IO流接口,数据被直接写入byte[]缓冲区

UDP编程

UDP发送接收实例:服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class UdpService {
public static void main(String[] args) throws IOException {
try {
DatagramSocket server = new DatagramSocket(5060);
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
server.receive(packet);
String info = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
System.out.println(packet.getAddress().getHostName() + "(" + packet.getPort() + "):" + info);
packet.setData("Hello Client".getBytes());
packet.setPort(5070);
packet.setAddress(InetAddress.getLocalHost());
server.send(packet);
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

UDP发送接收实例:客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class UdpServer {
public static void main(String[] args) throws IOException {
try {
DatagramSocket client = new DatagramSocket(5070);
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
packet.setPort(5060);
packet.setAddress(InetAddress.getLocalHost());
packet.setData("Hello Server".getBytes());
client.send(packet);
client.receive(packet);
String info = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
System.out.println(packet.getAddress().getHostName() + "(" +
packet.getPort() + "):" + info);
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

HTTP编程

浏览器访问Socket服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class UdpServer {
public static void main(String[] args) throws IOException {
try {
DatagramSocket client = new DatagramSocket(5070);
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
packet.setPort(5060);
packet.setAddress(InetAddress.getLocalHost());
packet.setData("Hello Server".getBytes());
client.send(packet);
client.receive(packet);
String info = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
System.out.println(packet.getAddress().getHostName() + "(" +
packet.getPort() + "):" + info);
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

HTTP客户端编程
HTTP响应代码

  • 1xx:表示一个提示性响应,例如101表示将切换协议,常见于WebSocket连接
  • 2xx:表示一个成功的响应,例如200表示成功,206表示只发送了部分内容
  • 3xx:表示一个重定向的响应,例如301表示永久重定向,303表示客户端应该按指定路径重新发送请求
  • 4xx:表示一个因为客户端问题导致的错误响应,例如400表示因为Content-Type等各种原因导致的无效请求,404表示指定的路径不存在
  • 5xx:表示一个因为服务器问题导致的错误响应,例如500表示服务器内部故障,503表示服务器暂时无法响应

从Java 11开始,引入了新的HttpClient,它使用链式调用的API,能大大简化HTTP的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.Map;

public class HttpDemo {
// 全局HttpClient:
static HttpClient httpClient = HttpClient.newBuilder().build();

public static void main(String[] args) throws Exception {
String url = "https://www.baidu.com/";
HttpRequest request = HttpRequest.newBuilder(new URI(url))
// 设置Header:
.header("User-Agent", "Java HttpClient").header("Accept", "*/*")
// 设置超时:
.timeout(Duration.ofSeconds(5))
// 设置版本:
.version(HttpClient.Version.HTTP_2).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// HTTP允许重复的Header,因此一个Header可对应多个Value:
Map<String, List<String>> headers = response.headers().map();
for (String header : headers.keySet()) {
System.out.println(header + ": " + headers.get(header).get(0));
}
System.out.println(response.body().substring(0, 1024) + "...");
}
}

JavaWeb第1篇网络编程
https://www.eldpepar.com/coding/9859/
作者
EldPepar
发布于
2022年10月27日
许可协议