1.什么是网络编程?
在网络通信下,不同的计算机上运行的程序,进行的数据传输
- 应用场景,网络通讯,网游对战,金融证券,国际贸易,邮件,等等
不管是什么场景下,都是计算机跟计算机之间通过网络进行数据传输。
- java中可以使用java.net包下的技术轻松开发出常见的网络应用程序
2.常见的软件架构 C/S:Client/Server 客户端/服务器, B/S:Browser/Server 浏览器/服务器
1.C/S:Client/Server
在用户本地需要安装下载并安装客户端程序,在远程有一个服务器端程序。
2.B/S:Browser/Server
只需要一个浏览器,用户通过不同的网址。客户只需要访问不同的服务器
3.B/S:Browser/Server 和 C/S:Client/Server 优缺点
B/S架构的优缺点:
不需要开发客户端,只需要界面+服务端
用户不需要下载,打开浏览器就能使用
B/S缺点:
如果应用过大,用户体验感收到影响
C/S架构的优缺点:
画面可以做的非常精美,用户体验感好
C/S缺点:
需要开发客户端,也需要开发服务端
用户需要下载和跟新的时候太麻烦
3.网络编程的三要素是什么?
- IP、端口号、协议
4.网络编程的三要素分别表示什么
- IP:设备在网络中地址,是唯一标识
- 端口号:应用程序在设备中的唯一标识
- 协议:数据在网络中传输的规则
常见的协议有UDP、TCP、HTTP、HTTPS、FTP
- IP:的作用
设备在网络中的地址,是唯一标识
2.IPv4有什么特点
目前的主流方案
最多只有2^32次方个ip,目前已经用完了
3.IPv6有什么特点
为了解决IPv4不够用的情况下出现的
最多有2^128次方ip
可以为地球上的每一粒沙子都设ip
5.IPv4的一些小细节
1.现在如何解决IPv4不够的问题
利用局域网IP解决IP不够的问题
2.特殊的IP是什么?
127.0.0.1(永远标识本机)localhost也就是本机IP
3.常见的两个CMD命令
ipconfig:查看本机IP地址
ping:检查网络是否连通
6.InetAddress类的使用
import java.net.InetAddress;
import java.net.UnknownHostException;
public class SocketnetTest {
public static void main(String[] args) throws UnknownHostException {
/**
* static InetAdders getByName(String host) 确定主机名称的IP地址,主机名称可以是机器名称,也可以是IP地址
* String getHostName() 获取此IP地址的主机名
* String getHostAddress() 返回文本显示中的IP地址字符串
*/
//1.获取InetAdders的对象172.20.xx.xx
InetAddress allByName = InetAddress.getByName("电脑名称不建议使用中文");//主机名
System.out.println(allByName);
String hostAddress = allByName.getHostAddress();//ip
System.out.println(hostAddress);
}
}
7.端口号
应用程序在设备中的唯一标识
端口号:由两个字节表示整数,取值范围:0~65535
其中0~1023之间的端口号用于一些知名的网络服务或者应用。
我们自己使用1024以上的端口就可以了。
注意:一个端口只能被一个应用程序使用。
8.协议
计算机网络中,连接和通信的规则被称为网络通讯协议
- OSI参考模型:世界互联协议标准,全球通讯规范,单模型过于理想化,未能在因特网上广泛推广
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
OSI参考模型 TCP/IP参考模型 TCP/IP参考模型各层对应协议 面向那些 应用层
表示层
会话层
应用层 HTTP、FTP、Telnet、DNS... 一把是应用程序需要关注的。
如浏览器,邮箱,程序员一般在这一层开发
传输层
传输层 TCP、UDP、... 选择传输使用的TCP,UDP协议 网络层 网络层 IP、ICMP、ARP... 封装自己的Ip,对方的Ip信息 数据链路层
物理层
物理+数据链路层 硬件设备。
0101001010101100101010...
转化为二进制利用物理设备传输 UDP协议
-
用户数据协议(User Datagram Protocol)
-
UPD是面向无连接通讯协议。
速度快,有大小限制一次最多发送64k,数据不安全,易丢失数据
TCP协议
- 传输控制协议TCP(Transmission Control Protcocol)
- TCP协议是面向连接的通讯协议。
速度慢,没有大小限制,数据安全。
UPD的应用场景:
网络会议、语音通话、在线视频、
TCP的应用场景:
下载软件、文字聊天、发送邮件
UPD通讯程序(发送数据)
- 创建发送端的DatagramSocket对象
- 数据打包(DatagramPacket)
- 发送数据
- 释放资源
import java.io.IOException;
import java.net.*;
public class SendMessageDemo {
public static void main(String[] args) throws IOException {
//发送数据
//1.创建DatagramSocket对象(快递公司)
//细节:
//绑定端口以后我们就是通过这个端口往外发送
//空参:所有可用的端口钟随机一个一个进行使用
//有参:指定端口进行绑定
DatagramSocket ds = new DatagramSocket();
//2.打包数据
String str = "你好闲~~";
byte[] bytes = str.getBytes();
InetAddress address = InetAddress.getByName("172.20.10.9");
int prot = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,prot);
//3,发送数据
ds.send(dp);
//4.释放资源
ds.close();
}
}
UPD通讯程序(接收数据)
- 创建接收端的DatageramSocket对象
- 接收打包好的数据
- 解析数据包
释放资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ReceiveMessageDemo {
public static void main(String[] args) throws IOException {
//接收数据
//1.创建DatagramSocket对象(快递公司)
//细节:
//在接收的时候,一定要绑定端口
//而且绑定的端口要跟发送的端口保持一致!!
DatagramSocket ds = new DatagramSocket(10086);
//2.接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//该方法是堵塞的
//程序执行到这一步时候会在这里死等
//等发送端发送
System.out.println("等待数据");
ds.receive(dp);
//3,解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
InetAddress address = dp.getAddress();
int port = dp.getPort();
System.out.println("接收到数据" + new String(data, 0, len));
System.out.println("该数据是从" + address + "这台电脑中的" + port + "这个端口中发出来的");
//4.释放资源
ds.close();
}
}
注意:先执行等待端,在执行发送端
9.练习
聊天室
按照下面的要求实现应用程序
UDP发送数据:数据来源键盘输入,直到输入的数据886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送数据,故采用死循环接收
发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class SendMessageDemo {
public static void main(String[] args) throws IOException {
/*
按照下面的要求实现应用程序
UDP发送数据:数据来源键盘输入,直到输入的数据886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送数据,故采用死循环接收
*/
//1.创建对象
DatagramSocket ds = new DatagramSocket();
Scanner sc = new Scanner(System.in);
//2,打包数据
while (true) {
System.out.println("请输入你要说的话");
String str = sc.next();
if (str.equals("886")) {
break;
}
byte[] bytes = str.getBytes();
InetAddress address = InetAddress.getByName("172.20.10.9");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
//3.发送数据
ds.send(dp);
}
//4.释放资源
ds.close();
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveMessageDemo {
public static void main(String[] args) throws IOException {
/*
按照下面的要求实现应用程序
UDP发送数据:数据来源键盘输入,直到输入的数据886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送数据,故采用死循环接收
*/
DatagramSocket ds = new DatagramSocket(10086);
//2,接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while (true) {
//等待数据
ds.receive(dp);
//3.解析包
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String hostName = dp.getAddress().getHostName();
//打印数据
System.out.println("ip为" + ip + "主机名" + hostName + "的人,发送了数据:" + new String(data, 0, len));
}
}
}
开启重复运行变成聊天室一个发送端相当于一个人
10.UPD的三种通讯方式
- 单播:发送端只给一台电脑发送数据
- 组播:发送端给一组电脑发送数据
- 广播:发送端可以给局域网中所有的电脑发送数据
UPD的三种通讯方法(代码实现)
- 单播
UPD的方式就是单播
2.组播
组播地址:224.0.0.0~239.255.255.255
其中224.0.0.0~224.0.0.255为预留的组播地址
3.广播
广播地址:255.255.255.255
发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class SendMessageDemo {
public static void main(String[] args) throws IOException {
/**
* 组播发送端代码
*/
//创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket();
//创建DatagramPacket对象
String s = "你好你好";
byte[] bytes = s.getBytes();
//组播地址
InetAddress inetAddress = InetAddress.getByName("224.0.0.1");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, inetAddress, port);
//调用MulticastSocket发送数据方法发送数据
ms.send(dp);
//释放资源
ms.close();
}
}
接收端*3:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class ReceiveMessageDemo {
public static void main(String[] args) throws IOException {
/*
组播接收端编码
*/
//1,创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket(10000);
//2,将当前本机,添加到224.0.0.1的这一组中
InetAddress address = InetAddress.getByName("224.0.0.1");
//把当前的本机添加到224.0.0.1中
ms.joinGroup(address);
//3.创建DatagramPacket对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//4.接收数据
ms.receive(dp);
//5.解析数据
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String hostName = dp.getAddress().getHostName();
System.out.println("ip为" + ip + "主机名" + hostName + "的人,发送了数据:" + new String(data, 0, len));
//6.释放资源
ms.close();
}
}
11.TCP通讯程序
- TCP通讯协议是一种可靠的网络协议,它在通讯的两端各建立一个Socket对象
- 通讯之前要保证连接已经建立
- 通过Socket产生的IO流来进行网络通讯
TCP的代码实现
发送端
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//TCP协议,发送数据
//1.创建Socket对象
//细节:在创建对象的同时会连接服务端
// 如果连接不上,代码会报错
Socket socket = new Socket("172.20.10.9",10086);
//2.可以从连接通道中获取输出流
OutputStream os = socket.getOutputStream();
//写出数据
os.write("你好你好".getBytes());
//3.释放资源
os.close();
socket.close();
}
}
接收端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//TCP协议,接收数据
//1.创建对象ServerSocket
ServerSocket serverSocket = new ServerSocket(10086);
//2.监听客户端的连接
Socket socket = serverSocket.accept();
//3.从连接通道中获取输入流读取数据
/*
InputStream is = socket.getInputStream();
//转化流
InputStreamReader isr = new InputStreamReader(is);
//缓冲流
BufferedReader br = new BufferedReader(isr);
*/
//1.一行代码搞定
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
int b;
while ((b = br.read()) != -1) {
System.out.print((char) b);
}
//释放资源
socket.close();
serverSocket.close();
}
}
12.TCP通讯程序(三次握手和三次挥手)
1.TCP通讯程序(三次握手):确保连接建立
- 客户端向服务器发出连接请求,等待服务器确认
- 服务器向客户返回一个响应,告诉客户端收到了请求
- 客服端向服务器再次发出确认信息,建立连接
2.TCP通讯程序(四次挥手):确保连接断开,且数据一定要处理完毕
- 客户端向服务器发出取消连接的请求
- 服务器向客户端返回了一个响应,表示收到了客户端的取消请求(服务器将最后的数据处理完毕)
- 服务器向客户端发送确定取消的信息
- 客户端再次发送确认消息,连接取消
文件上传的案例
发送端
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//客户端:将本地文件上传到服务器,接收服务器的反馈.
//服务端:接收客户端上传的文件,上传完毕之后给出反馈
//1.创建Socket对象,并连接服务器
Socket socket = new Socket("172.20.10.9", 10086);
//2.创建本地文件中的数据,并写入服务器中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\教务系统\\mysocketnet\\clientdir\\QQ图片20240327105514.png"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
//往服务器写出结束标记
socket.shutdownOutput();
//3.接收服务器的回写的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = br.readLine();
System.out.println("回写的数据"+s);
//4.释放数据
socket.close();
}
}
接收端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) throws IOException {
//客户端:将本地文件上传到服务器,接收服务器的反馈.
//服务端:接收客户端上传的文件,上传完毕之后给出反馈
//1,创建对象并绑定端口
ServerSocket ss = new ServerSocket(10086);
//2,等待客户端来连接
Socket socket = ss.accept();
//3.读取数据并保存到本地文件中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\教务系统\\mysocketnet\\serverdir\\" + name + ".png"));
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
//4.回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
//5.释放资源
socket.close();
ss.close();
}
}
多线程版的服务端和线程池的服务端
发送端
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//客户端:将本地文件上传到服务器,接收服务器的反馈.
//服务端:接收客户端上传的文件,上传完毕之后给出反馈
//1.创建Socket对象,并连接服务器
Socket socket = new Socket("172.20.10.9", 10086);
//2.创建本地文件中的数据,并写入服务器中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\教务系统\\mysocketnet\\clientdir\\QQ图片20240327105514.png"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
//往服务器写出结束标记
socket.shutdownOutput();
//3.接收服务器的回写的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = br.readLine();
System.out.println("回写的数据"+s);
//4.释放数据
socket.close();
}
}
多线程的实现
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class MyRunnable implements Runnable {
Socket socket;
/**
* 表示你在创建对象的时候需要创建一个Socket过来
*
* @param socket
*/
public MyRunnable(Socket socket) {
this.socket = socket;
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
try {
//3.读取数据并保存到本地文件中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\教务系统\\mysocketnet\\serverdir\\" + name + ".png"));
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
//4.回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//5.释放资源
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
接收端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws IOException {
//客户端:将本地文件上传到服务器,接收服务器的反馈.
//服务端:接收客户端上传的文件,上传完毕之后给出反馈
//创建线程池对象
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数量
16,//线程池总大小
60,//空闲时间
TimeUnit.SECONDS,//空闲时间(单位)
new ArrayBlockingQueue<>(2),//队列
Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象
new ThreadPoolExecutor.AbortPolicy()//堵塞队列
);
//1,创建对象并绑定端口
ServerSocket ss = new ServerSocket(10086);
while (true) {
//2,等待客户端来连接
Socket socket = ss.accept();
//开启一条线程线程
//一个用户就对应服务器的一条线程
//new Thread(new MyRunnable(socket)).start();
pool.submit(new MyRunnable(socket));
}
}
}
在B/S架构中客户端就是浏览器而我们服务端就是接收客户端传输过来的数据,当我们要回写的时候就是把数据回写给浏览器