欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java网络编程笔记

程序员文章站 2022-07-12 08:07:34
...

一、Java网络类核接口

Java中,有关网络方面的功能都定义在 java.net 包中。
Java所提供的网络功能可分为以下三类:
1. URL 和 URLConnection
URLConnection是所有类的超类,它代表应用程序和URL之间的通信链接。
此类的实例可用于读取和写入此URL引用的资源。
2. Socket
Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。
3. Datagram
Datagram(数据包)是一种尽力而为的传送数据的方式,它只是把数据的目的地记录在数据包中,然后就直接放在网络上,系统不保证数据能不能安全送到,或者什么时候可以送到,也就是说,他并不保证传送质量。

二、InetAddress类

InetAddress类是Java的IP地址封装类,该类没有构造函数,可以通过该类的静态方法创建该类的实例对象。
这些静态方法如下所示:

方法名 说明
static InetAddress[] getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其IP地址所组成的数组。
static InetAddress getByAddress(byte[] addr) 在给定原始IP地址的情况下,返回InetAddress对象。
static InetAddress getByAddress(String host, byte[] addr) 根据提供的主机名和IP地址创建InetAddress。
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的IP地址。
static InetAddress getLocalHost() 返回本地主机。

【案例:演示InetAddress相关用法】
InternetDemo.java,代码如下:

import java.net.InetAddress;
public class InternetDemo {
    public static void main(String[] args) throws Exception {
        InetAddress inetAddress = InetAddress.getLocalHost();
        // 将此IP地址转换为String
        System.out.println(inetAddress.toString());
        // 获取此IP地址的主机名。
        System.out.println(inetAddress.getHostName());
        // 获取IP
        System.out.println(inetAddress.getHostAddress());
    }
}

URL和URLConnection类

1. URL类

URL是Uniform Resource Location的缩写,即“统一资源定位符”。
通俗地说,URL是在Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务程序上。
采用URL,可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等,这种格式已经成为描述数据资源位置的标准方式。
可以通过URL类提供的构造方法,来获取URL实例对象。
URL类的常用构造方法如下所示:

方法名 说明
URL(String spec) 根据String表示形式创建URL对象。
URL(String protocol, String host, int port, String file) 根据指定的protocol、host、port和file创建URL对象。
URL(String protocol, String host, String file) 根据指定的protocol、host和file创建URL。
URL(URL context, String spec) 通过在指定的上下文中对给定的spec进行解析,创建URL。

URL类中一些很基本的方法如下:

  • public String getProtocol():获取该URL的协议名。
  • public String getHost():获取该URL的主机名。
  • public int getPort():获取该URL的端口号。
  • public String getPath():获取该URL的文件路径。
  • public String getFile():获取该URL的文件名。
  • public String getRef():获取该URL在文件中的相对位置。
  • public String getQuery():获取该URL的查询名。
  • public final Object getContent():获取传输协议。
  • public final InputStream openStream():打开到此URL的连接并返回一个用于从该连接读入的InputStream。
    【案例:演示URL类相关用法】
    URLReader.java,代码如下:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class URLReader {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://www.baidu.com");
        // 获取协议
        System.out.println("protocol: " + url.getProtocol());
        // 获取主机名
        System.out.println("hostname: " + url.getHost());
        // 获取端口号
        System.out.println("port: " + url.getPort());
        // 获取文件
        System.out.println("file: " + url.getFile());
        // 把URL转化为字符串
        System.out.println("toString: " + url.toString());
        System.out.println("========================================");
        InputStream inputStream = url.openStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
        bufferedReader.close();
        inputStreamReader.close();
        inputStream.close();
    }
}

运行此程序,运行结果如下:

protocol: http
hostname: www.baidu.com
port: -1
file: 
toString: http://www.baidu.com
========================================
<!DOCTYPE html>
<!--STATUS OK--><html> <head>
......

注意:在程序中,必须选择public InputStreamReader(InputStream in, String charsetName)构造器来读取数据,否则,在获取的数据中,中文将显示为乱码。
通过URL类的toString()方法,可以很清晰的看出URL的格式有三部分组成:

  1. 协议(或称为服务方式);
  2. 存有该资源的主机IP地址(有时也包括端口号);
  3. 主机资源的具体地址,如目录和文件名等。
    前两者之间用“????/”符号隔开,后两者用“/”符号隔开。其中前两者是不可缺少的。

2. URLConnection类

URLConnection类是一个抽象类,它代表应用程序与URL之间的通信连接。
URLConnection类的实例可用于读取和写入此URL引用的资源。
URLConnection允许用POST、PUT和其他的HTTP请求方法将数据送回服务器。
使用URLConnection对象的一般步骤如下:

  1. 创建一个URL对象;
  2. 通过URL对象的openConnection方法创建URLConnection对象;
  3. 配置参数和一般请求属性;
  4. 获取输入流并读数据;
  5. 获取输出流并写数据;
  6. 关闭连接。

