文章目录
- 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) {// 假设服务器端口号为 8888System.out.println("---服务器---");try {// 构建服务器ServerSocket serverSocket = new ServerSocket(8888);// 监听客户端发送来的信息 获得socketSocket 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;}@Overridepublic 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);// 监听客户端发送来的信息 获得socketsocket = 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;}@Overridepublic 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();}}
}