02_基础加强_通信
概述
网络编程可以让程序与网络上的其他设备中的程序进行数据交互。
常见的通信模式有2种形式:
- Client-Server(CS) :需要开发客户端与服务端
- Browser/Server(BS):只需开发服务端,客户端为浏览器
网络编程3要素:
-
IP地址:设备在网络中的地址,是唯一的标识。
操作类InetAddress常用方法:
名称 说明 public static InetAddress getLocalHost() 返回本主机的地址对象 public static InetAddress getByName(String host) 得到指定主机的IP地址对象,参数是域名或者IP地址 public String getHostName() 获取此IP地址的主机名 public String getHostAddress() 返回IP地址字符串 public boolean isReachable(int timeout) 在指定毫秒内连通该IP地址对应的主机,连通返回true -
端口:应用程序在设备中唯一的标识。
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
- 注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占 用8080,MySQL占用3306)
- 动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
-
协议: 数据在网络中传输的规则。
网络通信协议有两套参考模型:
- OSI参考模型:世界互联协议标准,全球通信规范,由于此模型过于理想化,未能在因特网上进行广泛推广。
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准
OSI参考模型 TCP/IP参考模型 各层对应 面向操作 应用层 应用层 HTTP、FTP、DNS、SMTP… 应用程序需要关注的:浏览器,邮箱。程序员一般在这一层开发 表示层 会话层 传输层 传输层 TCP、UDP... 选择使用的TCP/UDP协议 网络层 网络层 IP、ICMP… 封装源和目标IP,进行路径选择 数据链路层 数据链路层+物理 物理寻址、比特流... 物理设备中传输 物理 传输层的2个常见协议:
-
TCP(Transmission Control Protocol) :传输控制协议
-
使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。
-
传输前,采用“三次握手”方式建立连接,所以是可靠的 。
-
在连接中可进行大数据量的传输。
-
连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低
用于对信息安全要求较高的场景,例如:文件下载、金融等数据通信。
-
-
UDP(User Datagram Protocol):用户数据报协议
- UDP是一种无连接、不可靠传输的协议。
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快
常用于语音通话,视频会话等
UDP编程
简单入门
UDP是一种无连接、不可靠传输的协议。
将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB内,直接发送出去即可
数据包类:DatagramPacket
构造器 | 说明 |
---|---|
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 创建发送端数据包对象 buf:要发送的内容,字节数组 length:要发送内容的字节长度 address:接收端的IP地址对象 port:接收端的端口号 |
public DatagramPacket(byte[] buf, int length) | 创建接收端的数据包对象 buf:用来存储接收的内容 length:能够接收内容的长度 |
常用方法:
方法 | 说明 |
---|---|
public int getLength() | 获得实际接收到的字节个数 |
数据发送/接收端类:DatagramSocket
构造器 | 说明 |
---|---|
public DatagramSocket() | 创建发送端的Socket对象,系统会随机分配一个端口号。 |
public DatagramSocket(int port) | 创建接收端的Socket对象并指定端口号 |
常用方法:
方法 | 说明 |
---|---|
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 接收数据包 |
示例:
// 客户端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
byte[] buffer = "我发啦".getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 9999);
socket.send(packet);
socket.close();
}
}
// 服务端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket serverSocket = new DatagramSocket(9999);
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
serverSocket.receive(packet);
System.out.println(new String(buffer, 0, packet.getLength()));
serverSocket.close();
}
}
广播
UDP的三种通信方式:
- 单播:单台主机与单台主机之间的通信。
- 广播:当前主机与所在网络中的所有主机通信。
- 组播:当前主机与选定的一组主机的通信。
实现广播的方法:
-
发送端发送的数据包的目的地写的是广播地址255.255.255.255、指定端口;
InetAddress.getByName("255.255.255.255")
-
本机所在网段的其他主机的程序只要注册对应端口就可以收到消息了。
组播
使用组播地址:224.0.0.0 ~ 239.255.255.255
实现方法:
- 发送端的数据包的目的地是组播IP (例如:224.0.1.1, 端口:9999)
- 接收端必须绑定该组播IP(224.0.1.1),端口还要注册发送端的目的端口9999;
- DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP。
MulticastSocket socket = new MulticastSocket(9999);
// 绑定组播地址
socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"), 9999),
NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));
TCP编程
简单入门
在java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议。
客户端发送数据步骤:
- 创建客户端的Socket对象,请求与服务端的连接。
- 使用socket对象调用getOutputStream()方法得到字节输出流。
- 使用字节输出流完成数据的发送。
- 释放资源:关闭socket管道。
Socket构造器:
构造器 | 说明 |
---|---|
public Socket(String host , int port) | 创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口 |
常用方法:
方法 | 说明 |
---|---|
OutputStream getOutputStream() | 获得字节输出流对象 |
InputStream getInputStream() | 获得字节输入流对象 |
服务端接收步骤:
- 创建ServerSocket对象,注册服务端端口。
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
- 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
- 释放资源:关闭socket管道
构造器 | 说明 |
---|---|
public ServerSocket(int port) | 注册服务端端口 |
方法:
方法 | 说明 |
---|---|
public Socket accept() | 等待接收客户端的Socket通信连接 连接成功返回Socket对象与客户端建立端到端通信 |
示例:
// 客户端
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
OutputStream outputStream = socket.getOutputStream();
byte[] msg = "TCP消息发啦".getBytes();
outputStream.write(msg);
socket.close();
}
}
// 服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
BufferedReader inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("[" + socket.getRemoteSocketAddress().toString() + "]: " + inputStream.readLine());
serverSocket.close();
}
}
即时通信
即时通信,是指一个客户端的消息发出去,其他客户端可以接收到。前面的例子是只发给服务端的。
实现思路:服务端把在线的Socket管道存储起来,一旦收到一个消息就推送给其他管道