使用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();

总结

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

相关文章

  • Maven jar包冲突的解决方案

    Maven jar包冲突的解决方案

    这篇文章主要介绍了Maven jar包冲突的解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 使用spring配置文件.xml的头文件

    使用spring配置文件.xml的头文件

    这篇文章主要介绍了使用spring配置文件.xml的头文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Java语言的11大特点(Java初学者必知)

    Java语言的11大特点(Java初学者必知)

    Java是一种简单的,面向对象的,分布式的,解释型的,健壮安全的,结构中立的,可移植的,性能优异、多线程的静态语言。这篇文章主要介绍了Java语言的11大特点,需要的朋友可以参考下
    2020-07-07
  • 如何解决Idea断点调试乱跳的问题

    如何解决Idea断点调试乱跳的问题

    这篇文章主要介绍了如何解决Idea断点调试乱跳的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • java  ThreadPoolExecutor使用方法简单介绍

    java ThreadPoolExecutor使用方法简单介绍

    这篇文章主要介绍了java ThreadPoolExecutor使用方法简单介绍的相关资料,需要的朋友可以参考下
    2017-02-02
  • java8 多个list对象用lambda求差集操作

    java8 多个list对象用lambda求差集操作

    这篇文章主要介绍了java8 多个list对象用lambda求差集操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • 关于重写equals()方法和hashCode()方法及其简单的应用

    关于重写equals()方法和hashCode()方法及其简单的应用

    这篇文章主要介绍了关于重写equals()方法和hashCode()方法及其简单的应用,网上的知识有些可能是错误的,关于 equals() 方法的理解,大家讨论不一样,需要的朋友可以参考下
    2023-04-04
  • Java实现Swing组件定制Button示例

    Java实现Swing组件定制Button示例

    这篇文章主要介绍了Java实现Swing组件定制Button,涉及java Swing组件Button相关属性设置与使用操作技巧,需要的朋友可以参考下
    2018-01-01
  • SpringBoot 工程中的异常处理方式

    SpringBoot 工程中的异常处理方式

    这篇文章主要介绍了SpringBoot 工程中的异常处理方式,帮助大家更好的理解和学习使用springboot框架,感兴趣的朋友可以了解下
    2021-02-02
  • Java设计模式之组合模式的示例详解

    Java设计模式之组合模式的示例详解

    组合模式,又叫部分整体模式,它创建了对象组的数据结构组合模式使得用户对单个对象和组合对象的访问具有一致性。本文将通过示例为大家详细介绍一下组合模式,需要的可以参考一下
    2022-03-03

最新评论