【Java】网络编程(二)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 网络编程

服务器向客户端回写数据


服务端实现:

publicclassServerTCP {
publicstaticvoidmain(String[] args) throwsIOException {
System.out.println("服务端启动 , 等待连接 .... ");
// 1.创建 ServerSocket对象,绑定端口,开始等待连接ServerSocketss=newServerSocket(6666);
// 2.接收连接 accept 方法, 返回 socket 对象.Socketserver=ss.accept();
// 3.通过socket 获取输入流InputStreamis=server.getInputStream();
// 4.一次性读取数据// 4.1 创建字节数组byte[] b=newbyte[1024];
// 4.2 据读取到字节数组中.intlen=is.read(b)// 4.3 解析数组,打印字符串信息Stringmsg=newString(b, 0, len);
System.out.println(msg);
// =================回写数据=======================// 5. 通过 socket 获取输出流OutputStreamout=server.getOutputStream();
// 6. 回写数据out.write("我很好,谢谢你".getBytes());
// 7.关闭资源.out.close();
is.close();
server.close();
    }
}

客户端实现:

publicclassClientTCP {
publicstaticvoidmain(String[] args) throwsException {
System.out.println("客户端 发送数据");
// 1.创建 Socket ( ip , port ) , 确定连接到哪里.Socketclient=newSocket("localhost", 6666);
// 2.通过Scoket,获取输出流对象 OutputStreamos=client.getOutputStream();
// 3.写出数据.os.write("你好么? tcp ,我来了".getBytes());
// ==============解析回写=========================// 4. 通过Scoket,获取 输入流对象InputStreamin=client.getInputStream();
// 5. 读取数据数据byte[] b=newbyte[100];
intlen=in.read(b);
System.out.println(newString(b, 0, len));
// 6. 关闭资源 .in.close();
os.close();
client.close();
    }
}

第三章 综合案例


3.1 文件上传案例


文件上传分析图解


  1. 【客户端】输入流,从硬盘读取文件数据到程序中。
  2. 【客户端】输出流,写出文件数据到服务端。
  3. 【服务端】输入流,读取文件数据到服务端程序。
  4. 【服务端】输出流,写出文件数据到服务器硬盘中。

image.png

基本实现

服务端实现

publicclassFileUpload_Server {
publicstaticvoidmain(String[] args) throwsIOException {
System.out.println("服务器 启动.....  ");
// 1. 创建服务端ServerSocketServerSocketserverSocket=newServerSocket(6666);
// 2. 建立连接 Socketaccept=serverSocket.accept();
// 3. 创建流对象// 3.1 获取输入流,读取文件数据BufferedInputStreambis=newBufferedInputStream(accept.getInputStream());
// 3.2 创建输出流,保存到本地 .BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("copy.jpg"));
// 4. 读写数据byte[] b=newbyte[1024*8];
intlen;
while ((len=bis.read(b)) !=-1) {
bos.write(b, 0, len);
        }
//5. 关闭 资源bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
    }
}

客户端实现:

publicclassFileUPload_Client {
publicstaticvoidmain(String[] args) throwsIOException {
// 1.创建流对象// 1.1 创建输入流,读取本地文件  BufferedInputStreambis=newBufferedInputStream(newFileInputStream("test.jpg"));
// 1.2 创建输出流,写到服务端 Socketsocket=newSocket("localhost", 6666);
BufferedOutputStreambos=newBufferedOutputStream(socket.getOutputStream());
//2.写出数据. byte[] b=newbyte[1024*8 ];
intlen ; 
while (( len=bis.read(b))!=-1) {
bos.write(b, 0, len);
bos.flush();
        }
System.out.println("文件发送完毕");
// 3.释放资源bos.close(); 
socket.close();
bis.close(); 
System.out.println("文件上传完毕 ");
    }
}

文件上传优化分析


  1. 文件名称写死的问题
    服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下:

FileOutputStream fis = new FileOutputStream(System.currentTimeMillis()+".jpg") // 文件名称

BufferedOutputStream bos = new BufferedOutputStream(fis);

循环接收的问题

服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下:

// 每次接收新的连接,创建一个Socketwhile(true){
Socketaccept=serverSocket.accept();
    ......
}
  1. 效率问题
    服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下:
