文章目录
网络编程的本质
- 网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机
网络编程的目的
- 直接或间接的通过网络协议与其他计算机实现数据交换,进行通讯
达到网络编程效果需要什么
- 如何准确的定位网络上的一台或多台主机(IP,端口号);定位主机上的特定应用
- 找到主机后如何可靠高效的进行数据传输
计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及外部设备,通过通信线路(有线或无线)连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
网络通信的要素
通信双方的地址:
- IP地址
- 端口号
规则(网络通信的协议):
- OSI参考模型:模型过于理想化,未能在因特网上进行推广
- TCP/IP参考模型:事实上的国际标准
IP
IP地址:类InetAddress
- IP可以唯一定位一台网络计算机
- 本机IP地址:127.0.0.1 :主机名称:localhost
IP地址的分类
-
方式一:IPV4/IPV6
- IPV4:如127.0.0.1 ,4个字节组成,4个0-255.2019年11月26日,全球所有43亿个IPv4地址已分配完毕(大约30亿在北美,4亿在亚洲),这意味着没有更多的IPv4地址可以分配给ISP和其他大型网络基础设施提供商。
- IPV6:如ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,128位(16个字节),写成8个无符号整数,每个整数用四个16进制位表示,数之间用冒号分开。
-
方式二:公网地址(万维网使用)/私网地址(局域网使用)
- 192.168.开头的就是私有地址,范围为192.168.0.0–192.168.255.255,专门为组织机构内部使用
-
域名:解决了记忆IP问题
测试IP地址类InetAddressg
package com.cheng.lesson01;
import java.net.InetAddress;
import java.net.UnknownHostException;
//测试IP
public class TestInetAddress {
public static void main(String[] args) {
try {
//查询本机地址
InetAddress InetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(InetAddress1);///127.0.0.1
InetAddress InetAddress4 = InetAddress.getLocalHost();
System.out.println(InetAddress1);///127.0.0.1
InetAddress InetAddress5 = InetAddress.getByName("localhost");
System.out.println(InetAddress5);//localhost/127.0.0.1
//查询网站IP地址
InetAddress InetAddress2 = InetAddress.getByName("www.baidu.com");
System.out.println(InetAddress2);//www.baidu.com/36.152.44.95
//常用方法
System.out.println(InetAddress2.getAddress());//[B@1b6d3586
System.out.println(InetAddress2.getCanonicalHostName());//36.152.44.95
System.out.println(InetAddress2.getHostAddress());//36.152.44.95
System.out.println(InetAddress2.getHostName());//www.baidu.com
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
端口
IP地址用来标识一台计算机,但是一台计算机上可能提供多种网络应用程序,如何来区分这些不同的程序呢?这就要用到端口。如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序,也就是应用程序的标识。
- 端口的表示是一个16位的二进制整数,2个字节,对应十进制的0~65535。
- 单个协议下,端口号不能冲突
端口分类
公有端口0~1023
- HTTP端口号:80
- HTTPS端口号:443
- FTP端口号:21
- Telent端口号:23
程序注册端口:1024~49151,分配给用户或者程序
- Tomcat端口号:8080
- MySQL端口号:3306
- Oracle端口号:1521
动态,私有端口:49152~65565(尽量不用)
常用cmd端口命令
netstat -ano #查看所有的端口
netstat -ano|findstr "3306"#查询某个端口
tasklist|findstr "3008"#查询特定端口的进程
端口类 InetSocketAddress测试
该类实现IP套接字地址(IP地址+端口号)它也可以是一对(主机名+端口号),在这种情况下将尝试解析主机名。 如果解决方案失败,那么该地址被认为是未解决的,但在某些情况下仍可以使用,例如通过代理连接。
package com.cheng.lesson01;
import java.net.InetSocketAddress;
public class TestInetSocketAddress {
public static void main(String[] args) {
// 两个参数,地址,端口号
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);
System.out.println(socketAddress);// /127.0.0.1:8080
InetSocketAddress socketAddress1 = new InetSocketAddress("localhost", 8080);
System.out.println(socketAddress1);//localhost/127.0.0.1:8080
System.out.println(socketAddress.getAddress());///127.0.0.1
System.out.println(socketAddress.getHostName());///127.0.0.1
System.out.println(socketAddress.getPort());//8080
}
}
通信协议
通信协议是指双方实体完成通信或服务所必须遵循的规则和约定。
通信协议三要素
- 语法:即如何通信,包括数据的格式、编码和信号等级(电平的高低)等。
- 语义:即通信内容,包括数据内容、含义以及控制信息等。
- 定时规则(时序):即何时通信,明确通信的顺序、速率匹配和排序
TCP/UDP协议
TCP:传输控制协议
UDP协议:用户数据包协议
TCP和UDP对比
TCP:像打电话
- 需要连接,稳定,可靠
- 三次握手,四次挥手
- 三次握手大致流程:
- 客户端向服务器发送一个SYN J
- 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
- 客户端再想服务器发一个确认ACK K+1
- 四次挥手大致流程:
- 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
- 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
- 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
- 接收到这个FIN的源发送端TCP对它进行确认。
- 三次握手大致流程:
- 客户端和服务端界限明确
- 传输完成后就释放连接,效率低
UDP:像发短信
- 不用连接,不稳定,不可靠
- 客户端服务端没有明确的界限
Socket(套接字)
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点。
Socket套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。
TCP编程
实现发送消息
:需要建立连接,并且要知道地址
服务端
package com.cheng.lesson02;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//服务端
public class TCPServerDemon01 {
public static void main(String[] args) {
//提升作用域
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
//1.服务端的地址 localhost:7777
serverSocket = new ServerSocket(7777);
//2.等待来自客户端的连接
socket = serverSocket.accept();
//3.读取客户端的消息
inputStream = socket.getInputStream();
//4.管道流 ByteArrayOutputStream: 字节数组输出流,可以捕获内存缓冲区的数据,转换成字节数组。
byteArrayOutputStream = new ByteArrayOutputStream();//创建字节数组输出流对象)
byte[] buffer = new byte[1024];//定义字节数组缓冲区接收消息
int len;
while ((len = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, len);//从指定数组的下标off开始写入len个字节到该输出流中
}
System.out.println(byteArrayOutputStream.toString());//避免乱码
} catch (IOException e) {
e.printStackTrace();
} finally {
//5.关闭资源,先开后关
if (byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端
package com.cheng.lesson02;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
//客户端
public class TCPClientDemon01 {
public static void main(String[] args) {
Socket socket =null;
OutputStream os =null;
try {
//1.获取服务端的地址和端口号
InetAddress serverIp = InetAddress.getByName("127.0.0.1");
int port=7777;
//2.创建一个socket连接
socket = new Socket(serverIp,7777);
//3.用IO流发送消息
os = socket.getOutputStream();
//4.编写发送的内容
os.write("万里顾一程,学习".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
if (os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行(先启动服务端,在启动客户端)
TCP编程实现文件上传
客户端
package com.cheng.lesson02;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
//客户端上传文件
public class TestClientDemon02 {
public static void main(String[] args) throws IOException {
//1.创建socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),8888);
//2.创建输出流
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream fis = new FileInputStream(new File("1.png"));
//4.写出文件
byte[] buffer1 = new byte[1024];
int len;
while((len=fis.read(buffer1))!=-1){
os.write(buffer1,0,len);
}
//告知服务器,客户端已经上传文件完毕
socket.shutdownOutput();//单向关闭输出流,但socket仍然是连接状态,连接并未关闭
//确认服务端成功接收文件,再断开和服务器的连接
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len1;
while((len1=inputStream.read(buffer2))!=-1){
baos.write(buffer2,0,len1);
}
System.out.println(baos.toString());//转换成string类型
fis.close();
os.close();
socket.close();
}
}
服务端
package com.cheng.lesson02;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
//服务端
public class TestServerDemon02 {
public static void main(String[] args) throws IOException {
//1.创建服务
ServerSocket serverSocket = new ServerSocket(8888);
//2.监听客户端的连接
Socket socket = serverSocket.accept();//阻塞式监听,会一直等待客户端连接
//3.创建输入流,获取客户端的输入流
InputStream is = socket.getInputStream();
//4.输出文件
FileOutputStream fos = new FileOutputStream(new File("receive1.png"));
byte[] buffer = new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
//成功接收完文件后通知客户端
OutputStream os = socket.getOutputStream();
//服务端这边输出的是一个byte数组类型,所以客户端那边接收要用管道流
os.write("我已经成功接收文件,可以断开".getBytes());
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
运行
UDP编程
实现简单的消息发送:不用连接,但需要知道对方的地址
发送端
package com.cheng.lesson03;
import java.io.IOException;
import java.net.*;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
//发送端
public class UdpClientDemon01 {
public static void main(String[] args) throws IOException {
//1.建立socket连接
DatagramSocket socket = new DatagramSocket();
//2.建立数据包
//发送的地址
String msg = "你好,万里顾一程";
InetAddress localhost = InetAddress.getByName("localhost");
int port=8080;
//数据, 数据的长度 发送的地址
DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port);
//3.发送包
socket.send(packet);
//关闭资源
socket.close();
}
}
接收端
package com.cheng.lesson03;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
public class UdpServerDemon01 {
public static void main(String[] args) throws IOException {
//接收端端开放端口
DatagramSocket socket = new DatagramSocket(8080);
//接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(),0,packet.getLength()));
//关闭连接
socket.close();
}
}
循环发送/接收消息
package com.cheng.chat;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.*;
//循环发送
public class UDPSenderDemon01 {
public static void main(String[] args) throws Exception {
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 8888);
//建立Socket连接
DatagramSocket socket = new DatagramSocket(8888);
//准备要发送的数据
// 用System.in从控制台得到数据
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while(true){
//把从数据台得到的消息读取出来
String data = reader.readLine();
byte[] dataBytes = data.getBytes();
// 要发送的数据 数据的长度 接收数据的地址
DatagramPacket packet = new DatagramPacket(dataBytes,0,dataBytes.length,new InetSocketAddress("localhost", 6666));
//用DatagramSocket发送数据包
socket.send(packet);
byte[] data1 = packet.getData();
String sendData = new String(data1,0,data1.length);
if(dataBytes.equals("再见")){
break;
}
}
//关闭连接
socket.close();
}
}
package com.cheng.chat;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//循环接收
public class UDPReceiveDemon01 {
public static void main(String[] args) throws Exception {
//建立socket连接
DatagramSocket socket = new DatagramSocket(6666);
while(true){
//循环接收
byte[] bytes = new byte[1024];//缓冲区存放数据
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length);//把数据返回成数据包
socket.receive(packet);//socket阻塞式接收包裹
//断开连接请求
byte[] data = packet.getData();
String receivedate = new String(data,0,data.length);
System.out.println(receivedate);
if (receivedate.equals("再见")){
break;
}
}
socket.close();
}
}
实现双向聊天
两边都可以是发送方或者接收方
package com.cheng.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
//UDP多线程在线咨询
public class TalkSend implements Runnable{
DatagramSocket socket =null;
DatagramPacket packet = null;
BufferedReader reader = null;
//定义发送地址和接收地址
private int sendPort;
private int receivePort;
private String receiveIp;
String data = null;
//初始化参数放入构造器
public TalkSend(int sendPort, int receivePort, String receiveIp) {
this.sendPort = sendPort;
this.receivePort = receivePort;
this.receiveIp = receiveIp;
try {
socket = new DatagramSocket(sendPort);//建立Socket连接
reader = new BufferedReader(new InputStreamReader(System.in));// 用System.in从控制台得到数据
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
//把从数据台得到的消息读取出来
try {
data = reader.readLine();
byte[] dataBytes = data.getBytes();
// 要发送的数据 数据的长度 接收数据的地址
DatagramPacket packet = new DatagramPacket(dataBytes,0,dataBytes.length,new InetSocketAddress(this.receiveIp,this.receivePort ));
//用DatagramSocket发送数据包
socket.send(packet);
byte[] data1 = packet.getData();
String sendData = new String(data1,0,data1.length);
if(dataBytes.equals("再见")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接
socket.close();
}
}
package com.cheng.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class TalkReceive implements Runnable{
DatagramSocket socket =null;
private int port;
private String msgSend;
public TalkReceive(int port,String msgSend) {
this.port = port;
this.msgSend = msgSend;
try {
socket = new DatagramSocket(port);//建立socket连接
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
//循环接收
try {
byte[] bytes = new byte[1024];//缓冲区存放数据
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);//把数据返回成数据包
socket.receive(packet);//socket阻塞式接收包裹
//断开连接请求
byte[] data = packet.getData();
String receivedate = new String(data, 0, data.length);
System.out.println(msgSend + ": " + receivedate);
if (receivedate.equals("再见")) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
开始测试
package com.cheng.chat;
public class TalkStudent {
public static void main(String[] args) {
new Thread(new TalkSend(7777,9999,"localhost")).start();//学生的消息从7777端口发出去,发送到9999
new Thread(new TalkReceive(8888,"老师")).start();//学生通过8888端口接收老师发送过来的消息
}
}
package com.cheng.chat;
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(5555,8888,"localhost")).start();//老师的消息从5555端口发出去,发送到8888端口
new Thread(new TalkReceive(9999,"学生")).start();//老师通过8888端口接收学生发送过来的消息
}
}
效果
URL
统一资源定位器(uniform resource locator;URL):定位互联网上的某一个资源,URL 是一个网页地址
一个网页地址实例:https://blog.csdn.net/wpc2018/article/details/112725366语法规则:
scheme://host.domain:port/path/filename
说明:
- scheme - 定义因特网服务的类型。最常见的类型是 http
- host - 定义域主机(http 的默认主机是 www)
- domain - 定义因特网域名,比如 runoob.com
- :port - 定义主机上的端口号(http 的默认端口号是 80)
- path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。
- filename - 定义文档/资源的名称
package com.cheng.lesson04;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemon01 {
public static void main(String[] args) throws IOException {
URL url = new URL("https://www.chinanews.com/sh/2021/01-25/9395546.shtml");
System.out.println(url.getAuthority());//作者名
System.out.println(url.getProtocol());//协议名
System.out.println(url.getPort());//端口
System.out.println(url.getFile());//文件地址
System.out.println(url.getHost());//主机ip
System.out.println(url.getPath());//文件全路径
}
}
URL下载网络资源
package com.cheng.lesson04;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
public class URLDemon02 {
public static void main(String[] args) throws IOException {
//下载地址
URL url = new URL("http://localhost:9090/pengcheng/ConfidentialFile.txt");
//通过url打开连接,连接资源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream("ConfidentialFile.txt");
byte[] buffer = new byte[1024];
int len;
while((len=inputStream.read(buffer))!=-1){
fileOutputStream.write(buffer,0,len);
}
//断开连接
fileOutputStream.close();
inputStream.close();
urlConnection.disconnect();
}
}