Java高级程序设计笔记 • 【第4章 网络编程】

时间:2023-06-23 13:15:56

全部章节   >>>>


本章目录

4.1 网络基础知识

4.1.1 IP地址

4.1.2 端口号

4.1.3 使用InetAddress

4.1.4 InetAddress 类的具体操作

4.1.5 实践练习

4.2 基于TCP协议的网络编程

4.2.1 TCP 协议基础

4.2.2 创建服务端套接字

4.2.3 创建客户端套接字

4.2.4 基于TCP的通信

4.2.5 实践练习

4.3 使用 Socket 类进行单向通信

4.3.1 服务端读取数据

4.3.2 客户端发送数据

4.3.3 实践练习

4.4 使用Socket类进行双向通信

4.4.1 双向通信服务端

4.4.2 双向通信客户端

4.4.3 TCP双向通信

4.4.4 实践练习

总结:


4.1 网络基础知识

  • 计算机网络,就是将分布在不同地域的计算机与专门的外部设备用通信线路互联成一个规模庞大、功能强大的网络系统,从而使众多计算机可以方便地互相传递信息,共享数据信息资源
  • 网络中不可避免的就是信息交互,而多台计算机终端之间的信息交互就必须依靠网络编程实现,Java中也针对网络通信提供了大量的API,使开发网络通信应用变的更为简单

4.1.1 IP地址

  • IP 地址用于唯一标识网络中的一台计算机,也可以是一台打印机或一部智能手机等终端设备,网络中每台计算机都有独立唯一IP地址
  • IP 地址是一个 32 位整数,通常将其分为 4 个 8 位的二进制数据,每位之间用圆点隔开,每个8 位整数可以转换成一个 0 ~ 255 的十进制整数,如局域网IP通常为192.168.0.XXX

技巧:在windows操作系统的命令提示符下,输入ipconfig可以查看本机ip地址

4.1.2 端口号

提问:

  • 通过IP地址可以定位到具体设备,电脑中有很多软件,如何能把信息数据发送给指定软件接收
  • 端口是一个 16 位的整数,用于表示数据交给哪个计算机中的通信程序(微信、QQ 或 LOL 等)处理,不同的应用程序处理不同端口上的数据,同一台计算机上不能有两个程序使用同一个端口,端口号为 0 ~ 65535

提醒:

前1024个端口已被系统占用,使用时尽量避开常用端口

4.1.3 使用InetAddress

Java 提供了 InetAddress 类代表 IP 地址,InetAddress 下还有两个子类 Inet4Address 和 Inet6Address,它们分别代表 IPv4 和 IPv6 地址 InetAddress 类没有提供构造方法,而是提供了静态方法来获取 InetAddress 实例

方法名

作用

public static InetAddress getName(String host)

根据主机获取对应的 InetAddress 对象

Public static InetAddress getLocalHost()

获取本机 IP 地址所对应的 InetAddress 实例

public String getHostAddress()

返回该 InetAddress 实例所对应的 IP 地址字符串

public String getHostName()

返回此 IP 地址的主机名称

4.1.4 InetAddress 类的具体操作

示例:根据主机名获取对应的 InetAddress 对象

// 根据主机名获取对应的 InetAddress 对象
InetAddress ip = InetAddress.getByName("www.sina.com");
// 获取该 InetAddress 实例的 IP 地址字符串
String ipStr = ip.getHostAddress();
System.out.println(" 新浪网地址 :" + ipStr);
// 获取该 InetAddress 实例的主机名称
String hostName = ip.getHostName();
System.out.println(" 新浪主机名 :" + hostName);

Java高级程序设计笔记 • 【第4章 网络编程】

4.1.5 实践练习

4.2 基于TCP协议的网络编程

4.2.1 TCP 协议基础

  • TCP/IP 通信协议是一种可靠的网络协议,它在通信的两端各建立一个 Socket,从而在通信的两端之间形成网络的虚拟链路,两端的程序就可以通过虚拟链路进行通信
  • Java 对基于 TCP 协议的网络通信提供了良好的封装,Java 使用 Socket 来代表连接的通信端口,并通过 Socket 产生的 I/O 流来进行网络通信
  • 使用 TCP 协议编写网络程序,需要提供服务端程序和客户端程序
  • Java 中的 ServerSocket 类作用类似于114 查号台总机,Socket 类可以实现普通电话和 114 查号台的分机通话功能,整个交互过程如下图

Java高级程序设计笔记 • 【第4章 网络编程】

