Java高级编程–网络编程
文章目录
1.IP地址
1.1IP地址介绍
- 什么是IP地址?为什么需要IP地址?
- 两台计算机通信, 双方都必须有地址->IP地址
- 唯一标识网络上的每一台计算机
1.2IP地址的组成
-
32位,由4个8位二进制数组成
-
IP地址 = 网络地址 +主机地址
- 网络地址:标识计算机或网络设备所在的网段
- 主机地址:标识特定主机或网络设备
1.3IP地址的配置和检测
- 查看IP地址,检测网络是否畅通
- 查看本机的IP地址<ipconfig>
- 测试网络是否通畅<ping 目标IP地址>
1.4DNS域名解析
-
访问网站时,为什么输入网址而不是IP地址?
DNS:Domain Name System,域名系统
2.网络服务器
通常指在网络环境下,具有较高计算能力,能够提供用户服务功能的计算机
3.网络通信协议
为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合
五层协议
- 物理层:基于电器特性的高低电压(电信号)高电压代表1低电压代表0
- 数据链路层:定义电信号的分组方式
- 网络层:引入一套新的地址来区分不同的广播域
- 传输层:端口到端口的连接通信
- 应用层(应用层,表示层,会话层):规定了数据的传输格式http,ftp
4.Socket
4.1什么是Socket?
- Socket的底层机制复杂,Java平台提供了一些简单的API,可以更简单有效的使用Socket开发而无需了解底层机制
- 通信链路的端点就被称为“套接字”(英文名Socket)
- 是提供给应用程序的接口
4.2java.net包
- Socket
- ServerSocket
- DatagramPacket
- DatagramSocket
- InetAddress
4.3基于TCP协议的Socket编程
-
基于TCP协议的Socket网络通信
- 用来实现双向安全连接网络通信
-
Socket通信模型
- 进行网络通信时,Socket需要借助数据流来完成数据的传递工作
-
三次握手和四次挥手
-
三次握手 (建立连接) ps:假设A是客户端,B是服务器
1.(A->B)携带syn数据包
A问B你能收到我的消息吗?k
2.(B->A)如果B同意连接则回复消息同意,向A发送syn+ack
B回答我收到了,你能收到我的消息吗?k
3.(A->B)A在向B发送ack数据包
A回答我收到了,开始通信
-
为什么不能用两次握手?
- 服务器收到客户端的
SYN
并回复SYN-ACK
后,只能确认自己可收、可发。但服务器无法确认客户端是否能正常接收数据(因为客户端未回复ACK
),可能会导致连接不可靠、资源浪费或数据错误。
- 服务器收到客户端的
-
四次挥手(关闭连接) ps:假设A是客户端,B是服务器
1.(A–>B)发送fin包
我要关闭,等你确认
2.(B–>A)发送ack包,此时B会进入等待关闭状态
B说稍等,等待关闭。
3.(B–>A)发送fin包,进入最后确认状态。
确认完成,可以关闭了。
4.(A–>B)A收到后回复ack包,进入超时等待状态。等待结束关闭A,B收到ack后立即关闭连接。
A确认关闭,B立即关闭。
-
为什么不能用三次挥手?
- 去掉第四次挥手(客户端的ACK),服务端发送 FIN 后,如果没有收到客户端的 ACK,服务端会一直处于最后确认状态,服务端会认为客户端没有收到 FIN,从而无法确定连接已经完全关闭。TCP协议有重传机制,如果服务端没有收到客户端的 ACK,它会重新发送 FIN。如果去掉第四次挥手,服务端会一直重传 FIN,直到超时,这会导致不必要的资源浪费。
-
Socket网络编程一般可以分成如下步骤
package com.hz.ch09.tcplx;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* 客户端(向外写数据)
*/
public class ClientSocketTCP {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
OutputStreamWriter osw = null;
Scanner scanner = new Scanner(System.in);
try {
//1.创建客户端Socket对象
socket = new Socket("192.168.29.12", 8080);
//2.从Socket中获取一个输出流,往服务器上写数据
os = socket.getOutputStream();
//3.将字节输出流转为字符输出流
osw = new OutputStreamWriter(os);
//4.写数据
System.out.println("请输入要发送的内容");
String data = scanner.nextLine();
osw.write(data);
//5.释放资源
osw.close();
os.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package com.hz.ch09.tcplx;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端
*/
public class ServerSocketTCP {
public static void main(String[] args) {
try {
System.out.println("服务器启动了");
//1.创建服务器端的ServerSocket对象
ServerSocket serverSocket = new ServerSocket(8080);
//2.调用accept()方法接收客户端的Socket对象
Socket socket = serverSocket.accept();
//3.获取输入流
InputStream is = socket.getInputStream();
//4.将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(is);
char[] chars = new char[1024];
int len = 0;
while ((len = isr.read(chars)) != -1) {
String s = new String(chars, 0, len);
System.out.println(s);
}
//5.释放资源
isr.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
4.4Socket中实现对象的传递
-
序列化
package com.hz.ch09.tcplx3; import java.io.Serializable; public class UserInfo implements Serializable { private String userName; private String pwd; public UserInfo() { } public UserInfo(String userName, String pwd) { this.userName = userName; this.pwd = pwd; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "UserInfo{" + "userName='" + userName + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
package com.hz.ch09.tcplx3; import java.io.*; import java.net.Socket; import java.util.Scanner; public class ClientSocketTCP3 { public static void main(String[] args) { try { //1.创建一个Socket对象,指定服务器的IP地址和端口号 Socket socket=new Socket("127.0.0.1",8080); //2.获取输出流,向服务器端发送信息 OutputStream os=socket.getOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(os); oos.writeObject(getUserInfo()); //3.获取输入流,读取服务器端的回复信息 InputStream is=socket.getInputStream(); DataInputStream dis=new DataInputStream(is); System.out.println("收到服务器端的回复:"+dis.readUTF()); //4.关闭资源 dis.close(); is.close(); oos.close(); os.close(); socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } private static UserInfo getUserInfo(){ Scanner scanner=new Scanner(System.in); System.out.print("请输入用户名:"); String name=scanner.next(); System.out.print("请输入密码:"); String pwd=scanner.next(); UserInfo userInfo=new UserInfo(name,pwd); return userInfo; } }
package com.hz.ch09.tcplx3; import java.io.DataOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class ServerSocketTCP3 { public static void main(String[] args) { try { System.out.println("服务器已启动,等待连接......"); ServerSocket serverSocket = new ServerSocket(8080); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); ObjectInputStream ois = new ObjectInputStream(is); UserInfo userInfo = (UserInfo) ois.readObject(); System.out.println("收到客户端发来的消息:" + userInfo.toString()); String s = "登录失败"; if (check(userInfo)) { s = "登录成功"; } OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); dos.writeUTF(s); dos.close(); os.close(); ois.close(); is.close(); socket.close(); serverSocket.close(); } catch (Exception e) { throw new RuntimeException(e); } } public static boolean check(UserInfo userInfo) { if ("yy".equals(userInfo.getUserName()) && "0909".equals(userInfo.getPwd())) { return true; } return false; } }
4.5多线程处理多请求
- 实现多客户请求
- 采用多线程的方式
- 一个专门负责监听的应用主服务程序
- 一个专门负责处理请求的线程程序
package com.hz.ch09.tcplx6;
import java.io.Serializable;
public class Student implements Serializable {
private String name;
private String pwd;
public Student(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
package com.hz.ch09.tcplx6;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 6666);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
Student student = new Student("杨洋", "0909");
oos.writeObject(student);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package com.hz.ch09.tcplx6;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client2 {
public static void main(String[] args) {
try {
Socket socket=new Socket("127.0.0.1",6666);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(os);
Student student=new Student("陈楚生","0725");
oos.writeObject(student);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package com.hz.ch09.tcplx6;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class StuThread extends Thread {
private Socket socket;
public StuThread(Socket socket) {
this.socket = socket;
}
public void run() {
try {
InputStream inputStream = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(inputStream);
Student student = (Student) ois.readObject();
System.out.println(student);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.hz.ch09.tcplx6;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
System.out.println("----------服务器端----------");
try {
ServerSocket serverSocket = new ServerSocket(6666);
while (true){
Socket socket = serverSocket.accept();
StuThread stuThread=new StuThread(socket);
stuThread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.6基于UDP协议的Socket编程
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠 |
速度 | 慢 | 快 |
- 基于UDP协议的Socket网络编程步骤
package com.hz.ch09.udplx3;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client1 {
public static void main(String[] args) {
System.out.println("---------客户端1----------");
Scanner scanner = new Scanner(System.in);
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
//通过IP获得inetAddress对象
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
while (true) {
System.out.print("客户端1请输入要发送的内容:");
String msg = scanner.next();
//封装数据包
dp = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, inetAddress, 8888);
ds = new DatagramSocket();
ds.send(dp);
//准备空数据包
byte[] bytes = new byte[1024];
dp = new DatagramPacket(bytes, bytes.length);
//填充数据包
ds.receive(dp);
//解析数据包
String msg1 = new String(dp.getData(), 0, dp.getLength());
System.out.println("客户端1收到客户端2回复信息:" + msg1);
//关闭资源
ds.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.hz.ch09.udplx3;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;
public class Client2 {
public static void main(String[] args) {
System.out.println("---------客户端2----------");
Scanner scanner = new Scanner(System.in);
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
ds = new DatagramSocket(8888);
while (true) {
//准备空的数据包
byte[] bytes = new byte[1024];
dp = new DatagramPacket(bytes, bytes.length);
//填充数据包
ds.receive(dp);
//解析数据包
String msg = new String(dp.getData(), 0, dp.getLength());
System.out.println("客户端2收到客户端1的信息:" + msg);
System.out.print("客户端2请输入要回复的信息:");
String msg2 = scanner.next();
//客户端1的地址
SocketAddress sa = dp.getSocketAddress();
//封装数据包
dp = new DatagramPacket(msg2.getBytes(), 0, msg2.getBytes().length, sa);
//发生数据包
ds.send(dp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}