# Java教程 - 1 网络编程
什么是网络编程
网络编程就是实现网络中计算机之间数据交互和通信。
IP与端口
两台计算机上的两个程序进行通信,如何在网络上确定是这两个程序呢?
通过 IP 地址,可以确定是哪两台主机,但是主机上的程序有很多,如何确定是哪两个程序进行通信,这里涉及到端口信息。
端口是每个网络程序在设备上的唯一标识,每个网络程序都需要绑定一个端口号,传输数据的时候,通过 IP 确定发到哪台机器上,通过端口确定发到哪个程序。
端口号范围从0~63335,编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本都被系统程序占用了;
常用的软件使用的端口好:
mysql:3306
oracle:1521
web:80
tomcat:8080
Redis:6379
2
3
4
5
通信协议
我们要在两个应用程序之间进行网络通信,需要使用套接字 socket
来实现。
socket
由 IP地址
和 端口号
组成。IP 地址用来确定是哪台设备,端口号用来确定是设备上的哪个程序。
在网络编程中,TCP
和 UDP
是两个常用的协议,而 socket 是在应用层和传输层之间的接口,用于方便地使用 TCP 或 UDP 进行网络通信。
TCP 和 UDP 协议的区别:
TCP是一种可靠的面向连接的协议,它在传输数据之前先建立一个连接,确保数据的可靠性和完整性,然后再进行数据传输。
UDP是一种不可靠的无连接协议,它直接将数据分组发送到目的地址,不需要建立连接,速度快,但数据传输的可靠性较差。
# 1.1 TCP通信
TCP通信是区分服务端和客户端的,服务器启动服务,指定IP和端口,等待客户端的连接。
客户端创建连接,指定服务端的IP和端口,连接到服务端,这样服务端和客户端就可以发送数据了。
# 1 编写服务端
首先编写服务端的程序,创建一个 TcpServer.java
的类。
服务器端需要指定监听的端口号。
package com.doubibiji;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
private static final int SERVER_PORT = 8888;
public static void main(String[] args) throws IOException {
// 1.创建Socket,指定端口号
ServerSocket server = new ServerSocket(SERVER_PORT);
// 2.等待接收客户端的请求,程序在这里会阻塞等待客户端连接
Socket socket = server.accept();
// 3.获取客户端输入和输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 从客户端读取数据
byte[] data = new byte[1024];
while (true) {
// 4.从客户端读取数据
int len = is.read(data);
if (len > 0) {
//将数据转换为字符串
String message = new String(data, 0, len);
System.out.println(message);
// 如果接收到客户端发送exit,服务器就退出
if ("exit".equals(message)) {
break;
}
// 服务器将收到的内容发回给客户端
String reply = "服务器收到:" + message;
os.write(reply.getBytes("UTF-8"));
}
}
// 5.关闭服务端
is.close();
os.close();
socket.close();
// 关闭socket,正常情况下server是不需要关的
server.close();
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
服务端首先创建 socket,等待客户端的连接,客户端连接后,就可以获取客户端的输入或输出流,然后进行数据的发送和读取了。
上面用到了 while 循环,不停的从客户端读取数据,当接收到客户端发送 exit
时,退出程序。
# 2 编写客户端
下面开始编写客户端的程序,客户端需要指定连接的服务器的 IP 和端口号。
创建 TcpClient.java
,代码如下:
package com.doubibiji;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TcpClient {
// 127.0.0.1表示本地IP,服务器在本机运行
private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 8888;
public static void main(String[] args) throws IOException {
// 1.创建socket
Socket socket = new Socket(SERVER_IP, SERVER_PORT);
// 2.获取输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 从键盘输入内容发送给服务器
Scanner scanner = new Scanner(System.in);
byte[] data = new byte[1024];
while (true) {
String input = scanner.nextLine();
if (input.length() > 0) {
// 3.向服务器发送数据
os.write(input.getBytes("UTF-8"));
}
// 如果输入的exit就退出
if ("exit".equals(input)) {
break;
}
// 4.从服务器读取内容
int len = is.read(data);
//将数据转换为字符串
String reply = new String(data, 0, len);
System.out.println(reply);
}
// 关闭客户端
is.close();
os.close();
socket.close();
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
首先创建 socket,指定服务器的 IP 地址和端口,这里服务器和客户端都是在本地运行的,所以指定了本地的地址 127.0.0.1
。
获取输入输出流就可以读取和发送数据了,上面的代码读取键盘的输入,让内容发送给服务端。
上面使用了一个 while
循环,主要是为了可以不停的通过键盘输入,向服务端发送数据。
当输入 exit
的时候,可以退出程序。
# 3 运行程序
在运行的时候,我们先运行服务端程序,然后再运行客户端程序。
然后在客户端模块通过键盘输入内容发送到服务端,服务端会响应客户端的数据请求。
客户端:
服务端:
# 4 接收多个客户端连接
上面的服务端只能接收一个客户端的连接,因为只在一个线程中进行的。
如果要服务端接收多个客户端的连接,则需要使用多线程来实现。
package com.doubibiji;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
private static final int SERVER_PORT = 8888;
public static void main(String[] args) throws IOException {
// 1.创建Socket,指定端口号
ServerSocket server = new ServerSocket(SERVER_PORT);
while (true) {
// 2.等待接收客户端的请求,程序在这里会阻塞等待客户端连接
Socket socket = server.accept();
// 为每个客户端创建一个新的线程来处理
new Thread(new ClientHandler(socket)).start();
}
// 关闭socket,正常情况下server是不需要关的
// server.close();
}
private static class ClientHandler implements Runnable {
private final Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream is = null;
OutputStream os = null;
try {
// 3.获取客户端输入和输出流
is = socket.getInputStream();
os = socket.getOutputStream();
// 从客户端读取数据
byte[] data = new byte[1024];
while (true) {
// 4.从客户端读取数据
int len = is.read(data);
if (len > 0) {
//将数据转换为字符串
String message = new String(data, 0, len);
System.out.println(message);
// 如果接收到客户端发送exit,服务器就退出
if ("exit".equals(message)) {
break;
}
// 服务器将收到的内容发回给客户端
String reply = "服务器收到:来自" + socket.getInetAddress() + ", 消息" + message;
os.write(reply.getBytes("UTF-8"));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭服务端
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
这样就可以接收多个客户端的连接了,每连接一个客户端会开启一个线程。
# 1.2 UDP通信
UDP通信比TCP通信更简单一些。使用UDP协议进行通信不需要建立连接,可以直接发送数据包。
UDP通信也不分客户端和服务端。
# 1 编写接收端
首先创建 UdpReceiver.java
,编写代码如下:
package com.doubibiji;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpReceiver {
public static void main(String[] args) throws Exception {
// 1.创建DatagramSocket,需要指定接收端的端口
int port = 8888;
DatagramSocket socket = new DatagramSocket(port);
// 2.创建数据接收包
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
// 使用for循环,不停的接收数据
while (true) {
// 3.接收数据
socket.receive(packet);
// 4.获取数据,获取不是有效的数据,是获取整个接收包传递的数组
byte[] data = packet.getData();
// 获取有效的字节个数
int len = packet.getLength();
// 将数据转换成字符串
String str = new String(data, 0, len);
System.out.println("接收到:" + str);
if ("exit".equals(str)) {
break;
}
}
// 5.关闭
socket.close();
}
}
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
34
35
36
37
38
接收端不需要等待客户端的连接,直接接收客户端的数据即可。
上面的程序在接收到发送端发送 exit
后,就会退出。
# 2 编写发送端
发送端在发送数据的时候,需要指定接收端的 IP 和 端口。
编写发送端,创建 UdpSender.java
,编写代码如下:
package com.doubibiji;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UdpSender {
public static void main(String[] args) throws Exception {
// 1.创建创建DatagramSocket,发送可以不指定端口号,因为不接收数据
DatagramSocket socket = new DatagramSocket();
// 构建地址
InetAddress address = InetAddress.getByName("127.0.0.1");
// 指定接收的端口号
int port = 8888;
// 从键盘输入内容发送给服务器
Scanner scanner = new Scanner(System.in);
while (true) {
String input = scanner.nextLine();
// 2.创建DatagramPacket,指定数据,数据长度,地址,端口
byte[] data = input.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// 3.发送
socket.send(packet);
// 如果输入的exit就退出
if ("exit".equals(input)) {
break;
}
}
// 4.关闭
socket.close();
}
}
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
34
35
36
37
38
上面的程序接收键盘的数据,将输入信息发送给接收端,当输入 exit
的时候退出程序。
# 3 运行程序
运行的时候,先运行接接收端的程序,在运行发送端的程序。
然后在发送端模块通过键盘输入内容发送到接收端:
接收端就可以接收到信息了:
02-泛型 →