提示:
使用URLConnection对象并不是必须要按以上步骤完成。
用户如果使用URL类的默认设置,就可以省略第3步。
有时候仅需要从服务器读取数据,并不需要向服务器发送数据,这种情况下也可以省略第5步。

【案例:用URLConnection类获取博客园首页的相关内容】

新建TestURL.java,代码如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;

public class TestURL {
    public static void main(String[] args) throws Exception {
        // 实例化URL
        URL url = new URL("https://www.cnblogs.com");
        // 打开连接
        URLConnection connection = url.openConnection();
        // 获取输入流
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
        // 循环读取每一行数据
        String line = null;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
        // 释放资源
        reader.close();
    }
}

提示:
URL类提供的openStream()方法是打开连接到此URL的连接并返回一个用于从该连接读入的InputStream。
openStream()方法实际上是openConnection().getInputStream()方法的编写。

【案例:用HTTPURLConnection类提交请求到百度搜索并获取搜索后的结果】

首先打开www.baidu.com,在查询框中输入“java”,单击“百度一下”按钮,提交请求信息。
在查询结果页面可以看到,请求被提供到“www.baidu.com/s?wd=java”,搜索出与java相关的信息约有53,900,000个。
新建TestParamURL.java,代码如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
public class TestParamURL {
    public static void main(String[] args) throws Exception{
        String url = "http://www.baidu.com/s";
        String param = "wd=java";
        String result = sendGet(url, param);
        System.out.println(result);
    }
    // 以GET方式提交HTTP请求到服务器,并返回结果。
    public static String sendGet(String url, String param) throws Exception {
        StringBuffer result = new StringBuffer();
String urlName = url + "?" + param;
        URL urlObject = new URL(urlName);
        URLConnection connection = urlObject.openConnection();
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
        String line = null;
        while ((line = reader.readLine()) != null) {
            result.append(line).append("\n");
        }
        reader.close();
        return result.toString();
    }
    // 以POST方式提交HTTP请求到服务器,并返回结果。
    public static String sendPost(String url, String param) throws Exception{
        StringBuffer result = new StringBuffer();
        URL urlObject = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) urlObject.openConnection();
        // 设置是否向connection输出,因为这个是POST请求。
        // 参数要放在HTTP正文中,因此需要设为true。默认请情况下是false。
        connection.setDoOutput(true);
        // 设置是否从connection读入,默认情况下是true。
        connection.setDoInput(true);
        // POST请求不能使用缓存。
        connection.setUseCaches(false);
        // 设定传送的内容类型是可序列化的Java对象
        // (如果不设定此项,在传送序列化对象时,当Web服务器默认的不是这种类型时,可能会抛出 java.io.EOFException 异常)。
        connection.setRequestProperty("Content-type", "application/x-java-serialized-object");
        // 设定请求的方式为POST,默认是GET。
        connection.setRequestMethod("POST");

// 利用输出流向服务器传送参数,参数形式为“参数名=值&参数名=值”
        PrintWriter out = new PrintWriter(connection.getOutputStream());
        out.print(param);
        out.flush();
        out.close();
         BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
        String line = null;
        while ((line = reader.readLine()) !=null){
            result.append(line).append("\n");
        }
        reader.close();
        return result.toString();
    }
}

四、Socket套接字

套接字(Socket)允许程序把网络连接当成一个流,可以通过流的方式实现数据的交换。
Java中有专门的Socket类处理用户的请求和响应,利用Socket类可以轻松实现两台计算机间的通信。
Socket类是Java的基础类,用于执行客户端的TCP操作。

  1. 客户端套接字
  2. 服务端套接字

1. 客户端套接字

可以通过构造方法来获取客户端Socket实例对象:

| 方法名 | 说明 |
| Socket(String host, int port) | 创建一个流套接字,并将其连接到指定主机上的指定端口号。 |
| Socket(InetAddress address, int port) | 创建一个流套接字,并将其连接到指定IP地址的指定端口号。 |
常用方法如下所示:

  1. public InetAddress getInetAddress():返回此套接字连接到的远程IP地址,如果套接字是未连接的,则返回null。
  2. public int getPort():返回次套接字连接到的远程端口。如果上为连接套接字,则返回0。
  3. public int getLocalPort():返回此套接字绑定到的本地端口,如果上为绑定套接字,则返回-1。
  4. public InetAddress getLocalAddress():湖区套接字绑定的本地地址,如果尚未绑定套接字,则返回InetAddress.anyLocalAddress()。
  5. public InputStream getInputStream() throws IOException:返回此套接字的输入流。
  6. public OutputStream getOutputStream() throws IOException:返回此套接字的输出流。
  7. public void close() throws IOException:关闭此套接字。

