网络编程;InetAddress类;UDP协议和TCP协议;Socket编程;UDP编程;TCP编程 (Java Day26)
一,网络编程概念
- 网络:就是不同地域,不同型号、不同ip等多台计算机相互连接沟通形成的通信系统
- 元素:
- 看得见的元素:网线、交换机、计算机、内存条等一系列的硬件设备
- 看不见的元素:传输协议、io流、运行的程序等
- 网络编程:基于计算机网络来进行数据的接收,发送,处理等操作的过程会使用不同的语言来实现。比如 java c++ 等
-
网络编程三要素:ip地址、端口号、传输协议
-
IP地址
- 概述:计算机在网络中IP协议分配给每一台计算机的唯一标识。【他代表了计算机在网络中唯一地址。通过ip可以找到想要找的计算机】
- 作用:能够在网络准确找到计算机
- 分类:
IPv4:是由四个字节组成的一段序列,来表示计算机的标识,字节和字节之间使用.隔开。一个字节8位,4个字节是32位,一共有2^32个ip。大概42亿个ip,也就是说他可以给42亿台计算机分配地址。
- 比如:192.168.1.148
- 一般前三个字节代表的是子网段的,最后一个字节代表计算机的段号。每一个字节都是0到255之间的数
IPv6:8组16进制的数组成的一段序列,来表示ip地址。组与组之间使用 :隔开。每组数有16种变化,一共128位,2^128个ip,号称全世界每一粒沙子都有一个ip。
- 比如:fe80::741f:ab6d:2237:20af%13,
- 如果是0可以省略,一般是连续为0.
-
端口号【重点】
- 概述:就是一个数字,是计算机中正在运行的进程的编号。端口号不可以重复。他是由2个字节大小的数字来表示,0到65535的范围。
- 作用:能够通过他准确的找到计算机中正在运行的进程
- 端口号的分配:
- 程序在启动的时候计算机随机自动分配端口号
- 程序中人为的指定端口号 比如 : 第二阶段 安装 mysql 时会有一个端口号设置 3306 【默认的】一般情况下默认不改
- 常用的端口号:
- 系统程序:0到1024
- MySQL:3306
- Oracle:1521
- Tomcat:8080
- QQ:4000
-
通信协议
- 概述:数据在传输过程中接收、发送,处理等操作是有规则。不同的场景有不同的协议,往往不同的协议协同合作维护网络编程的环境。
- 网络分层:分工作业
- 应用层:对数据的发送和接收进行处理工作 HTTP协议、HTTPS协议、FTP协议。
- 传输层:把数据从一端传输到另一端 TCP协议 UDP协议【今天要讲的协议】
- 网络层:规划线路连接 IP协议
- 物理层:相关的硬件设施
二,InetAddress类
- 概述:此类表示互联网协议 (IP) 地址。【对ip的相关特征和行为的描述,一般包括主机和IP地址】
- 获取对象的方式:【静态方法】
-
getAllByName(String host)
: 通过主机名称获取对应的所有IP地址对象的数组 -
getByAddress(byte[] addr)
:通过IP地址的字节数组获取IP地址的对象 -
getByAddress(String host, byte[] addr)
:通过主机名称和IP地址的字节数组获取IP地址的对象 -
getByName(String host)
:通过主机名称获取对应的IP地址对象 -
getLocalHost()
:获取本地主机的IP对象
代码示例:
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
public class Demo_InetAddress {
public static void main(String[] args) throws UnknownHostException {
InetAddress[] allByName = InetAddress.getAllByName("APPLE-NO-MBP"); // 主机名
System.out.println(Arrays.toString(allByName)); // [APPLE-NO-MBP/192.168.43.61],获取主机名称和IP地址
// 127.0.1 本机的IP地址 对应的字节数组 127 0 0 1
byte[] bs = { 127, 0, 0, 1 };
InetAddress address = InetAddress.getByAddress(bs);
System.out.println(address); // 127.0.0.1,将数组变为IP地址,数组与IP地址之间的转化
InetAddress address2 = InetAddress.getByAddress("APPLE-NO-MBP", bs);
System.out.println(address2); // APPLE-NO-MBP/127.0.0.1,显示哪台机器下面的IP,包括主机名称和IP地址
InetAddress address3 = InetAddress.getByName("APPLE-NO-MBP");
System.out.println(address3); // APPLE-NO-MBP/192.168.43.61 获取的是主机和IPV4地址
InetAddress host = InetAddress.getLocalHost();
System.out.println(host); // apple-no-MBP/192.168.43.61, 获取自己电脑的IP地址
- 获取对象的属性常用方法:
-
getAddress()
:返回对象的原始IP地址 -
getHostName()
: 返回对象的主机名称 -
toString()
:将ip地址转换为字符串
代码示例
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
public class Demo_InetAddress {
public static void main(String[] args) throws UnknownHostException {
InetAddress host = InetAddress.getLocalHost();
System.out.println(host); // apple-no-MBP/192.168.43.61, 获取自己电脑的IP地址
// 常用方法
byte[] bs2 = host.getAddress(); // IP 的字节数组
System.out.println(Arrays.toString(bs2)); // [-64, -88, 43, 61], 因为192超出了字节的范围,溢出了
InetAddress address5 = InetAddress.getByAddress(bs2);
System.out.println(address5); // /192.168.43.61 转回为数组
String name = host.getHostName();
System.out.println(name); //apple-no-MBP 返回对象的主机名称
System.out.println(host.toString()); //apple-no-MBP/192.168.43.61, 将ip地址转换为字符串
}
}
三,UDP协议和TCP协议
- 概述:是传输层的协议,是数据端到端的协议
- UDP: 是面向无连接的协议。不用端和端之间联系就进行数据的传输。比如:QQ发消息、短信、微博留言、邮件等方式。所以不用管对方在不在线,直接操作数据,但是数据发出去之后有可能收不到,安全性低,效率高。不区分客户端和服务端,只有发送端和接收端。
- 特点:
- 数据不安全
- 效率高
- TCP:面向连接的传输协议。要求对方必须在线并且连接成功。区分客户和服务,把发送数据段叫做客户端,接收数据端就做服务端。
- 如何进行连接的?
- 三次握手:瞬间就可以完成的。
- 第一次:发送端发出信号给接收端
- 第二次:接收端接到信号给出回应
- 第三次:发送端收到回应确定连接成功
- 特点:
- 数据安全性有保证
- 效率低
四,Socket编程【套接字编程】
- Socket : 套接字
- 套接字:传输层编程是端到端的编程,套接字就是端和端进行交流的中间服务
- 举例: 网购 客户 网上买了东西 商家 把东西邮寄给你
- 客户:发送端
- 商家: 服务端
- 快递:把货在商家和客户之间进行传输 就是套接字
- Socket编程在不同的协议下使用的套接字不一样。
- UDP协议:DatagramSocket
- TCP协议:
- 客户端:Socket
- 服务端:ServerSocket
五,UDP编程
- DatagramSocket:他是UDP编程的套接字 ,此类表示用来发送和接收数据报包的套接字
- 数据报包【DatagramPacket】:指包装了发送的数据和接收数据数据的一个容器。【发送和接收的内容、发送的ip地址、端口号等内容】
- 构造方法
- DatagramSocket():获取套接字对象【一般用于发送端】
- DatagramSocket(int port):获取套接字对象【一般用于接收端】
- 常用方法:
- send(DatagramPacket dp):发送数据报包出去
- receive(DatagramPacket dp):接收数据报包
- DatagramPacket :数据报包
- 通过构造方法来封装相关数据的构造方法:
-
DatagramPacket(byte[] buf, int offset, int length, InetAddress
address, int port)
:用于发送端 -
DatagramPacket(byte[] buf, int offset, int length)
:用于接收端
- 说明:
- buf:存放发送或接收数据内容的字节数组
- offset:buf 数组从哪个角标开始发送或接收,代表开始的角标值
- length:代表发送内容的长度,或者接受数组长度
- address:发送端发送目标ip地址
- port:发送的目标端口号
- 数据报包的常用方法【获取数据报包内部的数据的方法】
- getData():获取数据报包中的内容数组
- getLength():获取数据报包中有效内容的长度
代码示例1,UDP发送端
- 发送端编写步骤:
- 获取套接字对象
- 准备发送的相关数据
- 获取数据报包同时封装发送的数据
- 发送数据报包
- 准备接收数据的容器【字节数组相关数据】
- 准备一个接收数据的数据报包
- 接收数据
- 解析数据
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
// 发送端
public class UDP_Send {
public static void main(String[] args) throws Exception {
// 创建发送端的套接字对象
DatagramSocket socket = new DatagramSocket();
byte[] buf = "美女你好".getBytes();
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length, InetAddress.getLocalHost(), 9999);
socket.send(packet);
// 接收发送端的回复
byte[] buf1 = new byte[1024];
DatagramPacket packet1 = new DatagramPacket(buf, 0, buf.length);
socket.receive(packet1);
// 如何获取接收到的数据呢?
byte[] bs = packet1.getData();// 获取到数据报包中的读取内容的数组
int len = packet1.getLength();// 获取到有效内容的字节个数
System.out.println(new String(bs , 0 , len));
}
}
代码示例2:UDP接收端
- 接收端编写步骤:
- 获取套接字
- 准备接收容器【字节数组的相关数据】
- 接收数据到数据报包
- 解析接收到的数据【数据报包方法】
- 准备回应的内容【字节数组】
- 将内容添加到接收使用的数据报包中
- 将该数据报包发送回发送端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
// 接收端
public class UDP_Receive {
public static void main(String[] args) throws Exception {
// 创建套接字对象
DatagramSocket socket = new DatagramSocket(9999);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
socket.receive(packet);
// 如何获取接收到的数据呢?
byte[] bs = packet.getData();// 获取到数据报包中的读取内容的数组
int len = packet.getLength();// 获取到有效内容的字节个数
System.out.println(new String(bs , 0 , len));
// 接收端要给个回应
byte[] bs2 = "帅哥你好".getBytes();
packet.setData(bs2);
socket.send(packet);
}
}
六,TCP编程
- 客户端:
- Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点
- 构造方法
-
Socket(InetAddress address, int port)
:创建套接字对象,包含了目标的ip和端口号 - 发送和接收数据采用的是io流技术,套接字发送和接收数据,先获取对用的流对象。
- 常用功能:
-
getOutputStream()
:获取字节输出流【专门使用在客户端和服务端流的目标就是客户端和服务端】 -
getInputStream()
:获取字节输入流
- 服务端:
- ServerSocket:此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果
- 某些操作:ServerSocket套接字侦听客户端套接字是否入侵服务端,侦听到客户端的套接字,要和该套接字交流,找一个和客户端同一类型的套接字对象。
- 构造方法:
- ServerSocket(int port):创建服务端的套接字对象【内含端口号】
- 常用方法
-
accept()
:获取一个和客户端类型一致的套接字对象
代码示例1:TCP客户端
- 步骤:
- 创建Socket套接字对象
- 获取字节输出流
- 使用输出流写出内容
- 获取字节输入流
- 创建读取内容的字节数组
- 读取内容
- 解析内容
- 关闭流资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TCP_Send {
public static void main(String[] args) throws Exception, IOException {
// 创建键盘录入对象
Scanner scanner = new Scanner(System.in);
while(true) {
// 创建客户端的套接字对象【链接服务器 需要传入服务器的ip和端口号】
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
// 发送数据和接收数据需要相应的流对象
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
System.out.println("请输入您要说的话:");
String next = scanner.next();
byte[] bs = next.getBytes();
// 把数据发送出去
os.write(bs);
// 接收服务端的回复信息
byte[] bs1 = new byte[1024];
int i = is.read(bs1);
System.out.println(new String(bs1,0,i));
os.close(); //关流
is.close(); //关流
}
}
}
代码示例2:TCP服务端
- 步骤:
- 创建ServerSocket套接字对象
- 提供Socket套接字对象【好和客户端的套接字交流】
- 获取字节输入流
- 创建读取内容的字节数组
- 读取内容
- 解析内容
- 获取字节输出流
- 使用输出流写出内容
- 关闭流资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TCP_Receice {
public static void main(String[] args) throws Exception {
// 创建服务端的套接字对象
ServerSocket serverSocket = new ServerSocket(9999);
// 创建键盘录入对象
Scanner scanner = new Scanner(System.in);
while(true) {
// 根据客户端的套接字对象生成对应的对象来进行正常的交流
Socket socket = serverSocket.accept();
// 得到输入流对象可以读取【接收发送过来的数据】
InputStream is = socket.getInputStream();
byte[] bs = new byte[1024];
int i = is.read(bs);
System.out.println(new String(bs,0,i));
// 给客户端一个回复
OutputStream os = socket.getOutputStream();
System.out.println("请输入您要回复的话:");
String next = scanner.next();
byte[] bs2 = next.getBytes();
os.write(bs2);
os.close(); //关流
is.close(); //关流
}
}
}
- 加强:一个服务端 ,多个客户端的时候,单线程的时候,客户的需求服务端一个一个的处理,处理第一个客户需求的时候,其他的客户就得等着,处理完了才会处理下一个。同时接受多个客户的需求并进行处理
服务端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TCP_Receice {
public static void main(String[] args) throws Exception {
// 创建服务端的套接字对象
ServerSocket serverSocket = new ServerSocket(9999);
// 创建键盘录入对象
Scanner scanner = new Scanner(System.in);
while(true) {
// 根据客户端的套接字对象生成对应的对象来进行正常的交流
// 服务端监测到一个客户端对象来 就给你分配一条线程【安排一个窗口不用等】
Socket socket = serverSocket.accept();
// 分配新的线程
new Thread() {// 一个新的窗口
public void run() {
// 得到输入流对象可以读取【接收发送过来的数据】
InputStream is = null;
OutputStream os = null;
try {
is = socket.getInputStream();
byte[] bs = new byte[1024];
int i = is.read(bs);
System.out.println(new String(bs,0,i));
// 给客户端一个回复
os = socket.getOutputStream();
System.out.println("请输入您要回复的话:");
String next = scanner.next();
byte[] bs2 = next.getBytes();
os.write(bs2);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
}
客户端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TCP_Send {
public static void main(String[] args) throws Exception, IOException {
// 创建键盘录入对象
Scanner scanner = new Scanner(System.in);
while(true) {
// 创建客户端的套接字对象【链接服务器 需要传入服务器的ip和端口号】
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
// 发送数据和接收数据需要相应的流对象
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
System.out.println("请输入您要说的话:");
String next = scanner.next();
byte[] bs = next.getBytes();
// 把数据发送出去
os.write(bs);
// 接收服务端的回复信息
byte[] bs1 = new byte[1024];
int i = is.read(bs1);
System.out.println(new String(bs1,0,i));
os.close(); //关流
is.close(); //关流
}
}