while(true){
Socketaccept=serverSocket.accept();
// accept 交给子线程处理.newThread(() -> {
        ......
InputStreambis=accept.getInputStream();
        ......
    }).start();
}
优化实现publicclassFileUpload_Server {
publicstaticvoidmain(String[] args) throwsIOException {
System.out.println("服务器 启动.....  ");
// 1. 创建服务端ServerSocketServerSocketserverSocket=newServerSocket(6666);
// 2. 循环接收,建立连接while (true) {
Socketaccept=serverSocket.accept();
/* 3. socket对象交给子线程处理,进行读写操作Runnable接口中,只有一个run方法,使用lambda表达式简化格式*/newThread(() -> {
try (
//3.1 获取输入流对象BufferedInputStreambis=newBufferedInputStream(accept.getInputStream());
//3.2 创建输出流对象, 保存到本地 .FileOutputStreamfis=newFileOutputStream(System.currentTimeMillis() +".jpg");
BufferedOutputStreambos=newBufferedOutputStream(fis);) {
// 3.3 读写数据byte[] b=newbyte[1024*8];
intlen;
while ((len=bis.read(b)) !=-1) {
bos.write(b, 0, len);
                    }
//4. 关闭 资源bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
                } catch (IOExceptione) {
e.printStackTrace();
                }
            }).start();
        }
    }
}

信息回写分析图解


前四步与基本文件上传一致.

  1. 【服务端】获取输出流,回写数据。
  2. 【客户端】获取输入流,解析回写数据。

image.png

回写实现publicclassFileUpload_Server {
publicstaticvoidmain(String[] args) throwsIOException {
System.out.println("服务器 启动.....  ");
// 1. 创建服务端ServerSocketServerSocketserverSocket=newServerSocket(6666);
// 2. 循环接收,建立连接while (true) {
Socketaccept=serverSocket.accept();
/*3. socket对象交给子线程处理,进行读写操作Runnable接口中,只有一个run方法,使用lambda表达式简化格式*/newThread(() -> {
try (
//3.1 获取输入流对象BufferedInputStreambis=newBufferedInputStream(accept.getInputStream());
//3.2 创建输出流对象, 保存到本地 .FileOutputStreamfis=newFileOutputStream(System.currentTimeMillis() +".jpg");
BufferedOutputStreambos=newBufferedOutputStream(fis);
                ) {
// 3.3 读写数据byte[] b=newbyte[1024*8];
intlen;
while ((len=bis.read(b)) !=-1) {
bos.write(b, 0, len);
                    }
// 4.=======信息回写===========================System.out.println("back ........");
OutputStreamout=accept.getOutputStream();
out.write("上传成功".getBytes());
out.close();
//================================//5. 关闭 资源bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
                } catch (IOExceptione) {
e.printStackTrace();
                }
            }).start();
        }
    }
}

客户端实现:

publicclassFileUpload_Client {
publicstaticvoidmain(String[] args) throwsIOException {
// 1.创建流对象// 1.1 创建输入流,读取本地文件BufferedInputStreambis=newBufferedInputStream(newFileInputStream("test.jpg"));
// 1.2 创建输出流,写到服务端Socketsocket=newSocket("localhost", 6666);
BufferedOutputStreambos=newBufferedOutputStream(socket.getOutputStream());
//2.写出数据.byte[] b=newbyte[1024*8 ];
intlen ;
while (( len=bis.read(b))!=-1) {
bos.write(b, 0, len);
        }
// 关闭输出流,通知服务端,写出数据完毕socket.shutdownOutput();
System.out.println("文件发送完毕");
// 3. =====解析回写============InputStreamin=socket.getInputStream();
byte[] back=newbyte[20];
in.read(back);
System.out.println(newString(back));
in.close();
// ============================// 4.释放资源socket.close();
bis.close();
    }
}

3.2 模拟B\S服务器(扩展知识点)


模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。

案例分析


  1. 准备页面数据,web文件夹。

image.png

我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问

ublicpublicstaticvoidmain(String[] args) throwsIOException {
ServerSocketserver=newServerSocket(8000);
Socketsocket=server.accept();
InputStreamin=socket.getInputStream();
byte[] bytes=newbyte[1024];
intlen=in.read(bytes);
System.out.println(newString(bytes,0,len));
socket.close();
server.close();
}

image.png

服务器程序中字节输入流可以读取到浏览器发来的请求信息

image.png

GET/web/index.html HTTP/1.1是浏览器的请求消息。/web/index.html为浏览器想要请求的服务器端的资源,使用字符串切割方式获取到请求的资源。

//转换流,读取浏览器请求第一行BufferedReaderreadWb=newBufferedReader(newInputStreamReader(socket.getInputStream()));
Stringrequst=readWb.readLine();
//取出请求资源的路径String[] strArr=requst.split(" ");
//去掉web前面的/Stringpath=strArr[1].substring(1);
System.out.println(path);

案例实现


服务端实现:

