java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java完成Socket文件传输

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

作者:牛言牛语

这篇文章主要介绍了使用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:无连接通信协议,数据的发送端和接收端不用构建逻辑连接(发送时发送端无需确认接收端的存在,接收端无需返回相映给发送端)

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

TCP的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();

总结

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

您可能感兴趣的文章:
阅读全文