P662~P710
网络基础
网络通信
概念:两台设备之间通过网络实现数据传输
网络通信:将数据通过网络从一台设备传输到另一台设备
java.net包下提供了一系列的类或接口,供程序员使用,完成网络通信
网络
- 概念:两台或多台设备通过一定物理设备连接起来,构成了网络。
- 根据网络的覆盖范围不同,对网络进行分类:
- 局域网:覆盖范围最小,仅仅覆盖一个教室或机房
- 城域网:覆盖范围较大,可以覆盖一个城市
- 广域网:覆盖范围最大,可以覆盖全国甚至全球,万维网是广域网的代表
ip地址
- 概念:用于唯一标识网络中的每台计算机
- 查看ip地址:ipconfig
- ip地址表示形式:点分十进制 xx.xx.xx.xx
- 每一个十进制数范围0~255
- ip地址的组成=网络地址+主机地址
- ipv6是用于替代ipv4的下一代ip协议,地址数量号称可以为全世界的每一粒沙子编上一个地址
- 由于ipv4的网络地址资源有限,严重制约了互联网的应用和发展,ipv6的使用,可以解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍
域名
- 概念:将ip地址映射成域名
- 好处:方便记忆
端口号
- 概念:用于标识计算机上某个特定的网络程序
- 表示形式:0~65535
- 0~1024已被占用,比如 ssh 22,ftp 21,smtp 25,http 80
- 常见网络程序端口号:
- tomcat:8080
- mysql:3306
- oracle:1521
- sqlserver:1433
网络通信协议
TCP/IP:传输控制协议,是Internet最基本的协议,是由网络层的IP协议和传输层的TCP协议组成
TCP、UDP
TCP协议:
- 使用TCP协议前,需要先建立TCP连接,形成传输数据通道
- 传输前采用三次握手方式,是可靠的
- TCP协议进行通信的两个应用程序:客户端、服务端
- 在链接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据包的大小限制在64K内
- 因无需连接,是不可靠的
- 发送数据结束时无需释放资源,速度快
InetAddress类,InetAddress_api.java
相关方法:
- 获取本机InetAddress对象 getLocalHost
- 根据指定主机名/域名获取ip地址对象getByName
- 获取InetAddress对象的主机名getHostName
- 获取InetAddress对象的地址 getHostAddress
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddress_api {
public static void main(String[] args) throws UnknownHostException {
//获取本机InetAddress对象
InetAddress localHost= InetAddress.getLocalHost();
System.out.println(localHost);
//根据指定主机名获取InetAddress对象
// InetAddress host1=InetAddress.getByName("");
//根据域名获取InerAddress对象
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println(host2);
//通过InetAddress对象,获取对应地址
String hostAddress= host2.getHostAddress();
System.out.println("host2对应的ip"+hostAddress);
//通过InetAddress对象,获取对应主机名/域名
String hostName=host2.getHostName();
System.out.println("host2对应主机名/域名"+hostName);
}
}
Socket
- 套接字(Socket)开发网络应用程序被广泛采用, 以至于成为事实上的标准。
- 通信的两端都要有socket, 是两台机器间通信的端点。
- 网络通信实际就是socket间的通信。
- Socket允许程序把网络连接当成一个流。 数据在两个socket之间通过IO传输。
- 一般主动发起通信的应用程序属于客户端, 等待通讯请求的为服务端。
Socket过程
当我们需要通讯时(读写数据)
socket使用方法getOutputStream()、getInputStream()
当客户端和服务端连接成功,如果客户端要读数据,socket使用getOutputStream,将数据传到数据通道中,服务器端socket使用getInputStream读取数据。服务器端给客户端数据同理。
TCP网络通信编程
基本介绍
基于客户端服务端的网络通信
底层使用的是TCP/IP协议
应用场景:客户端发送数据,服务端接受并显示
过程:
- 服务器端监听,等待连接
- 连接成功后,socket使用getOutputStream得到输出流,将数据发送给server,server的socket使用getInputStream
- 关闭socket
应用案例1,SocketTCP01Server.java、SocketTCP01Client.java(字节流)
- 编写一个服务器端和一个客户端。
- 服务器端在9999端口监听。
- 客户端连接到服务器端发送hello,然后退出。
- 服务器端接收到客户端发送信息,输出并退出。
//SocketTCP01Server
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1、在本机的9999端口监听,等待连接
// 要求在本机没有其他服务在监听9999
// 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
ServerSocket serverSockrt = new ServerSocket(9999);
System.out.println("服务器在9999端口监听,等待连接");
//2、当没有客户端连接9999端口时,程序会阻塞,等待连接
//如果有客户端连接,届时会返回Socket,程序继续
Socket socket = serverSockrt.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
InputStream inputStream = socket.getInputStream();
//4、IO读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
//根据读取到的实际长度显示内容
System.out.println(new String(buf, 0, readLen));
}
//5、关闭流和socket
inputStream.close();
socket.close();
serverSockrt.close();
}
}
//SocketTCP01Client
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//1、连接服务端(ip,端口)
//连接本机的9999端口,如果连接成功,返回Socket
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket返回" + socket.getClass());
//2、连上后,生成Socket,通过socket.getOutputStream
//得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3、通过输出流,写入数据到数据通道
outputStream.write("hello.server".getBytes(StandardCharsets.UTF_8));
//4、关闭流对象和socket
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
应用案例2,SocketTCP02Server.java、SocketTCP02Client.java(字节流)
- 编写一个服务器端,和一个客户端
- 服务器端在9999端口监听
- 客户端连接到服务器端,发送“hello,server”,并接收服务器端发回的“hello,client”,再退出
- 服务器端接收到客户端发送的信息,输出,并发送“hello,client”,再退出
//SocketTCP02Server
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//1、在本机的9999端口监听,等待连接
// 要求在本机没有其他服务在监听9999
// 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
ServerSocket serverSockrt = new ServerSocket(9999);
System.out.println("服务器在9999端口监听,等待连接");
//2、当没有客户端连接9999端口时,程序会阻塞,等待连接
//如果有客户端连接,届时会返回Socket,程序继续
Socket socket = serverSockrt.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
InputStream inputStream = socket.getInputStream();
//4、IO读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
//根据读取到的实际长度显示内容
System.out.println(new String(buf, 0, readLen));
}
//5、获取socket相关联的输出流
OutputStream outputStream=socket.getOutputStream();
outputStream.write("hello,client".getBytes(StandardCharsets.UTF_8));
//设置结束标记
socket.shutdownOutput();
//6、关闭流和socket
inputStream.close();
outputStream.close();
socket.close();
serverSockrt.close();
}
}
//SocketTCP02Client
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//1、连接服务端(ip,端口)
//连接本机的9999端口,如果连接成功,返回Socket
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket返回" + socket.getClass());
//2、连上后,生成Socket,通过socket.getOutputStream
//得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3、通过输出流,写入数据到数据通道
outputStream.write("hello.server".getBytes(StandardCharsets.UTF_8));
//设置结束标记
socket.shutdownOutput();
//4、IO读取
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
//根据读取到的实际长度显示内容
System.out.println(new String(buf, 0, readLen));
}
//5、关闭流对象和socket
outputStream.close();
inputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
应用案例3,SocketTCP03Server.java、SocketTCP03Client.java(字符流)
- 编写一个服务器端,和一个客户端
- 服务器端在9999端口监听
- 客户端连接到服务器端,发送“hello,server”,并接收服务器端发回的“hello,client”,再退出
- 服务器端接收到客户端发送的信息,输出,并发送“hello,client”,再退出
//SocketTCP03Server
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//1、在本机的9999端口监听,等待连接
// 要求在本机没有其他服务在监听9999
// 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
ServerSocket serverSockrt = new ServerSocket(9999);
System.out.println("服务器在9999端口监听,等待连接");
//2、当没有客户端连接9999端口时,程序会阻塞,等待连接
//如果有客户端连接,届时会返回Socket,程序继续
Socket socket = serverSockrt.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
// 将InputStream-》Reader,使用转换流InputStreamReader()
InputStream inputStream = socket.getInputStream();
//4、IO读取,使用readLine(),字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
//5、获取socket相关联的输出流
OutputStream outputStream = socket.getOutputStream();
//使用字符流回复信息
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello,client,字符流");
bufferedWriter.newLine();
bufferedWriter.flush();//手动刷新
//6、关闭流和socket
bufferedWriter.close();
bufferedReader.close();
socket.close();
serverSockrt.close();
}
}
//SocketTCP03Client
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//1、连接服务端(ip,端口)
//连接本机的9999端口,如果连接成功,返回Socket
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket返回" + socket.getClass());
//2、连上后,生成Socket,通过socket.getOutputStream
//得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3、通过输出流,写入数据到数据通道,使用字符流
//将OutputStream-》Writer,需要使用转换流OutputSreamWriter(字节流)
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello.server");
//设置写入结束标记,writer.newLine()
bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,需要对方使用readLine()
bufferedWriter.flush();//如果使用字符流需要手动刷新,否则数据不会写入数据通道
//4、IO读取
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
String s=bufferedReader.readLine();
System.out.println(s);
//5、关闭流对象和socket
bufferedReader.close();
bufferedWriter.close();
socket.close();
System.out.println("客户端退出");
}
}
应用案例4,TCPFileCopyServer.java、TCPFileCopyClient.java
- 编写一个服务器端,和一个客户端
- 服务器端在9999端口监听
- 客户端连接到服务器端,发送一张图片
- 服务器端接收客户端发送的图片,保存到src下,发送“收到图片”再退出
- 客户端收到服务器端的“收到图片”,再退出
- 要求使用StreamUtils.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPFileCopyServer {
public static void main(String[] args) throws IOException {
//1、在本机的9999端口监听,等待连接
// 要求在本机没有其他服务在监听9999
// 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务器在9999端口监听,等待连接");
//2、当没有客户端连接9999端口时,程序会阻塞,等待连接
//如果有客户端连接,届时会返回Socket,程序继续
Socket socket = serverSocket.accept();
//3、读取客户端发送的数据
//通过socket得到输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtil.streamToByteArray(bufferedInputStream);
//将得到bytes数组,写入到指定的路径,得到一个文件
String FilePath = "./17网络/src/bg.jpg";
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(FilePath));
bufferedOutputStream.write(bytes);
bufferedOutputStream.close();
//4、通过socket获取输出流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("收到");
bufferedWriter.flush();
socket.shutdownOutput();
//5、关闭其他功能
bufferedWriter.close();
bufferedInputStream.close();
socket.close();
serverSocket.close();
}
}
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TCPFileCopyClient {
public static void main(String[] args) throws Exception {
//客户端连接服务器,得到Socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//创建读取磁盘文件的输入流
String filePath= "./17网络/bg.jpg";
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
//bytes就是文件对应的字节数组
byte[] bytes = StreamUtil.streamToByteArray(bufferedInputStream);
//通过socket获取输出流,将bytes发送到服务端
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write(bytes);
bufferedInputStream.close();
socket.shutdownOutput();//写入结束
//接收从服务端返回的消息
InputStream inputStream = socket.getInputStream();
//使用StreamUtils 的方法,直接将inputStream读到的内容专成字符串
String s = StreamUtil.streamToString(inputStream);
System.out.println(s);
inputStream.close();
//关闭其他资源
bufferedOutputStream.close();
socket.close();
}
}
import java.io.*;
public class StreamUtil {
//功能:将输入流转换成byte[],即可以把文件的内容读入到byte[]
public static byte[] streamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];//字节数组
int len;
while ((len = is.read(b)) != -1) {
System.out.println(new String(b, 0, len));
}
byte[] array = bos.toByteArray();//将字节输出流转换成为数组的形式
bos.close();
return array;
}
public static String streamToString(InputStream is) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while((line= reader.readLine())!=null){
builder.append(line+"\r\n");
}
return builder.toString();
}
}
netstat指令
- netstat -an 可以查看当前主机网络情况,可以看到端口监听情况和网络连接情况
- netstat -an | more可以分页显示
- 要求在dos控制台下执行
Listening表示某个端口在监听
如果有一个外部程序连接到该端口,就会显示一条连接信息
TCP网络通讯秘密
当客户端连接到服务器端,实际上客户端也是通过一个端口和服务端进行通讯的这个端口是TCP/IP来随机分配的
UDP编程【了解】
- 类DatagramSocket和DatagramPacket实现了基于UDP协议网络程序
- UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
- DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号
- UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接
基本流程
- 核心的两个类/对象 DatagramSocket和DatagramPacket
- 建立发送端,接收端
- 建立数据报
- 调用类DatagramSocket的发送、接收方法
- 关闭DatagramSocket
UDP说明
- 没有明确的服务端和客户端,演变成数据的发送端和接收端
- 接收数据和发送数据是通过DatagramSocket对象完成
- 将数据封装到DatagramPacket对象,装包
- 当接收到DatagramPacket对象,进行拆包,取出数据
- DatagramSocket可以指定在哪个端口接收数据
应用案例,UDPReceiverA.java、UDPReceiverB.java
- 编写一个接收端A,和一个发送端B
- 接收端A在9999端口等待接收数据
- 发送端B向接收端A发送数据“hello”
- 接收端A接收到发送端B发送的数据,回复“hi”,再退出
- 发送端接收回复的数据再退出
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
public class UDPReceiverB {
public static void main(String[] args) throws IOException {
//创建DatagramSocket,准备发送和接收数据
DatagramSocket socket = new DatagramSocket(9998);
//将需要发送的数据封装到DatagramPacket
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
// DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("ip地址"), 9999);
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);
socket.send(packet);
byte[] bytes = new byte[1024];
packet = new DatagramPacket(bytes, bytes.length);
//调用接收方法,将网络传输的DatagramPacket对象填充到packet对象
//如果没有数据报发送到9999,就会阻塞
System.out.println("B准备接收数据");
socket.receive(packet);
//可以把packet拆包,去输出句并显示
int length = packet.getLength();//实际接收到的数据字节长度
data = packet.getData();//接收到的数据
String s = new String(data, 0, length);
System.out.println(s);
socket.close();
System.out.println("B端退出");
}
}
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//创建一个DatagramSocket对象,准备在9999接收数据
DatagramSocket socket = new DatagramSocket(9999);
//构建一个DataPacket对象,准备接收数据
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//调用接收方法,将网络传输的DatagramPacket对象填充到packet对象
//如果没有数据报发送到9999,就会阻塞
System.out.println("A准备接收数据");
socket.receive(packet);
//可以把packet拆包,去输出句并显示
int length = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到的数据
String s = new String(data, 0, length);
System.out.println(s);
data = "hi".getBytes(StandardCharsets.UTF_8);
//DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("ip地址"), 9999);
packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9998);
socket.send(packet);
socket.close();
System.out.println("A端退出");
}
}