使用Java完成Socket文件传输方式

 更新时间:2023年09月28日 16:29:13   作者:牛言牛语  
这篇文章主要介绍了使用Java完成Socket文件传输方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Java完成Socket文件传输

TCP协议的Socket文件传输

分别使用三个类(TCPFileUpload_Server服务器端、TCPFileUpload_Client客户端、StreamUtils工具类)完成图片的传输。

同样先运行服务器端文件,再运行客户端文件

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 服务器端
 */
public class TCPFileUpload_Server {
    public static void main(String[] args) throws Exception {
        //思路
        //在本机 的8888端口监听, 等待连接
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器端,监听8888端口,等待连接");
        //当没有客户端连接8888端口时,程序会 阻塞, 等待连接
        //如果有客户端连接,则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();
        //通过socket.getInputStream() 读取客户端写入到数据通道的数据, 并转换成字节数组
        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);
        //写入指定路径
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("src\\G.jpg"));
        bufferedOutputStream.write(bytes);
        //关闭IO流
        bufferedOutputStream.close();
        //向客户端回复收到图片
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("收到图片!");
        bufferedWriter.flush();
        socket.shutdownOutput();
        //关闭所有流
        bufferedWriter.close();
        bufferedInputStream.close();
        socket.close();
        serverSocket.close();
    }
}
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
/**
 * 客户端
 */
public class TCPFileUpload_Client {
    public static void main(String[] args) throws Exception {
        //连接服务端 (ip , 端口)
        //解读:连接本机的 8888端口, 如果连接成功,返回Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        System.out.println("客户端 连接端口:" + socket.getPort());
        //创建读取磁盘文件IO流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("src/P.jpg"));
        //获取字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);
        //通过Socket获取到输出流,将bytes发送到服务端
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        bufferedOutputStream.write(bytes);
        //关闭流对象,socket,刷新,添加终止符
        bufferedInputStream.close();
        bufferedOutputStream.flush();
        socket.shutdownOutput();
        //接受回复消息
        //此处可调用Utils的方法
//        String s = "";
//        while ((s = bufferedReader.readLine()) != null)
//            System.out.println(s);
        System.out.println(StreamUtils.streamToString(socket.getInputStream()));
        //关闭所有流
        bufferedOutputStream.close(); //socket的包装流不要过早关闭
        socket.close();
    }
}
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
 * 此类用于演示关于流的读写方法
 */
public class StreamUtils {
    /**
     * 功能:将输入流转换成byte[]
     *
     * @param is 输入流
     * @return byte数组
     * @throws Exception IO流异常
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
        byte[] b = new byte[1024];
        int len;
        while ((len = is.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        byte[] array = bos.toByteArray();
        bos.close();
        return array;
    }
    /**
     * 功能:将InputStream转换成String
     *
     * @param is 输入流
     * @return 字符串
     * @throws Exception IO流异常
     */
    public static String streamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) { //当读取到 null时,就表示结束
            builder.append(line + "\r\n");
        }
        return builder.toString();
    }
}

Java Socket数据传输基础以及优化

学到Java的TCP的Socket传输数据有些错误和心得在此记下

UDP和TCP

UDP:无连接通信协议,数据的发送端和接收端不用构建逻辑连接(发送时发送端无需确认接收端的存在,接收端无需返回相映给发送端)

  • UDP协议资源消耗小,通信效率高,通常用于音频、视频接收和普通数据的传输
  • 但是又因为UDP面向无连接性,不能保证数据的完整性,因此在传输重要数据的时候不推荐使用UDP协议

TCP:在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠性

  • 第一次握手:客户端向服务器发出连接请求
  • 第二次握手:服务器通知客户端收到了连接请求
  • 第三次握手:客户端再次向服务器发送确认信息,确认连接
  • TCP的传输安全性高于UDP,下载文件和浏览网页等使用的都是TCP

TCP的socket通信

  • 一种情况:客户端发送信息和接收信息需要输入输出流两个流,同理服务器也是这样,为了避免生成许许多多的流对象,所以可以利用socket中自带的输入输出流进行数据交互

