网络编程就是在网络通信协议下,不同计算机上运行的程序,进行的数据传输
Java中可以使用java.net包下的技术轻松开发出常见的网络应用程序
常见的软件架构:
C/S:Client/Server(客户端 / 服务器):在用户本地需要下载并安装客户端程序,在远程有一个服务器端程序
B/S:Browser/Server(浏览器 / 服务器):只需要一个浏览器,用户通过不同的网址,客户访问不同的服务器
在BS架构中,所有的图片、音频资源等都需要服务器传输到本地电脑上,而在CS架构中,这些资源包含在安装包内,已经在本地电脑上。因此BS架构中的图片、音频等资源一般没有CS架构中更精致
BS架构的优缺点:
① 不需要开发客户端,只需要页面 + 服务端
② 用户不需要下载,打开浏览器就能使用
③ 如果应用过大,用户体验受到影响
CS架构的优缺点:
① 画面可以做的非常精美,用户体验好
② 需要开发客户端,也需要开发服务端
③ 用户需要下载和更新的时候太麻烦
三要素:
① IP:设备在网络中的地址,是唯一的标识
② 端口号:应用程序在设备中唯一的标识
③ 协议:数据在网络中传输的规则,常见的协议有UDP、TCP、http、https、ftp
IP
全称:Internet Protocol,是互联网协议地址,也称IP地址。是分配给上网设备的数字标签
常见的IP分类为:IPv4、IPv6
IPv4:
全称:Internet Protocol version 4,互联网通信协议第四版。采用32位地址长度,分为4组,每组的大小都是0-255(十进制),最多有2^32个IP,目前已经用完了
例如:
IPv4的地址分类形式:
公网地址(万维网使用)和私有地址(局域网使用)。192.168.开头的就是私有地址,范围即为192.168.0.0-192.168.255.255,专门为组织机构内部使用,以此节省IP
例如:网吧中的每一台电脑共享同一个公网IP,再由路由器分享给每一台电脑不同的局域网IP
特殊IP地址:127.0.0.1,也可以是localhost:是回送地址,也称本地回环地址,也称本机IP,永远只会寻找当前所在本机
连接不同的网时,局域网可能不相同,而使用局域网地址传输数据,需要通过路由器。但若使用回送地址,则不通过路由器,因此建议练习时使用127.0.0.1地址
IPv6:
全称:Internet Protocol version 6,互联网通信协议第六版。由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而IPv4模式下IP的总数是有限的
采用128位地址长度,分为8组,每组2个字节大小,最多有2^128个IP,可以为地球上的每一粒沙子都设定IP
例如:
常用的CMD命令:
ipconfig:查看本机IP地址
ping:检查网络是否连通,后面可以跟IP或网址
InetAddress类的使用:
public static void main(String[] args) throws UnknownHostException {
/*
* static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
* String getHostName() 获取此IP地址的主机名
* String getHostAddress() 返回文本显示中的IP地址字符串
* */
//获取InetAddress的对象
//IP的对象 一台电脑的对象
InetAddress address = InetAddress.getByName("小璐乱撞");
System.out.println(address);
String name = address.getHostName();
System.out.println(name);
String ip = address.getHostAddress();
System.out.println(ip);
}
端口号
应用程序在设备中唯一的标识,可以理解为设备往外发送数据或者往内接收数据的接口
端口号:由两个字节表示的整数,取值范围:0~65535,其中0-1023之间的端口号用于一些知名的网络服务或者应用,我们自己使用1024以上的端口号就可以了
注意:一个端口号只能被一个应用程序使用
协议
计算机网络中,连接和通信的规则被称为网络通信协议
OSI参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准
UDP协议:
用户数据报协议(User Datagram Protocol)
UDP是面向无连接通信协议(不管是否已经连接成功),速度快,有大小限制,一次最多发送64K,数据不安全,易丢失数据
应用场景:网络会议、语音通话、在线视频(允许丢失少部分数据)
TCP协议:
传输控制协议(Transmission Control Protocol)
TCP协议是面向连接通信协议,速度慢,没有大小限制,数据安全
应用场景:下载软件、文字聊天、发送邮件(一点数据都不允许丢失)
UDP通信程序:
发送数据:
① 创建发送端的DatagramSocket对象
② 数据打包(DatagramPacket)
③ 发送数据
④ 释放资源
示例:
public static void main(String[] args) throws IOException {
//发送数据
//1.创建DatagramSocket对象
//细节:
//绑定端口,以后我们就是通过这个端口往外发送数据
//空参:所有可用的端口中随机一个进行使用
//有参:指定端口号进行绑定
DatagramSocket ds = new DatagramSocket();
//2.打包数据
String str = "小璐乱撞";
byte[] bytes = str.getBytes();
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
//3.发送数据
ds.send(dp);
//4.释放资源
ds.close();
}
接收数据:
① 创建接收端的DatagramSocket对象
② 接收打包好的数据
③ 解析数据包
④ 释放资源
示例:
public static void main(String[] args) throws IOException {
//接收数据
//1.创建DatagramSocket对象
//细节:
//在接收的时候,一定要绑定端口
//而且绑定的端口一定要跟发送的端口保持一致
DatagramSocket ds = new DatagramSocket(10086);
//2.接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//该方法是阻塞的
//程序执行到这一步的时候,会在这里死等
//等发送端发送消息
ds.receive(dp);
//3.解析数据包
byte[] data = dp.getData(); //这个数组和bytes是一个数组
int length = dp.getLength();
InetAddress address = dp.getAddress();
int port = dp.getPort();
System.out.println("接收到数据" + new String(data, 0, length));
System.out.println("该数据是从" + address + "这台电脑中的" + port + "这个端口发出的");
//4.释放资源
ds.close();
}
UDP的三种通信方式:
① 单播
发送端只给一个设备发送数据
代码实现:之前练习的代码都是单播
② 组播
发送端给一组设备发送数据
代码实现:
组播地址:224.0.0.0 ~ 239.255.255.255,其中224.0.0.0 ~ 224.0.0.255为预留的组播地址(自己能用的)
发送端:
public static void main(String[] args) throws IOException {
//组播发送端代码
//创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket();
//创建DatagramPacket对象
String str = "小璐乱撞";
byte[] bytes = str.getBytes();
InetAddress address = InetAddress.getByName("224.0.0.2");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
//发送数据
ms.send(dp);
//释放资源
ms.close();
}
接收端:
public static void main(String[] args) throws IOException {
//创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket(10000);
//将当前本机,添加到224.0.0.2这一组当中
InetAddress address = InetAddress.getByName("224.0.0.2");
ms.joinGroup(address);
//创建DatagramPacket对象
byte bytes[] = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//接收数据
ms.receive(dp);
//解析数据
byte[] data = dp.getData();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
int len = dp.getLength();
System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
//释放资源
ms.close();
}
③ 广播
发送端给局域网中所有的设备发送数据
广播地址:255.255.255.255
将之前的发送端发送的IP地址改成255.255.255.255即可
InetAddress address = InetAddress.getByName("255.255.255.255");
TCP通信程序:
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,通信之前要保证连接已经确立,通过Socket产生IO流来进行网络通信
客户端:
① 创建客户端的Socket对象(Socket)与指定服务端连接
Socket(String host, int post)
② 获取输出流,写数据
OutputStream getOutputStream()
③ 释放资源
void close()
服务器:
① 创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
② 监听客户端连接,返回一个Socket对象
Socket accept()
③ 获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
④ 释放资源
void close()
示例:
客户端:
public class Client {
public static void main(String[] args) throws IOException {
//TCP协议,发送数据
//1.创建Socket对象
//细节:在创建对象的同时会连接服务端
//如果连接不上,代码会报错
Socket socket = new Socket("127.0.0.1", 10001);
//2.可以从连接通道中获取输出流
OutputStream os = socket.getOutputStream();
//写出数据
os.write("小璐乱撞".getBytes());
//3.释放资源
os.close();
socket.close();
}
}
服务端:
public class Server {
public static void main(String[] args) throws IOException {
//TCP协议,接收数据
//1.创建对象ServerSocket
ServerSocket ss = new ServerSocket(10001);
//2.监听客户端的连接(若未有客户端来连接,会一直死等)
Socket socket = ss.accept();
//3.从连接通道中获取输入流读取数据
/*
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
*/
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
int b;
while((b = br.read()) != -1) {
System.out.print((char)b);
}
//4.释放资源
br.close();
socket.close();
ss.close();
}
}
三次握手:确保连接建立
[外链图片转存中…(img-1HWRhA1g-1709956049563)]
**四次挥手:**确保连接断开,且数据处理完毕
练习
public class Client {
public static void main(String[] args) throws IOException {
//1.创建Socket对象并连接客户端
Socket socket = new Socket("127.0.0.1", 10002);
//2.写出数据
OutputStream os = socket.getOutputStream();
String str = "小璐乱撞";
os.write(str.getBytes());
//写出一个结束标记
socket.shutdownOutput();
//3.接收客户端回写的数据
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int b;
while((b = isr.read()) != -1) {
System.out.print((char)b);
}
//4.释放资源
socket.close();
}
}
port java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//1.创建对象并绑定端口
ServerSocket ss = new ServerSocket(10002);
//2.等待客户端连接
Socket socket = ss.accept();
//3.socket中获取输入流读取数据
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int b;
//细节:
//read方法会从连接通道中读取数据
//但是,需要有一个结束标记,此处的循环才会停止
//否则,程序就会一直停在read方法这里,等待读取下面的数据
while((b = isr.read()) != -1) {
System.out.print((char)b);
}
//4.回写数据
String str = "好好好";
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
//5.释放资源
socket.close();
ss.close();
}
}
public class Client {
public static void main(String[] args) throws IOException {
//1.创建socket对象,并连接服务器
Socket socket = new Socket("127.0.0.1", 10002);
//2.读取本地文件的数据,并写到服务器当中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day31-code\\src\\test9\\a.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bis.close();
//写出结束标记
socket.shutdownOutput();
//3.接收服务器的回写数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = br.readLine();
System.out.println(line);
//4.释放资源
socket.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
//1.创建对象并绑定端口
ServerSocket ss = new ServerSocket(10002);
//2.等待客户端连接
Socket socket = ss.accept();
//3.读取数据并保存到本地文件中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//获取随机的名字(UUID)
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day31-code\\src\\test9\\serverdir\\" + name + ".jpg"));
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.close();
//4.回写数据
String str = "接收完毕";
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
//5.释放资源
socket.close();
ss.close();
}
}
这道题修改服务器端即可
public class Server {
public static void main(String[] args) throws IOException {
//1.创建对象并绑定端口
ServerSocket ss = new ServerSocket(10002);
while(true) {
//2.等待客户端连接
Socket socket = ss.accept();
//开启一条线程
//一个用户就对应服务端的一条线程
new Thread(new MyRunnable(socket)).start();
}
}
}
public class MyRunnable implements Runnable{
Socket socket;
public MyRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//3.读取数据并保存到本地文件中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day31-code\\src\\test10\\serverdir\\" + name + ".jpg"));
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.close();
//4.回写数据
String str = "接收完毕";
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(socket != null) {
try {
//5.释放资源
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}