Java网络编程笔记

一、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();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值