Socket使用方法

1.客户端构造函数

 Socket socket = new Socket(ip,port); 

注解: 因为TCP是逻辑连接式的传输,所以客户端需要得知服务器的ip和端口

使用getOutputStream() 方法获得输出流

OutputStream cos = socket.getOutputStream();
cos.write("你好服务器".getBytes());  

注解:write使用字节输入输出,需要经过 字符–字节–字符的转换,转换的方法 getBytes() , new String(buf,0,len) ,如果想要直接输出可以使用打印流 printStream

使用getInputStream方法获得输入流

InputStream cis = socket.getInputStream()
//设置一个缓冲数组 temp数组大小大于所接收的数据量
byte[] temp = new byte[1024];
int len = cis.read(temp);
//将所接守的字节转换为字符串 这里使用 temp.toString()会有乱码
System.out.println(new String(temp,0,len));

注意最后要释放资源

socket.close();

2.服务器

服务器首先要创建ServerSocket设置端口号

ServerSocket server = new ServerSocket(port:8888);

注解:注意当编写循环响应时server的定义需要在while(true)循环之外,不然会显示端口被占用的情况

Exception in thread "main" java.net.BindException: Address already in use: NET_Bind

然后使用accept()方法返回Socket对象

Socket socket = server.accept();

accept() 方法具有阻塞作用,后面的实例会提到

通过accept()获得socket对象后,后面的操作方式与客户端的socket其实是一致的了

//获得流对象
InputStream sis = socket.getInputStream();
OutputStream sos = socket.getOutputStream();
//打印从客户端获得的数据
byte[] temp = new btye[1024];
sis.read(temp);
System.out.println(new String(temp));
//对客户端发出返回信息
sos.write("你也好,客户端".getBytes());

注意最后要释放资源

socket.close();
server.close();

总的应用代码,图片传输

客户端

import java.io.*;
import java.net.*;
public class TCPClient {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\Users\\子陌\\Pictures\\1.jpg");
        Socket socket = new Socket("192.168.1.8",8888);
        OutputStream cos = socket.getOutputStream();
        InputStream cis = socket.getInputStream();
        int len =0;
        byte[] bytes = new byte[1024];
        while((len = fis.read(bytes))!=-1){
            cos.write(bytes,0,len);
        }
        socket.shutdownOutput();
        byte[] bytes1 = new byte[1024];
        int len1=0;
        len1 =cis.read(bytes1);
        System.out.println(new String(bytes1));
        socket.close();
        fis.close();
    }
}

服务器

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class TCPSever {
    public static void main(String[] args) throws IOException {
        //这里ServerSocket要放到外面
        ServerSocket server = new ServerSocket(8888);
        while(true) { 
            new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            //先把accept响应放到前面
                            Socket socket = server.accept();
                            String fileName = "itcast" + System.currentTimeMillis() + new Random().nextInt();
                            File file = new File("D:\\TCPUpLoad");
                            if (!file.exists()) {
                                file.mkdirs();
                            }
                            FileOutputStream fos = new FileOutputStream(file + "\\" + fileName + ".jpg");//这里file既是File类也可以当作路径名称
                            //FileOutPutStream 会自动生成空文件
                            InputStream sis = socket.getInputStream();
                            OutputStream sos = socket.getOutputStream();
                            int len = 0;
                            byte[] bytes = new byte[1024];
                            while ((len = sis.read(bytes)) != -1) {
                                fos.write(bytes, 0, len);
                            }
                            sos.write("已经收到".getBytes());
                            socket.close();
                            fos.close();
                            //重点:要将资源全部释放,不然会占用线程或者端口
                        }catch(IOException e)
                        {
                            System.out.println(e);
                        }
                    }
                }).start();
        }
        //server.close();
    }
}

代码中需要注意的点

1.服务器ServerSocket需要放到while外部,while的目的是能够随时响应客户端的请求

2.fileName 使用了

