网络编程
基本的通信架构
- 基本的通信架构包括两种 : CS架构(Client客户端/Server服务端), , C需要程序员开发
- BS架构(Browser浏览器/Server架构) : B不需要程序员开发
网络通信三要素 : IP, 端口, 协议
IP
- InetAddress
1 | package demoInetAddress; |
端口
用来标记计算机设备上正在运行的设备
端口分类
- 周知端口:0 - 1023 被预先定义的端口: (HTTP是80, FTP是21)
- 注册端口: 1024 - 49151
- 动态端口 49152 - 65535
一般开发使用注册端口,且一个设备不能出现两个程序端口号一样
协议
事先的一些规则
C / S 架构(重实现)
UDP通信实现
- 特点: 无连接,不可靠通信
- 不事先建立链接: 发送端每次把要发送的数据限制在64K内
- Java提供类包: java.net.DatagramSocket 实现UDP通信
使用方法 :
- 创建
- 客户端socket = new socket
- 接收端socket = new socket(int port)
- 接受和发送
- socket.receive()
- socket.send
- 数据包的新建
- DatagramPacket(bytes[], length, 目的地址IP, 目的地址端口) // 发送端
- DatagramPacket(bytes[], length)
1. 实现1: 一发一收:
UDPclient:
1 | package UDP; |
UDPserver
1 | package UDP; |
注意先启动服务端监听在使用客户端发送信息,即可实现通信
多发多收
UDPserver:
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 package UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPserver {
public static void main(String[] args) throws Exception {
// 1. 创建接收端对象,组测端口
DatagramSocket socket = new DatagramSocket(8080);
byte[] buf = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (true) {
socket.receive(packet);
int len = packet.getLength();
String msg = new String(buf, 0, len);
System.out.println(msg);
String ip = packet.getAddress().getHostAddress();
int port = packet.getPort();
System.out.println("对方IP : " + ip + " port " + port);
}
}
}UDPclient:
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 package UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UDPclient {
public static void main(String[] args) throws Exception {
// 创建发送端
DatagramSocket socket = new DatagramSocket();
var in = new Scanner(System.in);
while (true) {
String msg = in.nextLine();
byte[] bytes = msg.getBytes();
if (msg == "exit") {
socket.close();
break;
}
System.out.println("发送成功");
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8080);
socket.send(packet);
}
}
}
TCP 通信实现
发送端:
使用Socket创建和发送信息,将管道包装成OutputStream方便传输各种数据1
Socket(int IP, int port) // 发送地址,当前端口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package TCP;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 常见的Socket管道对象,请求与服务端Socket链接,可靠链接
Socket socket = new Socket("127.0.0.1", 9999);
// 2. 从socket通信管道得到一个字节输出流
OutputStream os = socket.getOutputStream();
// 3. 特殊数据流
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(1);
dos.writeUTF("今日一别,不知何时与君再相见");
socket.close();
}
}
服务端:
ServerSocket :1
public ServerSocket(int port)
方法:1
publicSocket accept() // 阻塞等待客户端链接,一旦有客户端链接放回Socket对象代表服务端管道
实现: 一发一收
Server:
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 package TCP;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// 创建ServerSocket对象,绑定端口号,监听客户端链接
ServerSocket ss = new ServerSocket(9999);
// 调用accept方法,阻塞等待客户端链接,一旦有客户端链接会返回一个Socket类型对象
Socket socket = ss.accept();
//获取输入流,读取客户端发送数据
InputStream is = socket.getInputStream();
// 把字节输入流包装成特殊数据流
DataInputStream dis = new DataInputStream(is);
int id = dis.readInt();
String msg = dis.readUTF();
System.out.println("id = " + id + " 收到一条消息: " + msg);
// 拿到IP和端口
System.out.println("IP" + socket.getInetAddress().getHostAddress());
System.out.println("端口:" + socket.getPort());
} catch (Exception e) {
e.printStackTrace();
}
}
}Client:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package TCP;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 常见的Socket管道对象,请求与服务端Socket链接,可靠链接
Socket socket = new Socket("127.0.0.1", 9999);
// 2. 从socket通信管道得到一个字节输出流
OutputStream os = socket.getOutputStream();
// 3. 特殊数据流
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(1);
dos.writeUTF("今日一别,不知何时与君再相见");
socket.close();
}
}多发多收
Client:
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 package TCP;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
// 1. 常见的Socket管道对象,请求与服务端Socket链接,可靠链接
Socket socket = new Socket("127.0.0.1", 9999);
// 2. 从socket通信管道得到一个字节输出流
OutputStream os = socket.getOutputStream();
// 3. 特殊数据流
DataOutputStream dos = new DataOutputStream(os);
var in = new Scanner(System.in);
while(true) {
String s;
s = in.nextLine();
if (s == "exit") {
break;
}
dos.writeUTF(s);
dos.flush();
}
socket.close();
dos.close();
}
}
Server
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
34
35 package TCP;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// 创建ServerSocket对象,绑定端口号,监听客户端链接
ServerSocket ss = new ServerSocket(9999);
// 调用accept方法,阻塞等待客户端链接,一旦有客户端链接会返回一个Socket类型对象
Socket socket = ss.accept();
//获取输入流,读取客户端发送数据
InputStream is = socket.getInputStream();
// 把字节输入流包装成特殊数据流
DataInputStream dis = new DataInputStream(is);
while (true) {
String msg = dis.readUTF();
System.out.println(" 收到一条消息: " + msg);
// 拿到IP和端口
System.out.println("IP" + socket.getInetAddress().getHostAddress());
System.out.println("端口:" + socket.getPort());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意到以上只能有一个接收端,因为只接受了一个socket,想实现多个接收,可以使用多线程:使用主线程接受客户链接,对于每个客户新开一个线程。
可以接受多个Client的Server
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
34
35
36
37
38
39
40
41
42
43
44
45 package TCP;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
class K extends Thread {
private Socket socket;
public K(Socket socket) {
this.socket = socket;
}
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
String msg = dis.readUTF();
System.out.println(" 收到一条消息: " + msg);
// 拿到IP和端口
System.out.println("IP" + socket.getInetAddress().getHostAddress());
System.out.println("端口:" + socket.getPort());
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("下线了"); // 或者写在Final
}
}
}
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(9999);
// 调用accept方法,阻塞等待客户端链接,一旦有客户端链接会返回一个Socket类型对象
while (true) {
Socket socket = ss.accept();
System.out.println(socket.getInetAddress().getHostAddress() + "上线了");
new K(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
B / S架构
waiting for learning
