网络编程 (套接字)
网络编程概述:网络编程即使用套接字来达到进程间通信
基本概念
网络的七层模型 (OSI):物理层、数据链路层、网络层、传输层 (UDP、TCP)、会话层、表示层、应用层 (http\https\ftp)
IP地址:唯一标识网络中的通信实体、
- IPV4:32位二进制组成;分成4组,每一组的范围0-255之间,每组之间通过.来分隔;例如:192.168.12.22
- IPV6:128位二进制组成;分成了8组,每一组16位,十六进制;每一组由4个十六进制数字组成;每组之间通过:来分隔;
- IP不能重复
- IP本身是动态分配的,静态IP可以实现但是收费高昂;
- 广播IP地址:255.255.255.255
端口号:
域名:将IP地址通过一个字符串来代替
DNS: 域名解析服务器
- 提供根据域名查询IP地址的服务
- 本机hosts文件位置:C盘 —> Windows —> System32 —> drivers —> etc —> hosts文件
- 首先检查本机的hosts文件中有没有域名的配置,如果有直接连接对应的IP地址,否则在DNS上查找对应的IP地址
代表IP地址和端口的类 — SocketAddress
-
SocketAddress 为抽象类 -
实现类:InetSocketAddress — 实现 IP 套接字地址(IP 地址 + 端口号) -
构造方法:
InetSocketAddress(String hostname, int port): 根据主机名和端口号创建套接字地址。 -
方法:
String getHostName() :获取 hostname。int getPort() :获取端口号。 public class SocketAddressDemo {
public static void main(String[] args) {
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",3344);
System.out.println(isa.getHostName());
System.out.println(isa.getPort());
}
}
TCP / UDP
UDP
使用UDP进行网络数据传输
- 底层基于流实现,使用JDK提供的
DatagramSocket 类
DatagramSocket
-
此类表示用来发送和接收数据报包的套接字; -
构造方法:
DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口DatagramSocket(int port) :创建数据报套接字并将其绑定到本地主机上的指定端口。 -
方法:
void send(DatagramPacket p) :从此套接字发送数据报包。void receive(DatagramPacket p) :从此套接字接收数据报包。void close() :用于关闭此数据报套接字
DatagramPacket:
-
此类表示数据报包 -
构造方法:
- 发送端构造方法:
DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 - 接收端构造方法:
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket ,用来接收长度为 length 的数据包。 -
方法:
byte[] getData() :获取数据内容。int getLength() :返回将要发送或接收到的数据的长度SocketAddress getSocketAddress() :获取要将此包发送的或发出此包的远程主机的SocketAddress
发送端和接收端的流程
发送端:
- 创建发送端的对象:
DatagramSocket - 准备数据报包:
DatagramPacket - 调用方法发送数据
- 关流
public class UDPSenderDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String str = "hello world";
byte[] bytes = str.getBytes();
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",3456);
DatagramPacket dp = new DatagramPacket(bytes,str.length(),isa);
ds.send(dp);
ds.close();
}
}
接收端:
- 创建接收端的对象
DatagramSocket ,并指定端口号 - 准备接受数据的数据报包
DatagramPacket - 调用接受数据的方法
- 关流
- 解析数据报包,并打印内容
public class UDPReceiveDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(3456);
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
ds.receive(dp);
ds.close();
byte[] data = dp.getData();
System.out.println(new String(data,0,dp.getLength()));
}
}
注意:先运行接收端在receive() 处阻塞,等待接收数据,再运行发送端发送数据
案例演示:使用UDP模拟单人聊天
public class ChatUDPDemo {
public static void main(String[] args) {
new Thread(new ChatReceiver()).start();
new Thread(new ChatSender()).start();
}
}
class ChatSender implements Runnable {
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
String msg = sc.next();
DatagramPacket dp = new DatagramPacket(msg.getBytes(), msg.length(),
new InetSocketAddress("127.0.0.1", 3345));
ds.send(dp);
if ("#".equals(msg)) {
break;
}
}
ds.close();
sc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ChatReceiver implements Runnable {
@Override
public void run() {
try {
DatagramSocket ds = new DatagramSocket(3345);
while (true) {
DatagramPacket dp = new DatagramPacket(new byte[200], 200);
ds.receive(dp);
String msg = new String(dp.getData(), 0, dp.getLength());
if ("#".equals(msg)) {
System.out.println(dp.getAddress().getHostName() + "已下线");
break;
}
System.out.println(dp.getAddress().getHostName() + ":" + msg);
}
ds.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
TCP
使用TCP进行网络数据传输
客户端:Socket类
- 此类实现客户端套接字
- 构造方法
Socket() :通过系统默认类型的 SocketImpl 创建未连接套接字Socket(String host, int port) :创建一个流套接字并将其连接到指定主机上的指定端口号。 - 方法:
void connect(SocketAddress endpoint) :将此套接字连接到服务器InputStream getInputStream() : 返回此套接字的输入流OutputStream getOutputStream() : 返回此套接字的输出流void shutdownInput() : 读取完成void shutdownOutput() : 输出完成。
服务器端:ServerSocket
- 此类实现服务器套接字
- 构造方法
ServerSocket(int port) :创建绑定到特定端口的服务器套接字。 - 方法
Socket accept() : 接受客户端的连接请求。 返回一个代表当前正在连接的客户端对象void close() :关流
TCP实现网络数据传输
- 客户端
- 创建客户端对象 —
Socket - 发起连接服务器端的请求 —
connect() - 获取输出流 —
getOutputStream() - 写出数据 —
write() - 通知服务器端数据写出完成 —
shutdownOutput() - 关流 —
close()
public class TCPClientDemo {
public static void main(String[] args) throws IOException {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1",8888));
OutputStream out = socket.getOutputStream();
out.write("hello".getBytes());
socket.shutdownOutput();
socket.close();
}
}
- 服务器端:
- 创建服务器端对象,监听端口 —
ServerSocket - 接受客户端的请求 —
accept() - 获取一个输入流 —
getInputStream() - 读取数据 —
read() - 通知客户端读取完成 —
shutdownInput() - 关流 —
close()
public class TCPServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = in.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
socket.shutdownInput();
ss.close();
}
}
案例演示:使用TCP通信模拟文件传输流程
import java.io.*;
import java.net.*;
public class FileUploadDemo {
public static void main(String[] args) {
Server server = new Server();
new Thread(server).start();
Client client = new Client();
new Thread(client).start();
}
}
class Client implements Runnable{
@Override
public void run() {
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress("127.0.0.1",8888));
File file = new File("D:\\Javase\\Day21\\src\\test.txt");
InputStream in = new FileInputStream(file);
String fileName = file.getName();
OutputStream out = socket.getOutputStream();
out.write(fileName.length());
out.write(fileName.getBytes());
byte[] bytes = new byte[1024];
int len;
while ((len = in.read(bytes)) !=-1){
out.write(bytes,0,len);
}
socket.shutdownOutput();
InputStream in1 = socket.getInputStream();
byte[] bytes1 = new byte[1024];
int len1;
while ((len1 = in1.read(bytes1)) !=-1){
System.out.println(new String(bytes1,0,len1));
}
socket.shutdownInput();
socket.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Server implements Runnable{
@Override
public void run() {
try {
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
InputStream in = socket.getInputStream();
int fileNameLen = in.read();
byte[] b = new byte[fileNameLen];
in.read(b);
String fileName = new String(b);
OutputStream out = new FileOutputStream("D:\\Javase\\Day21\\src\\testFile\\" + fileName);
int len;
byte[] bytes = new byte[1024];
while ((len = in.read(bytes)) != -1){
out.write(bytes,0,len);
}
socket.shutdownInput();
OutputStream out1 = socket.getOutputStream();
out1.write("文件传输完成".getBytes());
socket.shutdownOutput();
socket.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
|