String fileName = "itcast" + System.currentTimeMillis() + new Random().nextInt(9999); 

这也就是为什么从网上下载的图片有一大堆数字名字的原因

3.利用了多线程重写Runnable中的 run 方法,运用了匿名内部类,大大提高了服务器的响应速度

4.这里涉及之前提到的一个容易出bug的问题,server.accept() 的阻塞作用,因为会服务器会不断的while循环就会开启很多线程,如果

FileOutStream fos = new FileOutStream(...) 

运行先于server.accept(),就会每产生一个线程就创建一个空文件 我就是犯了这个错误导致电脑多了几万个带.jpg的空文件

5.服务器和客户端要同时运作时需要知道自己的ip地址,可以在运行的 cmd 中 输入 ipconfig 进行查询

6.Runnable 中的 run 方法并不能自动抛出异常,只能手动 try catch 详见上方代码

7.还有一个阻塞问题就是,当客户端读取结束后传输给服务器,但是服务器并不知道读取结束就会导致客户端和服务器的同时阻塞,这时需要 Socket 中的 shutdownOutput() 方法告诉服务器已经读取完毕

 while((len = fis.read(bytes))!=-1)
       {
            cos.write(bytes,0,len);
        }
        socket.shutdownOutput();

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中 instanceof 的用法详细介绍

    Java中 instanceof 的用法详细介绍

    在 Java 中,instanceof 是一个二元运算符(类型比较操作符),用于检查一个对象是否是某个特定类、接口的实例,或者是否是其子类的实例,这篇文章主要介绍了Java中 instanceof 的用法详细介绍,需要的朋友可以参考下
    2025-05-05
  • springmvc的文件保存方法详解

    springmvc的文件保存方法详解

    这篇文章主要介绍了springmvc的文件保存方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Spring Boot中的日志log原理与自定义日志格式

    Spring Boot中的日志log原理与自定义日志格式

    这篇文章主要介绍了Spring Boot中的日志log原理与自定义日志格式,本文将介绍一种日志追踪的方式,来全链路追踪请求的处理,并设置相应的格式,输出更加直观、易处理的日志,需要的朋友可以参考下
    2026-02-02
  • Spring实战之Bean的作用域request用法分析

    Spring实战之Bean的作用域request用法分析

    这篇文章主要介绍了Spring实战之Bean的作用域request用法,结合实例形式分析了spring中Bean的request作用域相关使用技巧与操作注意事项,需要的朋友可以参考下
    2019-11-11
  • idea导入gradle项目时遇到的坑及解决

    idea导入gradle项目时遇到的坑及解决

    这篇文章主要介绍了idea导入gradle项目时遇到的坑及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • 初始JAVA模块化开发的超详细步骤(适合菜鸟)

    初始JAVA模块化开发的超详细步骤(适合菜鸟)

    这篇文章主要介绍了初始JAVA模块化开发的超详细步骤,详细解释了模块描述符的职责、模块路径的概念以及如何使用命令行运行模块化Java程序,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2025-03-03
  • 详解SpringBoot整合MyBatis详细教程

    详解SpringBoot整合MyBatis详细教程

    这篇文章主要介绍了详解SpringBoot整合MyBatis详细教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • 关于Mysql的四种存储引擎

    关于Mysql的四种存储引擎

    这篇文章主要介绍了关于Mysql的四种存储引擎,MySql的核心就是存储引擎,不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能,需要的朋友可以参考下
    2023-05-05
  • Java中Thread类基本用法详解

    Java中Thread类基本用法详解

    Java中的Thread类是用于创建和管理线程的类,Thread类提供了许多方法来管理线程,包括启动线程、中断线程、暂停线程等,下面这篇文章主要给大家介绍了关于Java中Thread类基本用法的相关资料,需要的朋友可以参考下
    2023-06-06
  • IntelliJ IDEA 关闭多余项目的操作方法

    IntelliJ IDEA 关闭多余项目的操作方法

    这篇文章主要介绍了IntelliJ IDEA 关闭多余项目的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04

最新评论