【高级编程】网络编程 基于 TCPUDP 协议的 Socket 编程
文章目录
- IP地址
- Socket
- 基于 TCP 协议的 Socket 编程
- 基于 UDP 协议的 Socket 编程
IP地址
IP地址(Internet Protocol):唯一标识网络上的每一台计算机
IP地址的组成:32位,由4个8位二进制数组成
11000000.10101000.00000001.11001000 ==> 192.168.1.200
IP地址 = 网络地址 + 主机地址
- 网络地址:标识计算机或网络设备所在的网段
- 主机地址:标识特定主机或网络设备
8位 + 24位 | 前8位取值范围 | |
---|---|---|
A类 | 网络 主机 主机 主机 | 1~126 |
B类 | 网络 网络 主机 主机 | 128~191 |
C类 | 网络 网络 网络 主机 | 192~223 |
D类 | 用于组播通信 | 224~239 |
E类 | 用于科研 | 240~255 |
IP地址的配置和检测
查看IP地址,检测网络是否畅通
-
查看本机的IP地址:
ipconfig
-
测试网络是否通畅:
ping 目标IP地址
DNS域名解析
DNS:Domain Name System,域名系统
网络服务器
通常指在网络环境下,具有较高计算能力,能够提供用户服务功能的计算机
网络服务器 ===> 邮件服务器 Web服务器
- Microsoft IIS
- APACHE
- Apache Tomcat
网络通信协议
为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合
OSI七层网络模型 | TCP/IP四层概念模型 | 协议 | |
---|---|---|---|
应用层 | 应用层 | 规定了数据的传输格式 | HTTP FTP TFTP NFS WAIS SMTP |
表示层 | Telnet Rlogin SNMP | ||
会话层 | SMTP DNS | ||
传输层 | 传输层 | 端口到端口的连接通信 | TCP UDP |
网络层 | 网络层 | 引入一套新的地址来区分不同的广播域 | ICMP IP ARP RARP AKP UUCP |
数据链路层 | 数据链路层 | 定义电信号的分组方式 | FDDI Ethernet Arpanet PDN SLIP PPP |
物理层 | 基于电器特征的高低电压(电信号)高电压代表1 低电压代表0 | IEEE 802.1A IEEE 802.2到IEEE 802.11 |
TCP VS UDP
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
![]() | ![]() |
TCP可靠性源于序列号和确认序列号,UDP不能保证数据交付的可靠性
Socket
Socket 是一种网络通信的基础工具,它用于在计算机网络上进行数据传输。简而言之,Socket 提供了应用程序与网络之间的接口,使得应用程序可以通过网络发送和接收数据。
Socket 的底层机制复杂,Java 平台提供了一些简单的 API,可以更简单有效的使用Socket 开发而无需了解底层机制
-
通信链路的端点就被称为 " 套接字 "(英文名Socket)
-
是提供给应用程序的接口
Socket
类是 java.net
包的一部分,用于实现基于TCP的网络通信。
java.net包
-
Socket
-
ServerSocket
-
DatagramPacket
-
DatagramSocket
-
InetAddress
-
…
基于 TCP 协议的 Socket 编程
基于TCP 协议的 Socket 网络通信:用来实现双向安全连接网络通信
三次握手(建立连接) | 四次挥手(关闭连接) |
---|---|
A -> B 携带syn数据包 | A -> B 发送fin包 |
B -> A 同意连接 发送syn+ack | B -> A 发送ack包 进入等待关闭状态 |
A -> B 再次发送syn数据包 | B -> A 发送fin包 进入最后确认状态 |
A -> B 回复ack包 进入超时等待状态 等待结束关闭A B收到ack后立即关闭连接 |
Socket 通信模型
- 进行网络通信时,Socket 需要借助数据流来完成数据的传递工作
Socket 网络编程一般可以分成如下步骤进行
0、创建Socket 网络编程模型:客户端/服务器(C/S)
- 服务器端创建一个
ServerSocket
对象 指定端口号ServerSocket
负责监听客户端的连接请求。客户端创建一个Socket
对象 指定服务器的地址和端口号Socket
负责与服务器建立连接
1、建立连接
- 服务器端使用
ServerSocket
的accept()
方法等待客户端连接。这个方法会阻塞直到一个客户端连接进来
2、打开 Socket 关联的输入输出流
- 服务器端通过
Socket
对象获取输入输出流(InputStream
和OutputStream
) 用于接收和发送数据。客户端通过Socket
对象获取输入输出流 用于与服务器通信
3、数据流中读写信息
- 使用
InputStream
和OutputStream
(或者更高层次的BufferedReader
和PrintWriter
)进行数据传输 服务器和客户端可以通过这些流进行数据的读取和写入
4、关闭所有的数据流和 Socket
- 在通信完成后,双方应当关闭流和
socket
连接,以释放资源
// 传递对象信息 序列化
ObjectOutputStream oos = new ObjectOutputStream(...);
oos.writeObject(...);
ObjectInputStream ois = new ObjectInputStream(...);
Object = ois.readObject();
举例
使用 java.net
包来创建一个简单的 TCP 客户端-服务器应用程序。
UserServer
代表服务器端,UserClient
代表客户端。
public class UserServer {
public static void main(String[] args) {
// 假设服务器端口号为 8888
System.out.println("---服务器---");
try {
// 构建服务器
ServerSocket serverSocket = new ServerSocket(8888);
// 监听客户端发送来的信息 获得socket
Socket socket = serverSocket.accept();
// 获得字节流
InputStream is = socket.getInputStream();
// 读取数据
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
sb.append(new String(buffer, 0, bytesRead, "UTF-8"));
}
// 输出消息
String msg = sb.toString();
System.out.println("收到的消息: " + msg);
// 释放资源
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UserClient {
public static void main(String[] args) {
System.out.println("---客户端---");
try {
// 建立 socket 连接 指定服务器 IP + 端口号
Socket socket = new Socket("localhost",8888);
// 获得字节流
OutputStream os = socket.getOutputStream();
os.write("hello TCP!!!".getBytes());
// 释放资源
os.close();
socket.close();mm
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP 对象传递
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String age;
public Student(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{name='" + name + "', age='" + age + "'}";
}
}
public class StudentServer {
public static void main(String[] args) {
System.out.println("---服务器---");
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ObjectInputStream ois = null;
try {
// 构建服务器
serverSocket = new ServerSocket(8888);
// 监听客户端发送来的信息 获得socket
socket = serverSocket.accept();
// 获得字节流
is = socket.getInputStream();
ois = new ObjectInputStream(is);
// 读取 Student 对象
Student student = (Student) ois.readObject();
System.out.println(student.toString());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// 关闭资源
......
}
}
}
public class StudentClient {
public static void main(String[] args) {
System.out.println("---客户端---");
Socket socket = null;
ObjectOutputStream oos = null;
try {
// 建立 socket 连接 指定服务器 IP + 端口号
socket = new Socket("127.0.0.1", 8888);
oos = new ObjectOutputStream(socket.getOutputStream());
// 创建并发送 Student 对象
oos.writeObject(new Student("张三", "18"));
System.out.println("发送成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
多线程处理多请求 实现多客户请求
采用多线程的方式;一个专门负责监听的应用主服务程序;一个专门负责处理请求的线程程序。
public class LoginServer {
public static void main(String[] args) {
try {
System.out.println("\n服务端--------------------");
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = null;
while (true) {
socket = serverSocket.accept();
// 创建一个线程来处理登录请求
new LoginThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class LoginThread extends Thread {
private Socket socket;
public LoginThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
byte[] b = new byte[50];
is.read(b);
String msg = new String(b);
System.out.println(msg);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
public class LoginClient01 {
public static void main(String[] args) {
System.out.println("---客户端01---");
try {
Socket socket = new Socket("127.0.0.1", 8888);
OutputStream os = socket.getOutputStream();
os.write("用户名:张三,密码:123456".getBytes());
os.flush(); // 确保数据已经发送出去
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于 UDP 协议的 Socket 编程
DatagramSocket:用于发送和接收数据报(UDP数据包)。它提供了用于创建 UDP 套接字并通过其发送和接收数据报的功能。
DatagramPacket:表示要发送或接收的 UDP 数据包。它包含了数据及其目的地地址或来源地址。
基于 UDP 协议的 Socket 网络编程步骤
1、利用 DatagramPacket
对象封装数据包
2、利用 DatagramSocket
发送数据包
3、利用 DatagramSocket
接收数据包
4、利用 DatagramPacket
处理数据包
DatagramSocket
的 send(DatagramPacket packet)
方法将 DatagramPacket
数据包发送到网络上。DatagramSocket
的 receive(DatagramPacket packet)
方法从网络中接收数据,并将数据填充到 DatagramPacket
中。
public class UserServer {
public static void main(String[] args) {
System.out.println("---我是服务端---");
DatagramSocket ds = null;
DatagramPacket dp = null;
Scanner scanner = new Scanner(System.in);
try {
//接收信息
ds = new DatagramSocket(8888);
while (true){
byte info[] = new byte[1024];
//创建数据包 空包
dp = new DatagramPacket(info,info.length);
//监听
ds.receive(dp);
//拆包 获得数据
String msg = new String(dp.getData(), 0, dp.getLength()).trim();
System.out.println("客户端说:"+msg);
//发送
System.out.print("服务端请输入:");
String content = scanner.next();
//获得上个数据包的发送地址
SocketAddress sa = dp.getSocketAddress();
//封装数据包
dp = new DatagramPacket(
content.getBytes(),
content.getBytes().length,
sa
);
ds.send(dp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class UserClient {
public static void main(String[] args) {
//发送信息
Scanner scanner = new Scanner(System.in);
System.out.println("---我是客户端---");
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
while(true){
System.out.print("客户端请输入:");
String msg = scanner.next();
//封装数据包 字节数组 字节数组长度 地址对象InetAddress 端口号
dp = new DatagramPacket(
msg.getBytes() ,
msg.getBytes().length,
InetAddress.getByName("127.0.0.1"),
8888
);
//创建发送接收对象
ds = new DatagramSocket();
//发送数据包
ds.send(dp);
//接收
byte info[] = new byte[1024];
//创建数据包 空包
dp = new DatagramPacket(info,info.length);
//监听
ds.receive(dp);
//拆包 获得数据
String content = new String(dp.getData(), 0, dp.getLength()).trim();
System.out.println("服务器说:"+ content);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
ds.close();
}
}
}