2. 服务端套接字

每个服务器端套接字运行在服务器上特定的端口,监听在这个端口的TCP连接。
当远程客户端的Socket试图与服务器指定端口建立连接时,服务器被**,判定客户程序的连接,并打开两个主机之间固有的连接。
一旦客户端与服务器建立了连接,则两者之间就可以传送数据。
可以通过构造方法来获取服务器端Socket实例对象:

方法名 说明
ServerSocket() 创建非绑定服务器套接字。
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
ServerSocket(int port, int backlog) 利用指定的backlog创建服务器套接字,并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress bingAddr) 使用指定的端口、监听backlog和要绑定的本地IP地址创建服务器。

常用方法如下所示:

  1. Socket accept():监听并接受到此套接字的连接。当用户未连接上来时,此方法一直是闲置状态。此方法会返回一个Socket(它使用的端口与ServerSocket不同),这样ServerSocket可以空出来等待其他用户的连接。
  2. void close():关闭此套接字。
    不管一个Socket通信程序的功能多么齐全、程序多么复杂,其基本结构都是一样的,都包括以下步骤:
  3. 在客户端和服务器创建Socket/ServerSocket实例。
  4. 打开连接到Socket的输入/输出流。
  5. 利用输入/输出流,按照一定的协议对Socket进行读/写操作。
  6. 关闭输入/输出流和Socket。

【案例:演示Socket使用方法】

服务端TCPServer.java:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class TCPServer {
    public static void main(String[] args) throws Exception{
        // 1. 建立Socket
        ServerSocket serverSocket = new ServerSocket(8888);
        // 2. 等待客户端连接
        while (true) {
            // 3. 接收客户端连接
            Socket socket = serverSocket.accept();
            // 4. 在客户端和服务端同时打开输入/输出流
            // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
            // writer.write("你好, " + socket.getInetAddress() + ":" + socket.getPort());
            // writer.close();
            // 5. 服务端读取信息
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            String line = reader.readLine();
            System.out.println("客户端说:" + line);
            reader.close();
        }
    }
}

提示:
端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口)。
客户端TCPClient.java:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class TCPClient {
    public static void main(String[] args) throws Exception {
        // 1. 建立Socket
        Socket socket = new Socket("127.0.0.1", 8888);
        // 2. 在客户端和服务区段同时打开输入/输出流
        // BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
        // String line = reader.readLine();
        // System.out.println("服务器说:" + line);
        // reader.close();
        // 3. 向服务端写入
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
        writer.write("你好, " + socket.getInetAddress() + ":" + socket.getPort());
        writer.close();
    }
}

前面这是实现了一个服务器端和一个客户端通信的功能。
如何实现服务器端与多个客户端程序通信呢?这就需要多线程。
其中主线程有一个Socket绑定在一个固定端口上,负责监听客户端的Socket信息。
每当启动一个客户端程序,客户端发送来一个Socket连接请求,服务端就新开启一个线程,并在其中创建一个Socket与该客户端的Socket通信,直到客户端程序关闭,结束该线程。
主线程中的Socket在应用程序退出时关闭。
客户端代码如下:

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class TCPClient {
    public static void main(String[] args) throws Exception {
        Scanner input = new Scanner(System.in);
        // 1. 建立Socket
        Socket socket = new Socket("127.0.0.1", 8888);
        // 2. 在客户端和服务区段同时打开输入/输出流
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
        // 3. 向服务端写
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
        PrintWriter out = new PrintWriter(writer, true);
        System.out.println("现在开始对话!");
        String content = input.next();
        while (true){
            if (!content.equals("end")) {
                out.println("client:" + content);
                String line = reader.readLine();
                System.out.println(line);
                content = input.next();
            } else {
                out.println("end");
                break;
            }
        }
    }
}

服务端线程ServerThread.java:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class ServerThread extends Thread {
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter out;
    public ServerThread(Socket socket) throws Exception {
        this.socket = socket;
        this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
        this.out = new PrintWriter(socket.getOutputStream(), true);
    }
    @Override
    public void run() {
        try {
            while (true) {
                String line = this.reader.readLine();
                if (line.equalsIgnoreCase("end")) {
                    break;
                }
                System.out.println("来自客户端的消息:" + line);
                out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

服务端TCPServer.java:

import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
    public static void main(String[] args) throws Exception{
        // 1. 建立Socket
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器启用完成...");
        // 2. 等待客户端连接
        while (true) {
            // 3. 接收客户端连接
            Socket socket = serverSocket.accept();
            // 4. 实例化新线程
            Thread thread = new ServerThread(socket);
            // 5. 启动线程
            thread.start();
        }
    }
}