基于TCP协议服务端和客户端建立连接步骤

  1. 服务端程序创建 ServerSocket 对象,调用 accept() 方法,等待客户端连接
  2. 客户端程序创建 Socket 对象并请求与服务端建立连接
  3. 服务端接收客户端的连接请求,并创建新的 Socket 对象与客户端建立专线连接
  4. 实现(2)和(3)步骤中建立连接的两个 Socket 类在同一线程上对话
  5. 服务端重新等待新的连接请求

4.2.2 创建服务端套接字

在 Java 中使用的 ServerSocket 对象用户监听是来自客户端的 Socket 类连接,如果没有连接,它将一直处于等待状态,ServerSocket 类包含一个来自客户端的连接请求的监听方法

ServerSocket 类的常用方法

方法名

作用

public ServerSocket(int port)

构造方法,用指定端口创建 ServerSocket 实例

public Socket accept()

接收客户端的 Socket 类请求

public InetAddress getInetAddress()

返回服务端主机 IP 地址

public void close()

关闭 ServerSocket 对象

serverSocket实例调用accept方法后,就处于等待状态

示例:使用ServerSocket创建服务端

// 创建 ServerScoket 实例,并在 8888 端口监听客户端
ServerSocket server = new ServerSocket(8888);
// 调用 accep() 方法等待客户端的连接,该方法是一个阻塞方法,如果没有客户端请求服务,该方法下的代码将不会执行
System.out.println(" 服务端套接字已经创建,开始等待来自客户端的连接 ");
Socket socket = server.accept();
System.out.println(" 有客户端已成功连接 ");

4.2.3 创建客户端套接字

客户端使用 Socket 类连接到指定的服务端,每个 Socket 对象代表一个客户端,Socket 类的常用方法如下表

方法名

作用

public Socket(String host,int port)

public Socket(String host,int port) 构造方法,创建连接到指定远程主机和端口的 Socket 实例

public InputStream getInputStream()

返回该 Socket 对象对应的输入流

public OutputStream getOutputStream()

返回该 Socket 对象对应的输出流

public void close()

关闭 ServerSocket 对象

int port  Ip和端口必须和服务端保持一致

示例:使用Socket创建客户端和服务端连接

// 创建客户端 Socket 实例,连接指定 IP 地址和指定端口的服务端
Socket socket = new Socket("127.0.0.1", 8888);

说明:

  • 示例中的127.0.0.1代表本机,也可以使用本地IP地址,如果连接网络中其他电脑,则更改为对应IP即可
  • 代码执行过程中注意进行合理的异常处理,网络编程中可能出现的异常较多

4.2.4 基于TCP的通信

提问:

刚才实现的仅仅为两台设备通过TCP方式建立连接,如何进行通信?

在建立连接的基础之上,可以通过前面学习的输入流、输出流进行信息的发送和接收,从Socket中可以获取输入流、输出流

Java高级程序设计笔记 • 【第4章 网络编程】

示例:使用输入输出流进行信息发送和接收

客户端程序发送数据

// 创建 ServerSocket 类,用于监听客户端 Socket 类的请求连接
ServerSocket server = new ServerSocket(8888);
// 等待客户端的连接,客户端连接后,与客户端对应一个 Socket 管道
Socket socket = server.accept();
// 获得 Socket 管道输入的数据的字节输入流
OutputStream out = socket.getOutputStream();
// 使用打印流包装字节输出流,更为方便输出内容
PrintWriter writer = new PrintWriter(out);
writer.println(“ 欢迎您的访问 ”); //发送信息
writer.close();
server.close();

客户端程序读取数据

// 客户端连接到本机端口号是 8888 的服务端
Socket socket = new Socket("127.0.0.1", 8888);
// 获得 Socket 管道中获取读取数据的字节输入流
InputStream in = (InputStream) socket.getInputStream();
// 为了便于读取数据,将 in 转换成字符流
InputStreamReader isr = new InputStreamReader(in);
// 用 BufferdReader 包装转换后的字符流
BufferedReader reader = new BufferedReader(isr);
String data = reader.readLine();
System.out.println(" 服务端对客户端说 :" + data);
reader.close();
socket.close();

4.2.5 实践练习

4.3 使用 Socket 类进行单向通信

使用 Socket 类套接字,可以进行服务端和客户端的通信。Socket 类通信主要分为单向通信和多向通信两种

单向通信就是指只有一端发送数据,另一端只需接收数据,比如,服务端发送数据到客户端,客户端不需要发送数据到服务端

4.3.1 服务端读取数据

示例代码:

