目录
什么是网络编程
在网络通信协议下,不同计算机上运行的程序,进行数据传输。
例如:即时通信、邮件、网络游戏等等。
常见的软件架构
C/S:Client/Server(客户端/服务器)
需要用户在本地下载并安装客户端程序,在远程有一个服务器端程序。例如QQ、WeGame。
B/S:Browser/Server(浏览器/服务器)
只需要一个浏览器,用户输入不同的网址,就可以访问不同的服务器。例如淘宝,京东。
C/S优缺点:
优点:
用户体验好
缺点:
用户需要下载或者更新客户端
需要同时开发客户端和服务器
B/S优缺点:
优点:不需要开发客户端,只需要开发页面+服务端
用户不需要下载客户端,打开浏览就能用(所有的数据,都由服务端传给浏览器)
缺点:如果应用过大,用户体验差。
总结:对于画面和音乐有高的要求用C/S架构
对于画质没啥要求,显示就行采用B/S架构
网络编程三要素
IP(网络中的设备地址,唯一标识)、端口号(设备中程序的唯一标识,由2个字节表示的整数)、协议(数据在网络中传输的规则,UDP、TCP、HTTP、HTTPS、FTP等)。
IP
IPv4:采用32位地址长度,分成4组。即32个bit4个字节(一个字节有8位2进制数,1字节=8比特)。
点分十进制:192.168.1.20
2019年256^4全部分配完毕,出现了IPv6。
IPv6:采用128位地址长度,分成8组。即128bit16个字节。
冒分十六进制+0位压缩表示法(::中间会补0,补齐16位):fe80::4ddd:1da2:417e:ab44%9。
%:IPv6地址中的某些地址范围(如链接本地地址和唯一本地地址)不仅依赖于主机的地址,还依赖于网络接口(例如,网络适配器或网卡)。使用百分号标识符是为了确保正确选择要使用的网络接口。不同的网络接口可以有不同的地址,因此需要在地址后面使用%
来明确指定应该使用哪个接口。
目前如何解决IP不够的问题?使用局域网。
IPv4的地址分类:
公网地址(万维网使用)和私有地址(局域网使用)。
192.168.开头的就是私有地址,为组织机构内部使用,可以来节省IP。例如网吧:每台计算机IP都是192.168开头,多台计算机有一个共同的公网IP。
端口
由2个字节组成,设备上应用程序的标识。
协议
OSI参考模型:世界互联协议标准,全球通信规范,但模型过于理想化,未能在因特网上广泛推广
TCP/IP参考模型(TCP/IP协议):实际上的国际标准。
OSI参考模型 | TCP/IP参考模型 | TCP/IP模型各层对应协议 | 面向哪些方向 |
应用层 | 应用层 | HTTP、FTP、Telnet、DNS... | 应用程序需要关注的,程序员一般在这一层开发,如浏览器、邮箱。 |
表示层 | |||
会话层 | |||
传输层 | 传输层 | TCP、UDP... | 选择传输使用的TCP、UDP协议 |
网络层 | 网络层 | IP、ICMP、ARP... | 封装自己的IP,对方的IP等信息 |
数据链路层 | 物理+数据链路层 | 硬件设备 | 转换成二进制利用物理设备传输 |
物理层 |
UDP协议:
UDP是面向无连接的通信协议。速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据。
什么是面向无连接?计算机发消息给另一台时,不会管他们是否连接成功,该协议只负责发消息。
应用:语音聊天、视频会议,丢失数据会卡顿,不影响使用。
TCP协议:
TCP协议时面向连接的通信协议。速度慢、没有大小限制,数据安全。
面向连接:计算机发送消息时会先确保他们连接成功,再发送消息。
应用:发送消息、邮件。不能丢数据,会改变消息意思。
Java实现网络通信
InetAddress类:
创建对象:通过静态方法getByName()创建对象,他会根据电脑默认使用的ip创建IPv4的实现类(Inet4Address)或者IPv6的实现类(Inet6Address)。
对象方法:getHostName()获取主机名称,getHostAddress():获取主机IP地址。
UDP协议发送数据:
/**
* UDP发送信息
*/
@Test
public void test1() throws Exception{
// 数据包发送 ,空参会在可用端口中选一个使用
DatagramSocket datagramSocket = new DatagramSocket();
String data = "hello word!";
byte[] bytes = data.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 5200;
// 数据包打包封装
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,ip,port);
datagramSocket.send(datagramPacket);
datagramSocket.close();
}
UDP协议接收数据:
/**
* UDP接收信息
*/
@Test
public void test2() throws Exception {
// 接收数据参数要与发送数据的端口保持一致
DatagramSocket datagramSocket = new DatagramSocket(5200);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
datagramSocket.receive(datagramPacket);
InetAddress ip = datagramPacket.getAddress();
int port = datagramPacket.getPort();
byte[] data = datagramPacket.getData();
// 该方法是阻塞的
datagramSocket.close();
System.out.println(ip.getHostAddress()+":"+port+new String(data));
}
聊天室案例
/**
* UDP发送信息:人员1
*/
@Test
public void test3() throws Exception{
// 数据包发送,空参会在可用端口中选一个使用
DatagramSocket datagramSocket = new DatagramSocket();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入内容:");
String str = scanner.nextLine();
if("#".equals(str)){
break;
}
byte[] bytes = str.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 5200;
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,ip,port);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
}
/**
* UDP发送信息:人员2
*/
@Test
public void test4() throws Exception{
// 数据包发送,空参会在可用端口中选一个使用
DatagramSocket datagramSocket = new DatagramSocket();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入内容:");
String str = scanner.nextLine();
if("#".equals(str)){
break;
}
byte[] bytes = str.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 5200;
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,ip,port);
datagramSocket.send(datagramPacket);
}
datagramSocket.close();
}
/**
* UDP接收信息:聊天室
*/
@Test
public void test5() throws Exception {
DatagramSocket datagramSocket = new DatagramSocket(5200);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length);
while (true) {
datagramSocket.receive(datagramPacket);
InetAddress ip = datagramPacket.getAddress();
int port = datagramPacket.getPort();
byte[] data = datagramPacket.getData();
System.out.println(ip.getHostAddress()+":"+port+new String(data));
}
}
注意:在Spring Boot测试中,特别是在运行单元测试时,控制台输入通常会受到限制,导致无法直接输入内容并被Scanner读取。这是因为测试运行是自动化的,通常在没有实际交互性控制台的情况下运行,因此不支持直接从控制台输入。
UDP三种通信方式
单播:单对单发送信息
组播:单对局域网中一组发信息
组播地址:224.0.0.0~239.255.255.255其中224.0.0.0~224.0.0.255为预留的组播地址
MulticastSocket类代替DatagramSocket,在接收端时调用joinGroup方法放入一组。
广播:单对局域网中所有设备发送信息
广播地址:255.255.255.255
发送端将DatagramSocket的Ip改为255.255.255.255就是广播。
TCP通信协议接收和发送消息
/**
* TCP发送消息
*/
@Test
public void test6() throws Exception {
// 创建对象时会自动创建连接,连接失败会报异常
// 三次握手保证连接建立
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),5200);
OutputStream outputStream = socket.getOutputStream();
byte[] bytes = "你好,世界!".getBytes("UTF-8");
// 往输出流中写数据就会发送消息
outputStream.write(bytes);
// 一次消息发送结束标记,不给结束标记,在另一端会一直读取通道信息。
socket.shutdownOutput();
outputStream.close();
// 四次挥手 断开连接并且保证连接通道数据已经被处理完
socket.close();
}
/**
* TCP接收消息
*/
@Test
public void test7() throws Exception {
ServerSocket serverSocket = new ServerSocket(5200);
// 阻塞
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
int b;
while ((b = bufferedReader.read()) > -1){
System.out.print((char)b);
}
inputStream.close();
}