菜鸟之路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);//获取对应的ipString 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();//获取发送方的ipSystem.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 {//创建SocketSocket 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;}@Overridepublic 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,//核心线程数量,不能小于016,//最大线程数,不能小于0,最大数量 >= 核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue<>(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略);while (true) {//监听客户端Socket socket = ss.accept();//开启一条线程,一个用户对应服务端一条线程poolExecutor.submit(new MyRunnable(socket));}}
}