网络编程
1.网络概述
1.1 IP地址
唯一标识网络上的每一台计算机
组成:4个8位二进制数组成
IP地址 = 网络地址 + 主机地址
c类前8位:192~223
1.2 DNS域名解析
由域名解析为ip地址
1.3网络通信协议(互联网协议/五层协议体系结构)
为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合
网络体系结构:
应用层(HTTP等)、传输层(TCP、UDP)、网络层、数据链路层、物理层
2.Socket(套接字)简介
意思:套接字
基于字节流实现
输出流必须使用字节流相关的实现,如对象输出流。
输入流首次获取是使用字节流,可嵌套为其他流!!!
Socket是应用层与TCP/IP协议通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket:通信链路的端点就被称为“套接字”(英文名Socket),是提供给应用程序的接口
面试题: TCP与UDP的区别
TCP:面向连接 可靠的传输服务 传输较慢 数据传输没有大小限制
UDP:面向无连接 不可靠的传输服务 传输较快 每一个数据包有大小限制,要求在64KB以内
TCP特征:
1. TCP协议是完全依赖IO流的,面向连接的!!!
数据传输没有大小限制
因为面向连接,所以数据安全
因为面向连接,所以速度较慢
TCP协议严格区别客户端和服务器
UDP协议下的socket:
1. 是按照数据包的方式来处理数据,面向无连接。
每一个数据包有大小限制,要求在64KB以内!
因为面向无连接,所有传输数据不安全,不稳定
因为面向无连接,所有传输速度贼快
UDP不区分客户端和服务器,只有发送端和接收端
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
2.1基于TCP协议的Socket编程
特点:
面向连接的 可靠 安全的传输协议
TCP的连接和断开涉及三次握手和四次挥手
软件的分类:
B/S Browser Server 基于浏览器的软件,代码编写相对简单 因为我们只需要编写一套代码发布与服务器就可以
C/S Client Server 基于客户端的软件,因为我们要两套程序 一个客户端 一个服务器 维护难度
输出流必须使用字节流相关的实现,如对象输出流。
输入流首次获取是使用字节流,可嵌套为其他流!!!
客户端实现步骤:
1.创建Socket实例(地址和端口号)
2.通过Socket实例获取字节输出流,必须使用字节流
3.使用字节流输出(支持byte数组与int,也可使用String类型转byte)
4.关闭此Socket输出流(socket.shutdownOutput) //
必须关闭输出流(面向连接的,不关闭,对方不知道何时接收),而输入流无所谓
5.关闭资源(finally中)
/**
* 客户端
* @author asus
*
*/
public class Client {
public static void main(String[] args) {
try {
// 地址就写当前电脑的ip地址
// 本机的ip地址 localhost 等价 127.0.0.1
Socket socket = new Socket("localhost", 8899);
System.out.println("客户端准备就绪。。。。。。");
OutputStream os = socket.getOutputStream();
os.write("服务器你好,睡了吗?".getBytes());
socket.shutdownOutput(); // 关闭写入 输出流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
System.out.println("好激动,服务器回话了" + br.readLine());
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭资源
}
}
服务端实现步骤:
1.创建ServerSocket实例(服务器端口号)
2.调用accept方法 程序阻塞 等待客户端连接,返回一个Socket
3.通过socket获取字节输入流,也可转换成字符流/缓冲流等
4.关闭资源
/**
* 服务端
* @author asus
*
*/
public class Server {
public static void main(String[] args) {
// 通过ServerSocket 创建一个服务器Socket
// 端口号 表示每一个应用程序唯一的编号
// 一些应用程序有默认的端口号 比如 tomcat 8080 mysql 3306
// 端口号取值范围 0~ 65535 我们在定义端口号的时候 9000以上都可以使用
try {
ServerSocket ss = new ServerSocket(8899);
System.out.println("服务器启动完毕,等待连接。。。。。");
Socket socket = ss.accept(); // 此时调用accept方法 程序将会阻塞 等待客户端连接
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = br.readLine();
System.out.println("客户端发送的信息是:" + info );
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭资源
}
}
2.2基于TCP实现对象的传递
对象实例化,实现serializable接口
客户端:将字节输出流转换成对象输出流,其仍属于字节流
服务器:将字节输入流转换成对象输入流
public class Client {
public static void main(String[] args) {
System.out.println("客户端就绪。。。");
try {
Socket socket = new Socket("localhost", 9998);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Student("艺博", 180));
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(9998);
System.out.println("服务器就绪。。。");
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
Student student = (Student) ois.readObject();
socket.shutdownInput();
System.out.println(student);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public class Student implements Serializable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
2.3基于TCP实现多线程处理请求
1.服务器必须处于开启状态,使用死循环
2.业务处理代码放入线程类run方法中
3.可以使用多个线程分别回应客户端,在服务器端创建线程实例
//Client2,Client3与Client1代码相同
/**
* 模拟多个客户端访问服务器 服务器 一一回应
* @author asus
*
*/
public class Client1 {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 8899);
OutputStream os = socket.getOutputStream();
os.write("服务器你好,我是1号客户端".getBytes());
socket.shutdownOutput();
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while(( line =br.readLine()) != null) {
System.out.println("服务器回话了:" + line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关闭资源
}
}
}
/**
* 服务器
* 因为目前是三个客户端来访问 所以我们要
* 1.服务器必须一值处于开启状态
* 2.可以使用多个线程来分别回应客户端
* @author asus
*
*/
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(8899);
System.out.println("服务器启动……");
while(true) {
Socket socket = ss.accept(); // 不同的客户端请求访问 返回的是不同的socket
// 执行完以上代码 程序继续 服务器将停止 所以我们编写一个线程类 代替服务器应答
new ServerThread(socket).start();;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 当前了你作为服务器应答的线程类
* 如何区分不同的请求?
* 通过Socket 本类中声明一个属性 Socket
* 这个Socket将作为run方法中回应客户端的重要依据
*/
public class ServerThread extends Thread{
private Socket socket;
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = this.socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
while(( line =br.readLine()) != null) {
System.out.println("客户端说的话: " + line);
}
OutputStream os = socket.getOutputStream();
os.write("客户端你好,睡了".getBytes());
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.4基于UDP的Socket编程
UDP 用户数据报协议 报文 非面向连接 不安全 不可靠 效率高
步骤:
1.利用DatagramPacket对象封装数据包,创建实例
2.用DatagramSocket发送或接受数据,send/receive
/**
* UDP 用户数据报协议 报文 数据包
* 非面向连接 不安全 不可靠 效率高
* @author asus
*
*/
public class Client {
public static void main(String[] args) {
String info = "hello 服务器";
System.out.println("客户端准备发送信息……");
byte [] datas = info.getBytes();
InetAddress localHost;
try {
localHost = InetAddress.getLocalHost();
// 1 发送的内容 ---包裹内容
// 2 内容大小 --- 包裹的重量
// 3 地址 --- 地址
// 4 端口号 --- 联系方式
DatagramPacket dp = new DatagramPacket(datas, datas.length, localHost, 8899);
// 创建DatagramSocket 对象用于发送数据
DatagramSocket ds = new DatagramSocket();
ds.send(dp); // 通过send方法将打包好的内容发送
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 服务器
* @author asus
*
*/
public class Server {
public static void main(String[] args) {
try {
DatagramSocket ds = new DatagramSocket(8899);
System.out.println("服务器启动……");
byte [] bag = new byte[1024];
// 1 接收内容用的byte数组 --- 准备装快递包裹
// 2 数组的长度 --- 包裹大小
DatagramPacket dp = new DatagramPacket(bag, bag.length);
ds.receive(dp); // 接收包裹
// 拆快递的过程
System.out.println(new String(dp.getData(),0,dp.getData().length));
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. InetAddress
概念:表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法