网络模型、通信协议、IP和端口、InetAddress类、网络编程练习
16. 网络编程
16.1 什么是网络
16.1.1 网络
- 由点和线构成,表示诸多对象间的互相联系;
16.1.2 计算机网络
- 为实现资源共享和信息传递,通过通信线路连接起来的若干主机(Host);
- 互联网:(Internet)点与点相连;
- 万维网:(WWW - World Wide Web)端与段相连;
- 网络编程:让计算机与计算机之间建立连接、进行通信;
16.2 网络模型
16.2.1 OSI
- OSI(Oper System Interconnection)开放式系统互联;
- 第一层:物理层为设备之间的数据通信提供传输信号和物理介质;(双绞线、光导纤维);
- 第二层:链路层在物理层上,通过规程或协议(差错控制)来控制传输数据的正确性;(MAC)
- 第三层:网络层负责定义了能够标识所有网络节点的逻辑地址;(IP地址)
- 第四层:传输层负责是否选择差错恢复协议、数据流重用、错误顺序重排;(TCP、UDP)
- 第五层:会话层负责使应用建立和维持会话,使通信在失效时继续恢复通信;(断点续传)
- 第六层:表示层负责定义转换数据格式及加密,允许选择以二进制或ASCII格式传输;
- 第七层:应用层负责文件访问和管理、可靠运输服务、远程操作服务。(HTTP、FTP、SMTP)
16.2.2 TCP/IP模型
- 一组用于实现网络互连的通信协议,将协议分成四个层次;
- 第一层:接接口层负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网、ADSL等等;
- 第二层:网络层负责分配地址和传送二进制数据,主要协议是IP协议;
- 第三层:传输层负责传送文本数据,主要协议是TCP、UDP协议;
- 第四层:应用层负责传送各种最终形态的数据,是直接与用户打交道的层,典型协议是HTTP、FTP等;
16.3 通信协议
TCP/UDP
- TCP协议:Transmission Control Protocol 传输控制协议
- 是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制。建立连接的过程需要三次握手,断开连接的过程需要四次挥手;
- UDP协议:User Datagram Protocol 用户数据报协议
- 是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,每个包的大小64KB;
16.4 IP与端口
16.4.1 IP
IP协议:Internet Protocol Address 互联网协议地址/国际协议地址
- 分配给互联网设备的数字标签(唯一标识);
IP地址分为两种:
- IPV4 : 4字节32为整数,并分成4段8位的二进制数,每8位之间用圆点隔开,每8位整数可以转换位一个0~255的十进制整数;
格式:D.D.D.D
例如:255.255.255.255 - IPV6 : 16字节128位整数,并分成8段十六进制数,每16位之间用圆点隔开,每16位整数可以转换为一个0~65535的十进制数;
格式:X.X.X.X.X.X.X.X
例如:FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF.FFFF
16.4.2 IPV4的应用分类
- A类:政府机构,1.0.0.1 ~ 126.255.255.254
- B类:中型企业,128.0.0.1 ~ 191.255.255.254
- C类:个人用户,192.0.0.1 ~ 223.255.255.254
- D类:用于组播, 224.0.0.1~ 255.255.255.254
- 回环地址: 127.0.0.1 , 指本机,一般用于测试使用;
- 查看IP命令:ipconfig
- 测试IP命令:ping D.D.D.D
16.4.3 Port
- 端口号:在通信实体上进行网络通讯的程序的唯一标识;
端口分类:
- 公认端口:0~1023
- 注册端口:1024~49151
- 动态或私有端口:49152~65535
常用端口:
- MySql:3306
- Oracle:1521
- Tomcat:8080
- SMTP:25
- Web服务器:80
- FTP服务器:21
16.5 网络编程
16.5.1 InetAddress类
- 概念:表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法;
方法:
- public static InetAddress getLocalHost() //获取本地主机地址对象
- public static InetAddress getByName(String host) //根据主机名称获得地址对象
- public static InetAddress[] getAllByName(String host) //获得所有相关地址对象
- public String getHostAddress() //获取IP地址字符串
- public String getHostName() //获得IP地址主机名
import java.net.InetAddress;
public class TestInetAddress {
public static void main(String[] args) throws Exception {
InetAddress local = InetAddress.getLocalHost();//获取本地主机
//获取本地主机的IP、名字
String ip = local.getHostAddress();
String name = local.getHostName();
// System.out.println(ip+"\t"+name);
//获取任意主机的IP地址对象 (IP、主机名)
// InetAddress local2 = InetAddress.getByName("www.baidu.com");
// System.out.println(local2.getHostAddress());
// System.out.println(local2.getHostName());
InetAddress[] addres = InetAddress.getAllByName("www.baidu.com");
for(InetAddress add: addres) {
System.out.println(add.getHostAddress());
}
}
}
输出结果:
39.156.66.14
39.156.66.18
16.5.2 基于TCP的网络编程
Socket编程:
- Socket(套接字)是网络中的一个通信节点;
- 分为客户端Socket与服务器ServerSocket;
- 通信要求:IP地址+端口号;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
//1.创建服务套接字
ServerSocket server = new ServerSocket(6666);
System.out.println("服务器已启动");
//2.调用accept等待客户端
Socket client = server.accept();
//3.通过客户端获取输入输出流
InputStream is = client.getInputStream();
InputStreamReader isr = new InputStreamReader(is , "UTF-8");
BufferedReader br = new BufferedReader(isr);
OutputStream os = client.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
PrintWriter pw = new PrintWriter(osw);
//4.读取数据
String message = br.readLine();
System.out.println("客户端:"+message);
//5.响应数据
pw.println("我是服务端,你好");
pw.flush();
//6.关闭
pw.close();
br.close();
client.close();
server.close();
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
//1.创建客户端,连接指定的IP+端口号
Socket client = new Socket("192.168.31.59",6666);
//2.获取输入输出流
InputStream is = client.getInputStream();
InputStreamReader isr = new InputStreamReader(is , "UTF-8");
BufferedReader br = new BufferedReader(isr);
OutputStream os = client.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
PrintWriter pw = new PrintWriter(osw);
//3.发送数据
pw.println("我是客户端");
pw.flush();
//4.接收相应数据
String message = br.readLine();
System.out.println("服务端说:"+message);
//5.关闭
br.close();
pw.close();
}
}
输出结果:
服务器已启动
客户端:我是客户端
服务端说:我是服务端,你好
16.5.3 开发步骤
- 建立通信连接(会话):
创建ServerSocket,指定端口号;
调用accept等待客户端接入; - 客户端请求服务器:
创建Socket,指定服务器IP+端口号;
使用输出流,发送请求数据给服务器;
使用输入流,接收响应数据到客户端(等待); - 服务器响应客户端:
使用输入流,接收请求数据到服务器(等待)
使用输出流,发送响应数据给客户端;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务器,接收多个客户端请求
* 主线程,接收请求
* 子线程,复制接收数据
* */
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket server =new ServerSocket(8888);
System.out.println("服务器已启动");
while(true) {
Socket client = server.accept();
DataThread dt = new DataThread(client);
dt.start();
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class DataThread extends Thread {
private Socket client;
public DataThread(Socket client) {
this.client = client;
}
Scanner input = new Scanner(System.in);
public void run() {
//接收数据
try {
InputStream is = client.getInputStream();
BufferedReader br = new BufferedReader( new InputStreamReader(is , "UTF-8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"));
while(true) {
String msg = br.readLine();
System.out.println(client.getInetAddress().getHostName()+"说:"+msg);
System.out.println("请输入回复:");
String callmsg = input.next();
pw.println(callmsg);
pw.flush();
if(msg.equals("byebye") || msg.equals("886")) {
System.out.println(client.getInetAddress().getHostName()+"退出了聊天");
break;
}
}
br.close();
pw.close();
} catch (IOException e) {
System.err.println(client.getInetAddress().getHostName()+"死机了");
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
Socket client = new Socket("192.168.31.59",8888);
OutputStream os = client.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os , "UTF-8"));
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));
Scanner input = new Scanner(System.in);
while(true) {
System.out.println("请输入:");
String msg = input.next();
pw.println(msg);
pw.flush();
String callmsg = br.readLine();
System.out.println("服务端回复:"+callmsg);
if(msg.equals("byebye") || msg.equals("886")){
break;
}
}
pw.close();
client.close();
System.out.println("您退出了");
}
}
输出结果:
服务器已启动
DESKTOP-JFO9FHB说:你好
请输入回复:
hi
DESKTOP-JFO9FHB说:byebye
请输入回复:
886
DESKTOP-JFO9FHB退出了聊天
请输入:
你好
服务端回复:hi
请输入:
byebye
服务端回复:886
您退出了
16.5.4 课堂案例
- 使用Socket编程实现服务器端注册:
注册信息保存在properties文件中;
注册成功后返回字符串“注册成功”;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/*
* 服务端
* 提供注册
* 接收客户端发送的数据zhangsan#123
* 保存在properties文件中
* */
public class Server {
private Properties userPros;//Map体系下的,Key和value都是String
public Server() {
userPros = new Properties();
File file = new File("Files/userPros.properties");
if(file.exists()) {//文件存在
try{
//把properties文件里的内容加载到properties集合里
userPros.load(new FileReader(file));
}catch(Exception e) {
e.printStackTrace();
}
}
}
public void registerUser() {
try {
//1.创建服务端
ServerSocket server = new ServerSocket(7777);
System.out.println("服务端已启动");
//2.接收客户端
Socket client = server.accept();
//3.输入流输出流
BufferedReader br = new BufferedReader(new InputStreamReader( client.getInputStream() , "UTF-8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"));
//4.接收数据 zhangsan#123
String user = br.readLine();
String[] users = user.split("#");
String username = users[0];
String password = users[1];
//对用户名进行判断
if(userPros.containsKey(username)) {
pw.println("用户已存在,请重新注册!");
pw.flush();
}else {
//不存在,先存在userPros集合里,再存入文件
userPros.setProperty(username, password);
//将集合加载到文件中
userPros.store(new FileWriter("Files/userPros.properties"), "用户信息");
pw.println("注册成功");
pw.flush();
}
pw.close();
br.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
//1.创建客户端
Socket client = new Socket("192.168.31.59",7777);
System.out.println("开始注册。。。");
Scanner input = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = input.next();
System.out.println("请输入密码:");
String password = input.next();
String userinfo = username +"#"+password;
//2.获得流
BufferedReader br = new BufferedReader(new InputStreamReader( client.getInputStream() , "UTF-8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8"));
//3.提交用户信息
pw.println(userinfo);
pw.flush();
//4.接收服务端的结果
String msg = br.readLine();
System.out.println(msg);
br.close();
pw.close();
}
}
public class Start {
public static void main(String[] args) {
Server server = new Server();
server.registerUser();
}
}
userPros.properties文件内容:
#\u7528\u6237\u4FE1\u606F
#Wed Mar 18 17:07:42 CST 2020
zhangsan=123
输出结果:
开始注册。。。
请输入用户名:
zhangsan
请输入密码:
123
用户已存在,请重新注册!
第二次注册:
开始注册。。。
请输入用户名:
lisi
请输入密码:
123
注册成功
注册成功后文件内容:
#\u7528\u6237\u4FE1\u606F
#Wed Mar 18 22:22:19 CST 2020
zhangsan=123
lisi=123
16.6 总结
计算机网络:
为实现资源共享和信息传递,通过通信线路连接起来的若干主机;
TCP协议:
是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制;
IP:
分配给互联网设备的数字标签(唯一标识);
Port:
在通信实体上进行网络通讯的程序的唯一标识;
Socket编程:
建立连接、接收请求、发送响应;