菜鸟之路Day21一一网络编程
菜鸟之路Day21一一网络编程(一)
作者:blue
时间:2025.3.2
文章目录
- 菜鸟之路Day21一一网络编程(一)
- 0.概述
- 1.初识网络编程
- 2.网络编程三要素
- 3.InetAddress类的使用
- 4.UDP通信程序
- 4.1发送数据
- 4.2接受数据
- 4.3UDP通信程序练习(聊天室)
- 4.4单播,组播,广播
- 5.TCP通信程序
- 6.综合练习
- 6.1综合练习1(多发多收)
- 6.2综合练习2(接收并反馈)
- 6.3综合练习3(上传文件)
- 6.4综合练习4(解决上一题文件名重复的问题)
- 6.5综合练习5(上传文件一一多线程版)
- 6.6综合练习6(线程池优化)
0.概述
内容学习自黑马程序员BV1yW4y1Y7Ms
1.初识网络编程
①什么是网络编程:计算机与计算机之间通过网络进行数据传输
②常见软件架构有哪些:CS/BS
③通信的软件架构CS/BS各有什么区别和优缺点
CS:客户端服务端模式需要开发客户端
BS:浏览器服务端模式不需要开发客户端
CS:适合定制专业化的办公软件如:IDEA,网游
BS:适合移动互联网应用,可以在任何地方随时访问的系统
2.网络编程三要素
IP:设备在网络中的地址,是唯一的标识
端口号:应用程序在设备中唯一的标识
协议:数据在网络中传输的规则,常见的协议有UDP,TCP,http,https,ftp
3.InetAddress类的使用
InetAddress类表示互联网协议(IP)地址
public class myInetAddressDemo1 {
public static void main(String[] args) throws UnknownHostException {
/*
* static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
* String getHostName() 获取此IP地址的主机名
* String getHostAddress() 返回文本显示中IP地址字符串
* */
//获取InetAddress对象
//IP的对象或者说是主机的对象
InetAddress address = InetAddress.getByName("LAPTOP-CFHO6DM4");
//InetAddress address = InetAddress.getByName("192.168.213.203");
System.out.println(address);
//获取主机名
String hostName = address.getHostName();
System.out.println(hostName);
//获取对应的ip
String ip = address.getHostAddress();
System.out.println(ip);
}
}
4.UDP通信程序
4.1发送数据
①创建发送端的DatagramSocket对象;②数据打包(DatagramPacket)
③发送数据;④释放资源
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 ip = InetAddress.getByName("127.0.0.1");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);
//3.发送数据
ds.send(dp);
//4.释放资源
ds.close();
}
}
4.2接受数据
①创建接收端的DatagramSocket对象;②接收打包好的数据
③解析数据包;④释放资源
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);
ds.receive(dp);//接收数据,注意,这个方法是阻塞的,收不到数据程序就会停在这里
//3.解析数据包
byte[] data = dp.getData(); //获取接收到的字节数组
int length = dp.getLength();//获取数据报文的长度
int port = dp.getPort(); //获取发送方的端口号
InetAddress ip = dp.getAddress();//获取发送方的ip
System.out.println("收到数据:"+new String(data,0,length));
System.out.println("数据长度:"+length);
System.out.println("数据从发送方的"+port+"端口发出");
System.out.println("发送方的IP地址为:"+ip);
//4.释放资源
ds.close();
}
}
4.3UDP通信程序练习(聊天室)
按照下面的要求实现程序
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
发送端:
public class SendMessage {
public static void main(String[] args) throws IOException {
//发送端
DatagramSocket ds = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入你想说的话");
String str = sc.nextLine();
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 10086;//目的端口和目的地址
DatagramPacket dp = new DatagramPacket(str.getBytes(),str.getBytes().length,ip,port);
ds.send(dp);//发送数据
if("886".equals(str)) break;
}
//释放资源
ds.close();
}
}
接收端:
public class ReceiveMessage {
public static void main(String[] args) throws IOException {
//接收端
DatagramSocket ds = new DatagramSocket(10086);
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while (true) {
ds.receive(dp);
byte[] data = dp.getData();
int length = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
System.out.println("收到来自ip为:"+ip+"主机名为:"+name+"所发送的信息:"+new String(data,0,length));
}
}
}
4.4单播,组播,广播
单播:上述代码都是单播
组播:组播地址:224.0.0.0 - 239.255.255.255,其中224.0.0.0 - 224.0.0.255为预留的组播地址
同一分组的ip都可以接收到信息
组播发送端
public class SendMessage {
public static void main(String[] args) throws IOException {
//组播发送端
//创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket();
//创建DatagramPacket对象
String str = "你好,你好";
byte[] bytes = str.getBytes();
InetAddress ip = InetAddress.getByName("224.0.0.1");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);
//发送数据
ms.send(dp);
//释放资源
ms.close();
}
}
组播接收端
有多个组播接收端,只要接收端的ip为发送端目的的组播ip,就都能收到信息
public class ReceiveMessageOne {
public static void main(String[] args) throws IOException {
//组播接收端
//创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket(10086);
//将当前本机,添加到224.0.0.1这一组中
InetAddress ip = InetAddress.getByName("224.0.0.1");
ms.joinGroup(ip);
//创建DatagramPacket对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//接收数据
ms.receive(dp);
//解析数据
byte[] data = dp.getData();
int length = dp.getLength();
String HostAddress = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
System.out.println("收到来自ip为:"+HostAddress+"主机名为:"+name+"所发送的信息:"+new String(data,0,length));
//释放资源
ms.close();
}
}
广播:广播地址:255.255.255.255
局域网内任意ip都可以接收到信息
只需要将目的ip改为:255.255.255.255
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 ip = InetAddress.getByName("255.255.255.255");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);
//3.发送数据
ds.send(dp);
//4.释放资源
ds.close();
}
}
5.TCP通信程序
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象
通信之前要保证连接已经建立
通过Socket产生IO流来进行网络通信
客户端流程:
①创建客户端Socket对象(Socket)与指定服务端连接
Socket(String host,int port)
②获取输出流,写数据
OutputStream getOutputStream()
③释放资源
void close()
public class Client {
public static void main(String[] args) throws IOException {
//创建客户端Socket对象(Socket)与指定服务端连接
Socket socket = new Socket("127.0.0.1",10000);
//获取输出流,写数据
OutputStream os = socket.getOutputStream();
os.write("aaaa".getBytes());
//释放资源
os.close();
socket.close();
}
}
服务端流程:
①创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
②监听客户端连接,返回一个Socket对象
Socket accpet()
③获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
④释放资源
void close()
public class Server {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象(ServerSocket)
ServerSocket ss = new ServerSocket(10000);
//监听客户端连接,返回一个Socket对象
Socket accept = ss.accept();
//获取输入流,读数据,并把数据显示在控制台
InputStream is = accept.getInputStream();
int b;
while((b=is.read())!=-1){
System.out.println((char)b);
}
//释放资源
is.close();
ss.close();
}
}
6.综合练习
6.1综合练习1(多发多收)
客户端:多次发送数据
服务器:接收多次接收数据,并打印
客户端
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",10000);
Scanner sc = new Scanner(System.in);
OutputStream os = socket.getOutputStream();
while (true) {
System.out.println("请输入你要发送的信息");
String str = sc.nextLine();
os.write(str.getBytes());
if("886".equals(str)) break;
}
socket.close();
}
}
服务端
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10000);
//监听
Socket accept = ss.accept();
InputStreamReader isr = new InputStreamReader(accept.getInputStream());
int b;
while((b= isr.read())!=-1){
System.out.print((char)b);
}
//释放资源
accept.close();
ss.close();
}
}
6.2综合练习2(接收并反馈)
客户端:发送一条数据,接收服务端反馈的消息并打印
服务器:接收数据并打印,再给客户端反馈消息
客户端
public class Client {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket socket = new Socket("127.0.0.1",10000);
//发送数据
OutputStream os = socket.getOutputStream();
String str="见到你很高兴!";
os.write(str.getBytes());
//写一个结束标志
socket.shutdownOutput();
//接受服务器端返回数据
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int b;
while((b= isr.read())!=-1){
System.out.println((char)b);
}
//释放资源
socket.close();
}
}
服务端
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10000);
//监听客户端
Socket socket = ss.accept();
//接收来自客户端的数据
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int b;
while((b= isr.read())!=-1){
System.out.println((char)b);
}
//对客户端进行反馈
OutputStream os = socket.getOutputStream();
String str="有多开心呢?";
os.write(str.getBytes());
socket.close();
ss.close();
}
}
6.3综合练习3(上传文件)
客户端:将本地文件上传到服务器。接收服务器的反馈。
服务器:接收客户端上传的文件,上传完毕之后给出反馈。
客户端
public class Client {
public static void main(String[] args) throws IOException {
//创建Socket
Socket socket = new Socket("127.0.0.1",10000);
//读本地文件的数据,然后发出去
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\ClientDir\\girl1.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int len;
byte[] bytes = new byte[1024*5];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bis.close();
//结束标志,很关键
socket.shutdownOutput();
//接收服务端反馈
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int B;
while((B=isr.read())!=-1){
System.out.println((char)B);
}
//释放资源
socket.close();
}
}
服务器
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10000);
//监听客户端
Socket socket = ss.accept();
//接收客户端发来的数据,写到本地
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\girl1.jpg"));
int len;
byte[] bytes = new byte[1024*5];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
//向客户端反馈
BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
Bos.write("上传成功".getBytes());
Bos.flush(); // 刷新输出流,确保数据发送到客户端
//释放资源
socket.close();
ss.close();
}
}
6.4综合练习4(解决上一题文件名重复的问题)
我们可以用UUID这个类,来形成随机的文件名称
public class UUIDdemo {
public static void main(String[] args) {
String str = UUID.randomUUID().toString().replace("-","");
System.out.println(str);
}
}
修改上题的服务端代码,将文件名替换为随机的UUID
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10000);
//监听客户端
Socket socket = ss.accept();
//接收客户端发来的数据,写到本地
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-","");//随机的文件名
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\"+name+".jpg"));
int len;
byte[] bytes = new byte[1024*5];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
//向客户端反馈
BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
Bos.write("上传成功".getBytes());
Bos.flush(); // 刷新输出流,确保数据发送到客户端
//释放资源
socket.close();
ss.close();
}
}
6.5综合练习5(上传文件一一多线程版)
Runnable类
public class MyRunnable implements Runnable{
Socket socket;
public MyRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//接收客户端发来的数据,写到本地
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-","");//随机的文件名
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\ServerDIr\\"+name+".jpg"));
int len;
byte[] bytes = new byte[1024*5];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
//向客户端反馈
BufferedOutputStream Bos = new BufferedOutputStream(socket.getOutputStream());
Bos.write("上传成功".getBytes());
Bos.flush(); // 刷新输出流,确保数据发送到客户端
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (socket!=null) { //做一个非空判断
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
服务端
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10000);
while (true) {
//监听客户端
Socket socket = ss.accept();
//开启一条线程,一个用户对应服务端一条线程
new Thread(new MyRunnable(socket)).start();
}
}
}
6.6综合练习6(线程池优化)
频繁创建线程并销毁非常浪费系统资源,所以要用线程池优化,需要更改的只有服务端的代码
服务端
public class Server {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10000);
//线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
3,//核心线程数量,不能小于0
16,//最大线程数,不能小于0,最大数量 >= 核心线程数量
60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
);
while (true) {
//监听客户端
Socket socket = ss.accept();
//开启一条线程,一个用户对应服务端一条线程
poolExecutor.submit(new MyRunnable(socket));
}
}
}