publicclassSerDemo {
publicstaticvoidmain(String[] args) throwsIOException {
System.out.println("服务端  启动 , 等待连接 .... ");
// 创建ServerSocket 对象ServerSocketserver=newServerSocket(8888);
Socketsocket=server.accept();
// 转换流读取浏览器的请求消息BufferedReaderreadWb=newBufferedReader(newInputStreamReader(socket.getInputStream()));
Stringrequst=readWb.readLine();
// 取出请求资源的路径String[] strArr=requst.split(" ");
// 去掉web前面的/Stringpath=strArr[1].substring(1);
// 读取客户端请求的资源文件FileInputStreamfis=newFileInputStream(path);
byte[] bytes=newbyte[1024];
intlen=0 ;
// 字节输出流,将文件写会客户端OutputStreamout=socket.getOutputStream();
// 写入HTTP协议响应头,固定写法out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析out.write("\r\n".getBytes());
while((len=fis.read(bytes))!=-1){
out.write(bytes,0,len);
        }
fis.close();
out.close();
readWb.close(); 
socket.close();
server.close();
    }
}

访问效果


  • 火狐

image.png

发现浏览器中出现很多的叉子,说明浏览器没有读取到图片信息导致。

浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。

publicclassServerDemo {
publicstaticvoidmain(String[] args) throwsIOException {
ServerSocketserver=newServerSocket(8888);
while(true){
Socketsocket=server.accept();
newThread(newWeb(socket)).start();
        }
    }
staticclassWebimplementsRunnable{
privateSocketsocket;
publicWeb(Socketsocket){
this.socket=socket;
        }
publicvoidrun() {
try{
//转换流,读取浏览器请求第一行BufferedReaderreadWb=newBufferedReader(newInputStreamReader(socket.getInputStream()));
Stringrequst=readWb.readLine();
//取出请求资源的路径String[] strArr=requst.split(" ");
System.out.println(Arrays.toString(strArr));
Stringpath=strArr[1].substring(1);
System.out.println(path);
FileInputStreamfis=newFileInputStream(path);
System.out.println(fis);
byte[] bytes=newbyte[1024];
intlen=0 ;
//向浏览器 回写数据OutputStreamout=socket.getOutputStream();
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
out.write("\r\n".getBytes());
while((len=fis.read(bytes))!=-1){
out.write(bytes,0,len);
                }
fis.close();
out.close();
readWb.close();
socket.close();
            }catch(Exceptionex){
            }
        }
    }

image.png

image.png

相关文章
|
29天前
|
Java
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
37 1
|
29天前
|
XML JSON 搜索推荐
【高手过招】JAVA网络编程对决:URL与URLConnection的高级玩法,你敢挑战吗?
【高手过招】JAVA网络编程对决:URL与URLConnection的高级玩法,你敢挑战吗?
49 0
|
29天前
|
Java
【实战演练】JAVA网络编程高手养成记:URL与URLConnection的实战技巧,一学就会!
【实战演练】JAVA网络编程高手养成记:URL与URLConnection的实战技巧,一学就会!
29 3
|
29天前
|
安全 Java 网络安全
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
33 2
|
30天前
|
存储 算法 Java
Java中的集合框架深度解析云上守护:云计算与网络安全的协同进化
【8月更文挑战第29天】在Java的世界中,集合框架是数据结构的代言人。它不仅让数据存储变得优雅而高效,还为程序员提供了一套丰富的工具箱。本文将带你深入理解集合框架的设计哲学,探索其背后的原理,并分享一些实用的使用技巧。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高效编程的大门。
|
1月前
|
Java 程序员 数据库连接
Java中的异常处理:从基础到高级云计算与网络安全:技术融合的双刃剑
【8月更文挑战第26天】在Java编程的世界中,异常处理是一块基石,它确保了程序的健壮性和稳定性。本文将带你从异常处理的基本概念出发,逐步深入到高级应用,包括自定义异常和最佳实践。你将学会如何优雅地处理程序中可能遇到的各种问题,以及如何设计异常处理策略来提升代码质量和维护性。
|
1月前
|
编解码 网络协议 Oracle
java网络编程入门以及项目实战
这篇文章是Java网络编程的入门教程,涵盖了网络编程的基础知识、IP地址、端口、通讯协议(TCP和UDP)的概念与区别,并提供了基于TCP和UDP的网络编程实例,包括远程聊天和文件传输程序的代码实现。
java网络编程入门以及项目实战
|
28天前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
56 0
|
29天前
|
缓存 Java API
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
【技术前沿】JAVA网络编程黑科技:URL与URLConnection的创新应用,带你飞越极限!
30 0
|
1月前
|
Java 网络安全 云计算
深入理解Java异常处理机制云计算与网络安全:技术挑战与应对策略
【8月更文挑战第27天】在Java编程的世界里,异常处理是维护程序健壮性的重要一环。本文将带你深入了解Java的异常处理机制,从基本的try-catch-finally结构到自定义异常类的设计,再到高级特性如try-with-resources和异常链的应用。通过具体代码示例,我们将探索如何优雅地管理错误和异常,确保你的程序即使在面对不可预见的情况时也能保持运行的稳定性。