文章目录
前言
网络编程我个人觉得难度还是不大的,主要就是理清楚连接和发送需要哪些过程,还有需要 io 流基础。
网络编程三要素
Ip
ip 是上网设备在网络中的地址,是唯一的,就是分配给上网设备的数字标签
因为 ip 地址太难记了,所以 ip 地址会映射成 域名/主机名
采用 http 协议
Ipv4
互连网通信协议第四版
采用32位地址长度,分成四组,每组最大 255
Ipv4 地址分类
- 公网地址(万维网使用)和私有地址(局域网使用)
- 192.168 开头就是私有地址
127.0.0.1 表示本机
采用点分十进制表示法
常见 CMD 命令:
- ipconfig: 查看本机 ip 地址
- ping:检查网络是否连通
Ipv6
由于 Ipv4 模式下 Ip 的总数优先,所以采用 128 位置长度,分成八组,足以给地球上每一粒沙子分配
采用 冒分十六进制表示法
端口号
- 应用程序在设备中唯一的标识
- 由两个字节表示整数,取值返回:0 ~ 65535
其中 0 ~ 1023 之间的端口号被一些知名网络服务或应用占用,比如:ssh 22,ftp 21,所以我们自己使用的话,使用 1024 及以上端口
一个端口号只能被一个应用程序使用
常见的网络程序端口号
- tomcat:8080
- mysql:3306
- oracle:1521
- sqlserver:1433
协议
人交流依靠的语言也算一种协议,所以网络也有自己的协议,程序才懂
这里就不谈 OSI 和 TCP/IP 模型了
TCP 协议
- 面向连接 的通信协议
- 速度慢,没有大小显示,数据安全
三次握手(连接)
- 第一次握手:客户端 向 服务器 发出 连接请求(等待服务器确认)
- 第二次握手:服务器 向 客户端 返回 一个响应(告诉客户端收到了请求)
- 第三次握手:客户端 向 服务器 再次发出 确认信息 (连接建立
简单来说就是
- 客户端 发 请求
- 服务端 收到 请求, 响应回去
- 客户端 再次请求,(建立连接)
四次挥手(断开连接)
- 第一次挥手:客户端 向 服务端 发出 取消连接请求
- 第二次挥手:服务器 向 客户端 返回 一个响应
- 第三次挥手:服务器 向 客户端 发出 确认取消信息(将最后的数据处理完毕后)
- 第三次挥手:客户端 发送 确认取消连接了信息
简单来说就是
- 客户端 发 请求
- 服务端 收到 请求,响应回去
- 服务端 处理完信息,再次确认
- 客户端 取人 (取消连接)
UDP 协议
- 面向无连接 的通信协议
- 速度快,一次最多发送 64 k , 数据不完全,容易丢数据
InetAddress 类
InetAddress 类就代表一个 ip 对象
获取 ip 对象
- public static InetAddress getByName(String host)
public class client {
public static void main(String[] args) throws IOException {
//通过 ip 地址获取 ip 对象
InetAddress ip1 = InetAddress.getByName("127.0.0.1");
//通过 主机名 获取 ip 对象
InetAddress ip2 = InetAddress.getByName("DESKTOP-B88KGEA");
//通过 域名获取 ip 对象
InetAddress ip3 = InetAddress.getByName("www.baidu.com");
}
}
InetAddress.getLocalHost(); 是获取本地 ip 对象,也就是 127.0.0.1
获取到 ip 对象后,得到信息相关方法
public class client {
public static void main(String[] args) throws IOException {
//通过 ip 地址获取 ip 对象
InetAddress ip1 = InetAddress.getByName("127.0.0.1");
//获取 ip 对象的域名/主机名
String hostName = ip1.getHostName();
System.out.println(hostName);
//获取 ip 对象 对应的地址
String ipAddress = ip1.getHostAddress();
System.out.println(ipAddress);
}
}
netstat 常用指令
- netstat -an 可以查看当前主机网络情况,包括端口监听情况,网络连接情况
- netstat -an | more 可以分页显示
- 要求在 dos 控制台下执行
- ctrl + c 退出
0 0 0 0 也表示本机,冒号后面是端口
外部地址:连接到主机的连接,也显示 ip 地址和端口号
如果由一个外部程序(客户端)连接到该端口,就会显示一条连接信
Socket(套接字)
- 网络应用程序一般 都用 Socket 开发
- 通信的两端都要有 Socket,它是两台机器通信的端点
- Socket 运行程序把网络连接当成一个流,数据在两个 Socket 间通过 IO 传输。
TCP 编程
获取 Socket类 中 获取字节输入输出流的两个方法
- public InputStream getInputStream()
- public OutputStream getOutStream()
注意
- 如果 socket 端点相关的 IO 输出,必须用 socket.shutdownOutput,打上结束标记,不然不知道啥时候输出完,一直等着,如果是 BufferedWriter 且使用了readLine 可以用 newLine 当结束标记
- 如果是有缓冲区的流记得,写出完第一时间刷新
public class Main {
public static void main(String[] args) throws IOException {
Socket socket = new Socket();
//两个 socket端点 之间的输出流
InputStream is = socket.getInputStream();
//两个 socket端点 之间的输入流
OutputStream os = socket.getOutputStream();
//结束标记
socket.socket.shutdownOutput();
}
}
经常配合转换流,和缓冲流使用
public class Main {
public static void main(String[] args) throws IOException {
Socket socket = new Socket();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//包装成字符缓冲流提升效率
BufferedOutputStream bos = new BufferedOutputStream(os);
//转化成字符流,再用缓冲流包装
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
}
}
服务端
- public ServerSocket(int port) 类: 在某端口监听
- public Socket accept() ServerSocket 类中的方法:连接到端口返回 socket 对象
public class Main {
public static void main(String[] args) throws IOException {
//在 8888 端口监听
ServerSocket serverSocket = new ServerSocket(8888);
//有人连接,就返回一个 socket 对象,否则就阻塞在这里
Socket socket = serverSocket.accept();
}
}
客户端
- public Socket(String host, int port):连接某个端口
//连接 本机的 10022 端口
Socket socket = new Socket("127.0.0.1", 10022);
总结
- 服务端 监听,客户端连接后,就可以开始 IO 操作
- 先要运行服务端,因为连接端口的时候,服务端都没启动,端口咋链接
- 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是 TCP/IP 来分配的,是随机的
- 用完后记得关闭流 包括 socket
- ServerScoket 可以创建多个 Socket, 只要由一次 accept 就会返回一个 socket, 多个客户端连接,那就要多个 socket
实例
客户端
public class server {
public static void main(String[] args) throws IOException {
//1. 连接服务端 (ip, 端口)
//写服务器的ip地址或者域名
//解读:连接这台主机 的 9999 端口,连接成功返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//2. 连接上后,生成Socket,通过socket.getOutputStrem()
//得到和 这个socket 关联的 输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到数据通道
outputStream.write("hello server".getBytes());
//4. 关闭流对象和 socket 必须关闭
outputStream.close();
socket.close();
}
}
服务端
public class Inet {
public static void main(String[] args) throws IOException {
//1. 在本机 9999端口监听,等待连接
// 细节: 要求本机没有其他服务在监听999
ServerSocket serverSocket = new ServerSocket(9999);
//2. 当没有客户端连接 9999 端口时,程序会阻塞,等待连接
// 如果由客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
//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.关闭流
inputStream.close();
socket.close();
serverSocket.close();
}
}
UDP 编程
- DatagramPacket 对象封装了 UDP 数据报,在数据报中包含了发送端的 IP 地址 和 端口号 以及 接收端的 IP 地址和端口号
- UDP 协议中每一个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接
UDP 没有所谓的 客户端 和 服务端,两边是等价地位
相关 类和方法
- public DatagramSocket(int port)【Packet中的方法】:准备在哪个端口接收数据
- public DatagramPacket(byte buf[], int length)【Packet中的方法】:把数据放进某个 byte 数组内,length 是指,最大接收的数据量
- void receive(DatagramPacket p [Socket中的方法]):接收 packet 的数据
- void send(DatagramPacket p [Socket中的方法]):发送 packet 的数据
- int getLength()【Packet中的方法】:返回实际接收到的数据字节长度
- packet.getData();【Packet中的方法】==返回接收到的数据
- public DatagramPacket(byte buf[], int length, InetAddress address, int port)【Packet中的方法】:指定 byte[] 数组及大小,把数据装好,发送到指定的 ip 的 端口
基本流程
- 核心的两个类/对象 DatagramSocket 与 DatagramPacket
- 建立发送端,接收端(没有)
- 发送数据前,建立数据包,接收数据前,建立数据包
- 调用DatagramSocket 的发送,接收方法
- 关闭 DatagramSocket数据
发送的数据包
接收的数据包
实例
A
public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//1. 创建一个 DatagramSocket 对象,准备在9999接收数C据
DatagramSocket socket = new DatagramSocket(9999);
//2. 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet对象
//老师提示: 当有数据包发送到 本机的9999端口时,就会接收到数据
// 如果没有数据包发送到 本机的9999端口, 就会阻塞等待.
System.out.println("接收端A 等待接收数据..");
socket.receive(packet);
//4. 可以把packet 取出数据,并显示.
int length = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到数据
String s = new String(data, 0, length);
System.out.println(s);
//===回复信息给B端
//将需要发送的数据,封装到 DatagramPacket对象
data = "好的, 明天见".getBytes();
//说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998);
socket.send(packet);//发送
//5. 关闭资源
socket.close();
System.out.println("A端退出...");
}
}
B
public class UDPSenderB {
public static void main(String[] args) throws IOException {
//1.创建 DatagramSocket 对象,准备在9998端口 接收数据
DatagramSocket socket = new DatagramSocket(9998);
//2. 将需要发送的数据,封装到 DatagramPacket对象
byte[] data = "hello 明天吃火锅~".getBytes(); //
//说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9999);
socket.send(packet);
//3.=== 接收从A端回复的信息
//(1) 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf, buf.length);
//(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet对象
//老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据
// 如果没有数据包发送到 本机的9998端口, 就会阻塞等待.
socket.receive(packet);
//(3) 可以把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端退出");
}
}