// 创建服务端套接字,监听 8888 接口
server = new ServerSocket(8888);
// 保存客户端发送的数据
String data = null;
while (true) {
// 等待客户端的连接,返回用来通信的 Socket 对象
Socket socket = server.accept();
// 获取客户端的输入流,用来读取传来的数据
InputStream in = socket.getInputStream();
// 将字节流包装成字符流
reader = new BufferedReader(new InputStreamReader(in));
// 判断输入流内的数据是否读取完毕
while ((data = reader.readLine()) != null) {
System.out.println(" 来自客户端的问候:" + data);}
break;

使用while(true)接收多个客户端连接

第二个while循环读取客户端发送的内容

4.3.2 客户端发送数据

示例代码:

// 创建客户端套接字,并连接到服务端
Socket client = new Socket("127.0.0.1", 8888);
// 创建一个可以向服务端发送数据的输出流对象
PrintWriter writer = new PrintWriter(client.getOutputStream());
// 向服务端写入数据
writer.write("hello,server!");
// 清空输出流缓存
writer.flush();

这里可以使用循环模拟不停发送数据

4.3.3 实践练习

4.4 使用Socket类进行双向通信

双向通信是指服务端和客户端都可以发送和接收数据,但是如果服务端需要同时接收多个客户端并且通信,复杂度将大大提高

ECHO 程序是网络编程通信交互的一个经典案例,称为回应程序,即客户端输入哪些内容,服务端会在这些内容前加上“ECHO”并将信息发回给客户端

4.4.1 双向通信服务端

服务端关键代码

// 创建服务端套接字对象
ServerSocket this.server = new ServerSocket(8888);
// 是否关闭服务端连接
boolean flag = true;
while (flag) {
// 获取连接的客户端套接字对象
socket = server.accept();
// 获取 socket 相关的输入流和输出流
BufferedReader reader = getReader(socket);
BufferedWriter writer = getWriter(socket);
// 保存客户端发送的数据
String data = null;
}
while ((data = reader.readLine()) != null) {
// 当获取的信息是“bye”时,关闭流
if ("bye".equals(data)) {
flag = false; //退出的标志
//关闭相关资源 并且 break;}
else {System.out.println(" 来自客户端的数据:" + data);
// 回写给客户端的数据
writer.write("echo:" + data);
// 插入一个行分隔符,readLine() 方法用来判定字符串有没有结束
writer.newLine();
// 刷新输出缓冲区
writer.flush();
}
}

循环读取客户端发送数据,直到发送的是bye退出

4.4.2 双向通信客户端

客户端关键代码

// 创建服务端套接字对象
ServerSocket this.server = new ServerSocket(8888);
// 是否关闭服务端连接
boolean flag = true;
while (flag) {
// 获取连接的客户端套接字对象
socket = server.accept();
// 获取 socket 相关的输入流和输出流
BufferedReader reader = getReader(socket);
BufferedWriter writer = getWriter(socket);
// 保存客户端发送的数据
String data = null;
}
else {
// readLine() 方法必须读取到行分隔符才返回读取。所以传递给输入流的字符串必须
包含行分隔符
System.out.println(" 客户端输出的数据 --->\t" + data);
writer.write(data);
// 插入一个行分隔符作为内容结束的标识
writer.newLine();
// 清空缓冲区
writer.flush();
// 读取服务端返回的数据
System.out.println(" 服务器响应的数据 --->\t " + reader.readLine());
}

4.4.3 TCP双向通信

说明:

  • 以上双向通信案例仅仅是客户端发送一条,然后马上接收(读取)服务端返回的数据,并不能实现*聊天,随意发送和接收
  • 这是因为读取和发送数据时受制于主线程约束,因为读取数据时采用循环读取,会占据主线程资源,此时无法进行其他操作
  • 如果想实现*双向通信功能,需要在服务端和客户端分别结合多线程功能进行实现,但是难度相应增加很多

4.4.4 实践练习

总结:

网络中通过IP地址可以定位到具体设备,通过端口可以和设备上的不同软件进行通信,Java中使用InetAddress类代表IP地址

基于TCP通信服务端步骤为

  1. 服务端创建ServerSocket,并指明通信端口号
  2. 调用serverSocket对象accept方法,等待客户端连接,返回Socket对象
  3. 从Socket对象中获取输入流、输出流进行读取或发送数据
  4. 关闭相关资源

基于TCP通信客户端步骤为

  1. 创建Socket对象,指明连接的服务端IP和端口
  2. 从Socket对象中获取输入流、输出流进行读取或发送数据
  3. 关闭相关资源