网络编程入门笔记
基础的科普
概述
举例
打电话——TCP
发短信——UDP
网络编程的目的
传播交流信息、数据交换、通信
网络通信的要素
- 通信双方的地址,包括IP地址、端口号port
- 网络通信的协议
tcp/ip参考模型
OSI七层网络模型是理想化的参考模型,并没有实际应用,实际是TCP/IP参考模型。
IP地址
IP地址:InetAddress
- IP用于唯一定位一台网络上的计算机
- 本机:127.0.0.1 或者 localhost
- IP地址的分类
- IPv4 : 32位,4个字节组成,每个字节范围为0——255的无符号整数,总量有42亿,即2的32次方。分布不均,30亿在北美,亚洲4亿,2011年就用尽了。
- IPv6 :128位,8个无符号整数,每个整数16位。
- 示例:2001:DB8:0:23:8:800:200C:417A
- 公网(互联网)/私网IP(局域网)
- ABCD类地址
- 192.168.X.X 这类地址是专门给组织内部使用的,内网地址
- 域名:用于记忆IP地址、集群时的多个IP地址与域名的映射
扩展
IPv6表示法
IPv6的地址长度为128位,是IPv4地址长度的4倍。于是IPv4点分十进制格式不再适用,采用十六进制表示。IPv6有3种表示方法。
一、冒分十六进制表示法
格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如:
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
这种表示法中,每个X的前导0是可以省略的,例如:
2001:0DB8:0000:0023:0008:0800:200C:417A→ 2001:DB8:0:23:8:800:200C:417A
二、0位压缩表示法
在某些情况下,一个IPv6地址中间可能包含很长的一段0,可以把连续的一段0压缩为“::”。但为保证地址解析的唯一性,地址中”::”只能出现一次,例如:
FF01:0:0:0:0:0:0:1101 → FF01::1101
0:0:0:0:0:0:0:1 → ::1
0:0:0:0:0:0:0:0 → ::
三、内嵌IPv4地址表示法
为了实现IPv4-IPv6互通,IPv4地址会嵌入IPv6地址中,此时地址常表示为:X:X:X:X:X:X:d.d.d.d,前96b采用冒分十六进制表示,而最后32b地址则使用IPv4的点分十进制表示,例如::192.168.0.1与::FFFF:192.168.0.1就是两个典型的例子,注意在前96b中,压缩0位的方法依旧适用
IP地址中A类、B类、C类地址的区别
IP地址表示方法不同:
一个A类IP地址是指, 在IP地址的四段号码中,第一段号码为网络号码,剩下的三段号码为本地计算机的号码。如果用二进制表示IP地址的话,A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”。A类IP地址中网络的标识长度为8位,主机标识的长度为24位。
一个B类IP地址是指,在IP地址的四段号码中,前两段号码为网络号码。如果用二进制表示IP地址的话,B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是“10”。B类IP地址中网络的标识长度为16位,主机标识的长度为16位。
一个C类IP地址是指,在IP地址的四段号码中,前三段号码为网络号码,剩下的一段号码为本地计算机的号码。如果用二进制表示IP地址的话,C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是“110”。C类IP地址中网络的标识长度为24位,主机标识的长度为8位。
2、IP地址范围不同:
A类IP地址 地址范围从1.0.0.1到127.255.255.254 (二进制表示为:00000001 00000000 00000000 00000001 - 01111111 11111111 11111111 11111110)。最后一个是广播地址。
B类IP地址地址范围从128.0.0.1-191.255.255.254 (二进制表示为:10000000 00000000 00000000 00000001-10111111 11111111 11111111 11111110)。 最后一个是广播地址。
C类IP地址范围从192.0.0.1-223.255.255.254 (二进制表示为: 11000000 00000000 00000000 00000001 - 11011111 11111111 11111111 11111110)。最后一个是广播地址。
3、子网掩码不同:
A类IP地址的子网掩码为255.0.0.0
B类IP地址的子网掩码为255.255.0.0
C类IP地址的子网掩码为255.255.255.0
4、适用范围不同:
A类适用的类型为大型网络,A类网络地址数量较少,有126个网络,每个网络支持的最大主机数为256的3次方-2=16777214台;
B类适用的类型为中型网络,B类网络地址数量适中,有16384个网络,每个网络支持的最大主机数为256的2次方-2=65534台;
C类适用的类型为小型网络,C类网络地址数量较多,有209万余个网络,适用于小规模的局域网络,每个网络支持的最大主机数为256的1次方-2=254台。
ABCD类地址记忆
A:0-127 中间128位
B:128-191 中间64位
C:192-223 中间32位
D:224-239 中间16位
E:240-255 中间16位
记的话就从0开始一直加128,再加它的二分之一,一直这样下去,即可算出。
端口Port
我们如果把IP地址比喻成一栋楼,那端口Port就是每套房子的名字,我们的主机上的程序相当于一套房子,而我们的每个程序运行时都要分配一个端口号。
- 不同的进程有不同的端口号,用来区分软件的。
- 规定范围 0~65535
- 不同的协议TCP\UDP都有对应0~65535的端口号范围,因此总的可以使用的端口号有65535 * 2 个,不同协议的端口号可以相同,而相同协议下比如TCP只能有一个80端口被一个进程使用,不能有冲突。
- 端口分类
- 公有端口0~1023
- HTTP: 80 (默认端口)
- HTTPS: 443
- FTP: 21
- SSH: 22
- Telent :23
- 程序注册端口范围:1024~49151 ,分配给用户或者程序的端口
- Tomcat:8080
- MySQL: 3306
- Oracle:1521
- 私有、动态端口范围:49152~65535
- 公有端口0~1023
简单命令
#查看所有端口列表
netstat -ano
#查看指定端口或字符串对应的端口列表。 |表示管道,过滤的意思,会先执行过滤再查找
netstat -ano|findstr "8080"
#查看指定端口或者程序的进程
tasklist|findstr "chrome"
tasklist|findstr "1736"
代码示例
public class INetAddressTest {
public static void main(String[] args) throws UnknownHostException {
//查询本机地址的3种方式
///127.0.0.1
System.out.println(InetAddress.getByName("127.0.0.1"));
//DESKTOP-G590BHK/172.16.57.150 电脑计算机名加主机
System.out.println(InetAddress.getLocalHost());
InetAddress localhost = InetAddress.getByName("localhost");
// System.out.println(localhost.getAddress());//返回2进制数组地址, byte[]
//localhost/127.0.0.1
System.out.println(localhost);
//activate.navicat.com 返回典范,标准的主机名,因为在本机电脑hosts配置了 127.0.0.1 activate.navicat.com
// System.out.println(localhost.getCanonicalHostName());
//localhost
System.out.println(localhost.getHostName());
System.out.println(localhost.getHostAddress());
System.out.println("-----------------------------------");
InetAddress qq = InetAddress.getByName("www.qq.com");
System.out.println(qq);
System.out.println(qq.getCanonicalHostName());
System.out.println(qq.getHostName());
System.out.println(qq.getHostAddress());
System.out.println("----------------port-------------------");
//IP端口对象
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 8080);
System.out.println(socketAddress.getAddress());
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress.getPort());
}
/**
* 这两个类作用不大,只是了解
* /127.0.0.1
* DESKTOP-G590BHK/172.16.57.150
* localhost/127.0.0.1
* localhost
* 127.0.0.1
* -----------------------------------
* www.qq.com/121.14.77.201
* www.qq.com
* www.qq.com
* 121.14.77.201
* ----------------port-------------------
* localhost/127.0.0.1
* localhost
* 8080
*/
}
通信协议
协议:就是约定,就像我们约定沟通用普通话。
因为通信协议的复杂,我们就大事化小,进行分层处理。
TCP/IP协议簇:实际上是一组协议
TCP:传输控制协议,Transmission Control Protocol
UDP:用户数据报协议,User Datagram Protocol
比较常用出名的便是TCP/IP协议。
TCP与UDP的对比
TCP
- 打电话
- 建立连接,稳定
- 三次握手、四次挥手
- 有客户端、服务端的概念
- 传输完成,释放连接,效率低
UDP
- 发短信
- 不建立连接,不稳定
- 没有客户端、服务端的明确界限
- 不管有没有准备好都可以发送信息
- DDOS饱和暴力攻击就是UDP协议形式
TCP通信步骤
客户端
- 连接服务器Socket
- 发送消息
服务端
- 建立服务端口ServerSocket
- 等待用户连接 serverSocket.accept()
- 接受用户消息
代码示例
TCP协议下的通讯
public class TCPClient {
public static void main(String[] args) throws IOException {
//1、客户端知道服务器的地址和端口号,建立连接。Socket是客户端套接字
try (Socket socket = new Socket("localhost", 9999)) {
try (OutputStream outputStream = socket.getOutputStream()) {
/* Scanner scanner = new Scanner(System.in);
System.out.println("!");
//服务端无法读取到所以数据,只能读取最后一部分,只要scanner不断开,也就是客户端不结束,服务端的读取流就一直阻塞着
while (scanner.hasNext()) {
if ("exit".equalsIgnoreCase(scanner.next())) {
scanner.close();
System.out.println("客户端退出聊天室!");
break;
}
//发送消息IO流
outputStream.write(scanner.next().getBytes());*/
outputStream.write("客户端可以发送信息给智能小白了".getBytes());
// }
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException {
//服务端通过ip地址和端口号创建一个服务端套接字 ServerSocket
try (ServerSocket serverSocket = new ServerSocket(9999)) {
//等待客户端连接
try (Socket socket = serverSocket.accept()) {
//获取客户端的读字节流,读取客户端的消息
try (InputStream is = socket.getInputStream()) {
//管道流
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
System.out.println(outputStream.toString());
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 客户端可以发送信息给智能小白了
*/
}
TCP协议下上传文件
public class TCPUploadClient {
public static void main(String[] args) throws Exception {
//1创建Socket连接
Socket socket = new Socket("127.0.0.1", 8090);
//2、创建输出流
OutputStream os = socket.getOutputStream();
//没有写file的路径名时默认是相对路径,即项目根目录下的位置,springboot-demo下
//读取文件
FileInputStream is = new FileInputStream(new File("asfasf.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
//通知服务器我结束发送消息了,结束输出流,这样服务器那边的就不会阻塞等待了
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
System.out.println(outputStream.toString());
//先开后关
inputStream.close();
outputStream.close();
is.close();
os.close();
socket.close();
}
//文件传输完成了!再见!
}
public class TCPUploadServer {
public static void main(String[] args) throws Exception {
//创建服务ServerSocket
ServerSocket serverSocket = new ServerSocket(8090);
//监听客户端
Socket socket = serverSocket.accept();
//获取输入流
InputStream is = socket.getInputStream();
//创建输出文件名及输出流
FileOutputStream os = new FileOutputStream(new File("hello.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
//通知客户端文件传输完成,即向客户端写消息
//先拿到输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件传输完成了!再见!".getBytes());
//关闭资源
outputStream.close();
os.close();
is.close();
socket.close();
serverSocket.close();
}
}
UDP
UDP的客户端Client可以直接发生消息给服务端或者其他人,不需要建立连接,因此就不需要要求服务端或其他人是在线的,也能发生成功;而TCP发生消息需要先建立连接,这时候如果连接的服务器不可用(比如没有启动起来),TCP的客户端启动时就会报错。如下:
Exception in thread "main" java.net.ConnectException: Connection refused: connect
但是,如果UDP接收方,比如服务器不可用的话,是接收不到数据的,所以理论上虽然不会报错,但是UDP服务器也是要先启动起来等待接受数据的。
代码示例
UDP协议下通讯聊天——发包进行聊天
首选创建发送消息和接受消息两个UDP工具类,然后创建两个用户对象,分别创建两条线程监听执行。
接受API类
public class UDPChatReceiver implements Runnable {
private DatagramSocket datagramSocket;
private int fromPort;
private String userName;
public UDPChatReceiver(int fromPort, String userName) {
this.fromPort = fromPort;
this.userName = userName;
try {
//1创建一个数据包套接字DatagramSocket,开放接收端口
datagramSocket = new DatagramSocket(fromPort);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
//准备容器接收数据
byte[] buffer = new byte[1024];
//2创建接收数据包,传入缓存容器
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//3阻塞接收数据
datagramSocket.receive(packet);
System.out.println(packet.getAddress());
//4读取数据后将字节流转字符串即可
String data = new String(packet.getData());
System.out.println(userName + ":" + data);
if ("exit".equalsIgnoreCase(data)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//5关闭流
datagramSocket.close();
}
}
}
发送API类
public class UDPChatSender implements Runnable {
//目标方的IP
private String toIP;
//目标方的端口
private int toPort;
//自己的端口
private int fromPort;
private DatagramSocket datagramSocket;
private BufferedReader reader;
public UDPChatSender(String toIP, int toPort, int fromPort) {
this.toIP = toIP;
this.toPort = toPort;
this.fromPort = fromPort;
try {
//1创建一个数据包套接字DatagramSocket
datagramSocket = new DatagramSocket(fromPort);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
//读取控制台数据
String str = reader.readLine();
//2创建一个数据包用于发送 ,包含数据及发送的目标IP和端口
DatagramPacket packet = new DatagramPacket(str.getBytes(), 0, str.getBytes().length, InetAddress.getByName(toIP), toPort);
//3发送数据包
datagramSocket.send(packet);
if ("exit".equalsIgnoreCase(str)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4关闭流
datagramSocket.close();
}
}
}
用户对象
public class Customer {
public static void main(String[] args) {
new Thread(new UDPChatReceiver(9800,"销售")).start();
new Thread(new UDPChatSender("localhost",9500,9601)).start();
}
}
public class Sale {
public static void main(String[] args) {
//接收线程
new Thread(new UDPChatReceiver(9500,"客户")).start();
//发送线程
new Thread(new UDPChatSender("localhost",9800,9600)).start();
}
}
java执行class要注意带包名时要退回到包名根路径下再执行java 包名.文件名 才能执行文件
Tomcat
Tomcat日志乱码是因为Windows默认编码是GBK,更改conf目录下的logging.properties文件即可。
URL
统一资源定位符:定位互联网上的某一个资源,定位资源用的。
DNS域名解析指把域名解析成IP地址,如 www.kugou.com ——》 117.27.241.66
URL格式
协议://IP地址:端口号/项目名/资源
代码示例——url资源下载
网络爬虫的本质就是爬取各种资源文件的URL地址,然后创建连接,打开下载流下载到地址来,或者是爬取到数据来进行分析。
public class URLTest {
public static void main(String[] args) throws Exception {
URL url1 = new URL("https://www.bilibili.com/video/BV1LJ411z7vY?p=12");
//需要注意的是host如果是域名则获取的主机还是域名,没有端口的话默认是-1
System.out.println(url1.getProtocol());//协议
System.out.println(url1.getHost());//主机
System.out.println(url1.getPort());//端口
System.out.println(url1.getPath());//文件
System.out.println(url1.getFile());//全路径
System.out.println(url1.getQuery());//参数
/**
* https
* www.bilibili.com
* -1
* /video/BV1LJ411z7vY
* /video/BV1LJ411z7vY?p=12
* p=12
* null
*/
//创建一个url对象,通过这个url对象可以进行访问、资源下载、爬虫抓取数据等操作
URL url = new URL("https://webfs.yun.kugou.com/202102241417/c9463283bf6cecf8eead45be1614a275/part/0/960125/KGTX/CLTX001/a46e805441a932ed0f5ef78dd229f688.mp3");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream is = connection.getInputStream();
FileOutputStream os = new FileOutputStream(new File("hope.mp3"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
os.write(buffer);
}
os.close();
is.close();
connection.disconnect();
}
}