网络编程
InetAddress类
Java中提供一个与 IP 地址相关的 InetADddress 类 ,该类用于封装一个IP地址,提供一些与 IP 地址相关 的 方法;
方法举例;
InetAddress getByName(String host) //获取给的主机名的 IP 地址, host参数表示知道主机。
InetAddress getLocalHost() //获取本地本机地址
String getHostName() // 获取本地IP地址的主机名;
boolean isReachable(int timeout) // 判断在限定时间内指定的IP地址是否可以访问;
String getHostAddress() //获取字符串格式的原始IP地址
代码案例:
public static void main(String[] args) throws Exception {
InetAddress localAddress = InetAddress.getLocalHost(); //获取本地主机 InetAddress对象;
InetAddress remoteAddress = InetAddress.getByName("www.baidu.com"); //获取 主机名为 “www.baidu.com"的InetAddress对象;
System.out.println("本机的IP地址:" + localAddress.getHostAddress());
System.out.println("baidu的IP地址:" + remoteAddress.getHostAddress());
System.out.println("三秒内是否可以访问:"+ remoteAddress.isReachable(3000));
System.out.println("baidu的主机名:" + remoteAddress.getHostName());
}
// InetAddress 对象 是一个 主机名+IP地址 的组合
//通过 getByName(String host) 获得 InetAddress对象;
UDP通信
UDP是一种面向无连接的通信;Java中提供两个类来实现 UDP通信;
DatagramPacket类: 该类 用于 封装UDP通信中的 数据信息;
DatagramSocket类: 该类用于 发送和接收 数据;
DatagramPacket类
用于封装UDP通信中的数据;
构造方法:
该类的接收端的构造方法 只需要接收一个字节数组来存放接收到的数据;而发送端必须指出 IP地址 和 端口号;
接收端的构造方法;
(1) DatagramPacket(byte[] buf , int length); // (1) 指定封装数据的字节数组 和 数据的大小,没有指定IP和端口号;
(2) DatagramPacket(byte[] buf , int offset , int length) // 和第一个类似,只是从 offset 位置开始发送数据
发送方的构造方法;
(1) DatagramPaket(byte[] buf,int length, InetAddress addr, int port) //指明IP地址和端口号
(2) DatagramPaket(byte[] buf,int setoff ,int length, InetAddress addr, int port) //从 offset 位置开始发送数据
DatagramPacket 常用方法:
InetAddress getAddress(); //用于返回发送端或者接收端的IP地址, 如果是发送端的DatagramPacket 对象,则返回接收端的IP地址,反之亦然
int getPort(); // 用于返回发送端或者接收端的端口号,如果是发送端的DatagramPacket 对象,则返回接收端的端口号,反之亦然
byte[] getData(); // 用于返回将要接收或者发送的数据,如果是发送端的DatagramPacket 对象,就返回将要发送的数据,反之亦然
int getLength(); // 同于返回接收或者将要发送数据的长度,如果是发送端的DatagramPacket 对象,就返回将要发送的数据长度,反之亦然;
DatagramSocket
用于创建发送端和接受端的对象;
构造方法:
(1) DatagramSocket(); //创建发送端的 DatagramSocket对象。没有指定端口号,系统自动分配一个网络程序中没有使用的端口号
(2) DatagramSocket(int port); //可以创建接收端或发送端的 DatagramSocket 对象,创建接收端时,必须指定端口号;
(3) DatagramSocket(int port,InetAddress addr); //指定出了IP地址,适用于计算机中有很多网卡的情况;
DatagramSocket 常用方法;
void receive(DatagramPacket p); //该方法用于接收 DatagramPacket数据报,没有收到之前处于阻塞状态,若发送的消息过长,会被截取;
void send(DatagramPacket p); //用于发送 DatagramPacket 数据报,数据报中要包含 发送的数据,数据长度,远程主机IP地址,端口号
void close(); // 关闭当前 Socket,释放资源
两个类使用举例:
//接收端程序
public class UDPReceiver {
public static void main(String[] args) throws Exception {
DatagramSocket server = new DatagramSocket(8900);//创建接口端对象,指定端口号8900;
byte[] buf = new byte[1024]; //定义字节数组,用于接收数据。
DatagramPacket packet = new DatagramPacket(buf, buf.length);//定义一个DatagramPacket对象,用于封装接收的数据;
System.out.println("等待接收数据");
while (true) {
server.receive(packet); //用于接收数据报数据,没有收到之前处于阻塞状态;
String str = new String(packet.getData(), 0, packet.getLength());//调用 Packet中的 getData方法获取信息,并转化为字符串
System.out.println(packet.getAddress() + ":" + packet.getPort() + "发送信息" + str);
}
}
//发送端程序
public class UDPSender {
public static void main(String[] args) throws Exception {
DatagramSocket client = new DatagramSocket(3000); //指定发送端的端口号3000;
String str = "你好, 中国!"; //定义要发送的数组
//定义 一个DatagramPacket数据包,封装发送端信息以及发送地址
DatagramPacket packet = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("localhost"), 8900);
System.out.println("开始发送数据...");
client.send(packet); //发送数据
client.close(); //释放资源
}
}
聊天室案例:
//聊天室
public class ChatRoom {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入聊天服务当前的端口号");
int serverPort = sc.nextInt();
System.out.println("请输入聊天服务发送信息对象的目标端口号:");
int targetPort = sc.nextInt();
System.out.println("聊天系统初始化完成");
try {
DatagramSocket socket = new DatagramSocket(serverPort);
new Thread(new ChatReceiver(socket),"接收服务").start();
new Thread(new ChatSender(socket, targetPort),"发送服务").start();
}catch (Exception e){
e.printStackTrace();
}
}
public class ChatReceiver implements Runnable{
//接收方程序;
private DatagramSocket server ;
public ChatReceiver(DatagramSocket server){
this.server = server;
}
@Override
public void run() {
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);//封装接收的数据;
while (true){
server.receive(packet);
String str = new String(packet.getData(),0,packet.getLength()); //将接收的 数据转化为字符串类型;
System.out.println("收到"+ packet.getAddress()+":"+packet.getPort()+"发送的数据\n"+str);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
//发送方
public class ChatSender implements Runnable{
private DatagramSocket client;
private int targetPort; //指定 发送的 端口号;
public ChatSender(DatagramSocket client, int targetPort){
this.client = client;
this.targetPort = targetPort;
}
@Override
public void run() {
try {
//获取输入的信息;
Scanner sc = new Scanner(System.in);
while (true){
String data = sc.nextLine();
byte[] buffer = data.getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length, InetAddress.getByName("localhost"),targetPort);
client.send(packet);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
在控制台运行两次 main方法即可使用;
TCP通信
UDP中只有发送端和接收端,不区分客户端和服务器端,计算机之间可以任意发送数据。而TCP通信是严格区分客户端和服务器端的,通信时必须有客户端先连接服务器端,服务器 不可以主动连接客户端;
JDK中提供两个类用户实现TCP程序; ServerSocket 类,用于表示服务器端。Socket 类,用户表示客户端;
ServerSocket类
该类的实例对象可以实现一个服务的程序;
构造方法:
(0) ServerSocket(); //创建对象时没有指定端口号,因此该对象不监听任何端口,不能直接使用
//使用时需要 调用 bind(SocketAddress endpoint) 方法将其绑定到指定的端口号上
(1) ServerSocket(int port); //可以绑定端口号。若参数值为0,系统分配一个未被其他程序占有的端口号;
(2) ServerSocket(int port, int backlog) ; //增加一个backlog参数。 用于指定在服务器忙时,可以与之保持链接请求的等待客户端数量,默认为50;
(3) ServerSocket(int port, int backlog, InetAddress bindAddr) //指定相关的IP地址,适用于有多个网卡多个IP地址的情况;
常用方法:
Socket accept(); //用于等待客户端的连接,链接之前处于阻塞状态,有连接返回一个 客户端对应的Socket对象;
InetAddress getInetAddress(); //返回一个InetAddress对象,该对象中封装了ServerSocket 绑定的IP地址;
boolean isClosed(); //判断 ServerSocket对象 是否关闭;
void bind(SocketAddress endpoint) //将 ServerSocket 对象 绑定到 指定的IP地址和端口号上,endpoint封装了 IP地址 和端口号;
Socket类
用于实现TCP客户端程序;
构造方法:
(0) Socket(); // 没有指定IP地址和端口号,只创建了对象,没有链接服务器。 该对象通过 connect(SocketAddress endpoint)方法才能链接服务器
//endpoint封装了 IP地址 和端口号;
(1) Socket(String host, int port); // 该对象 连接 指定的 地址和端口上运行的服务器程序, host 表示字符串类型的IP地址;
(2) Socket(InetAddress address, int port); // InetAddress 封装一个IP地址;
常用方法:
int getPort(); //返回 此 Socket 连接的远程服务器端口;
InetAddress getLocalAddress(); //获取 Socket对象绑定的IP地址,返回 IP地址封装的 InetAddress 形式;
void close(); // 关闭Socket连接
InputStream getInputStream(); //返回 输入流对象。如果该对象由 服务器端的Socket返回,就用于读取 客户端发送的数据;反之亦然
OutputSream getOutputStream(); //返回 输出流对象。如果该对象由 服务器端的Socket返回,就用于向 客户端发送数据;反之亦然
案例:
//TCP服务器
public class TCPServer {
public static void main(String[] args) throws Exception{
ServerSocket server = new ServerSocket(7700); //创建端口为7700的TCP服务端 ServerSocket对象;
while (true){
Socket client = server.accept(); //调用 accept() 方法 开始接收数据;
System.out.println("与客户端连接成功,开始进行数据交互");
OutputStream os = client.getOutputStream(); // 获取客户端的输出流对象;
os.write(("服务器端向客户端发出响应!").getBytes()); //向客户端 输出数据;
Thread.sleep(5000); //模拟与客户端交互耗时;
os.close();
client.close(); //关闭 流 和 Socket 连接
}
}
}
public class TCPClient {
public static void main(String[] args) throws Exception {
Socket client = new Socket(InetAddress.getLocalHost(), 7700); //创建 Socket 对象,连接到指定服务器端;
InputStream is = client.getInputStream(); //获取 服务端返回的 输入流 数据 ;
byte[] buffer = new byte[1024];
int len = is.read(buffer);
while (len != -1) {
System.out.println(new String(buffer,0,len));
len = is.read(buffer);
}
is.close();
client.close();
}
}
多线程的TCP网络程序
//多线程TCP服务器端;
public class ThreadTCPServer {
public static void main(String[] args) throws Exception{
ServerSocket server = new ServerSocket(7788);//创建一个绑定的端口号为7788的 TCP服务器端 对象
//循环等待 等待客户端连接
while(true){
Socket client = server.accept(); //等待服务器连接
//针对每一个客户端请求 创建一个线程进行连接管理;
Thread thread = new Thread(()->{
try {
int port = client.getPort(); //得到连接的客户端的端口号;
System.out.println("与端口号为:"+port + "的客户端成功建立连接!");
OutputStream os = client.getOutputStream();
os.write(("服务器向客户端发出响应!!").getBytes());
Thread.sleep(5000);
System.out.println("结束客户端和服务器端的交互");
os.close();
client.close();
}catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
// 多个 TCPClient 可以 与 TCP服务器端建立连接