【Java】网络编程(二)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 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

相关文章
|
1月前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
67 11
|
2月前
|
Java 程序员
JAVA程序员的进阶之路:掌握URL与URLConnection,轻松玩转网络资源!
在Java编程中,网络资源的获取与处理至关重要。本文介绍了如何使用URL与URLConnection高效、准确地获取网络资源。首先,通过`java.net.URL`类定位网络资源;其次,利用`URLConnection`类实现资源的读取与写入。文章还提供了最佳实践,包括异常处理、连接池、超时设置和请求头与响应头的合理配置,帮助Java程序员提升技能,应对复杂网络编程场景。
80 9
|
2月前
|
人工智能 Java 物联网
JAVA网络编程的未来:URL与URLConnection的无限可能,你准备好了吗?
随着技术的发展和互联网的普及,JAVA网络编程迎来新的机遇。本文通过案例分析,探讨URL与URLConnection在智能API调用和实时数据流处理中的关键作用,展望其未来趋势和潜力。
60 7
|
5月前
|
Java
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
71 1
|
2月前
|
网络协议 Java 物联网
Java网络编程知识点
Java网络编程知识点
65 13
|
2月前
|
Java 开发者
JAVA高手必备:URL与URLConnection,解锁网络资源的终极秘籍!
在Java网络编程中,URL和URLConnection是两大关键技术,能够帮助开发者轻松处理网络资源。本文通过两个案例,深入解析了如何使用URL和URLConnection从网站抓取数据和发送POST请求上传数据,助力你成为真正的JAVA高手。
75 11
|
2月前
|
安全 Java API
深入探索Java网络编程中的HttpURLConnection:从基础到进阶
本文介绍了Java网络编程中HttpURLConnection的高级特性,包括灵活使用不同HTTP方法、处理重定向、管理Cookie、优化安全性以及处理大文件上传和下载。通过解答五个常见问题,帮助开发者提升网络编程的效率和安全性。
182 9
|
2月前
|
JSON 安全 算法
JAVA网络编程中的URL与URLConnection:那些你不知道的秘密!
在Java网络编程中,URL与URLConnection是连接网络资源的两大基石。本文通过问题解答形式,揭示了它们的深层秘密,包括特殊字符处理、请求头设置、响应体读取、支持的HTTP方法及性能优化技巧,帮助你掌握高效、安全的网络编程技能。
103 9
|
2月前
|
JSON Java API
JAVA网络编程新纪元:URL与URLConnection的神级运用,你真的会了吗?
本文深入探讨了Java网络编程中URL和URLConnection的高级应用,通过示例代码展示了如何解析URL、发送GET请求并读取响应内容。文章挑战了传统认知,帮助读者更好地理解和运用这两个基础组件,提升网络编程能力。
71 5
|
2月前
|
数据采集 Java API
java怎么设置代理ip:简单步骤,实现高效网络请求
本文介绍了在Java中设置代理IP的方法,包括使用系统属性设置HTTP和HTTPS代理、在URL连接中设置代理、设置身份验证代理,以及使用第三方库如Apache HttpClient进行更复杂的代理配置。这些方法有助于提高网络请求的安全性和灵活性。

热门文章

最新文章