• 《计算机网络·自顶向下方法》 第二章 套接字编程作业 JAVA


    作业1

    概述:

    如何使用JAVA创建HTTP报文,在本回答中没有体现。
    本回答的报文均由一个自定义类而实现。

    代码逻辑:
    1.建立连接
    2.客户端发送请求的文件路径到服务端
    3.服务端寻找文件。如果找到,发送文件,并且发送一个确定报文。如果没有找到,发送一个没有找到的报文。

    服务端

    总所周知,服务端可能需要同时为多个客户端提供服务,所以很自然的,博主使用了多线程技术。具体的,博主使用了线程池技术
    同时,在服务端,对于同一类服务,线程池只需要一个就够了,所以博主使用了单例模式来设计代码。

    在编写代码的过程中,我遇到了两个问题:
    第一个问题,TCP是面向字节流的运输层协议,在发送一个文件后,再发送一个Object,如果之前不指定文件的长度,那么接收端根本无法确定,接收到的数据属于文件,还是属于后来发送的Object
    第二个问题,发送Object的时候,一开始我使用了ObjectOutputStream 发送,但是很显然地,接收端不能使用ObjectInputStream接收,因为在接收文件时,该Object的一部分字节可能已经被读入到byte[]中了。所以客户端必须使用socket的InputStream接收数据,再通过相关方法(详见代码)将byte转化为Object。问题就出在这里,我尝试了很久,但是一直在类型转换的时候出现异常,直到我将发送的Object在服务端转化为byte[],才发现得到的byte[]长度和客户端收到的不一样。为什么会这样,我找了很久的答案,但是一无所获,只能假设,在ObjectOutputStream基于Socket.OutputStream的时候,会做相关优化,减少字节长度。通过比对byte[]的内容大致可以猜测,基于Socket.OutputStream的ObjectOutputStream发送Object时,会忽略一部分Object的头部信息。最后的解决方法是,不使用ObjectOutputStream直接发送,而是将其转化为byte[]之后,再通过Socket.OutputStream发送。

    客户端

    客户端不用考虑多线程的问题
    实现比服务端简单得多,只需要根据服务端代码接收相关信息即可

    Datagram类

    自定义类,同时存在于客户端和服务端,用于客户端和服务端接收信息,上文提到的Object实际上就是指Datagram的某个实例

    Datagram类
    
    package DatagramBean;
    

    import java.io.Serializable;
    import java.util.Objects;

    /**

    • @Author : ZGQ

    • @Date : 2020/2/16 18:10

    • @Version : 1.0
      */
      public class Datagram implements Serializable {
      static final long serialVersionUID = 952752194657456L;
      int code;
      String message;

      public Datagram() {
      }

      public Datagram(int code, String message) {
      this.code = code;
      this.message = message;
      }

      public int getCode() {
      return code;
      }

      public void setCode(int code) {
      this.code = code;
      }

      public String getMessage() {
      return message;
      }

      public void setMessage(String message) {
      this.message = message;
      }

      @Override
      public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Datagram datagram = (Datagram) o;
      return code == datagram.code &&
      Objects.equals(message, datagram.message);
      }

      @Override
      public int hashCode() {
      return Objects.hash(code, message);
      }

      @Override
      public String toString() {
      return "Datagram{" +
      "code=" + code +
      ", message='" + message + ''' +
      '}';
      }
      }

    服务端代码
    
    package TCP;
    import DatagramBean.Datagram;
    

    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    /**

    • @Author : ZGQ
    • @Date : 2020/2/16 18:03
    • @Version : 1.0
      */

    //多线程
    class ServerUtil implements Runnable{

    private static final int MAX_BUFF_SIZE = 1024;
    
    private Socket socket = null;
    private InputStream is = null;
    private FileInputStream fis = null;
    private OutputStream os = null;
    private ObjectOutputStream oos=null;
    
    public ServerUtil(Socket socket){
        this.socket=socket;
    }
    
    //接收客户端的请求的文件路径
    private String receURL() throws IOException {
        byte[] data = new byte[MAX_BUFF_SIZE];
        int len = is.read(data);
        String s1 = new String(data,0,len);
        return s1;
    }
    
    //发送文件
    private void sendFile(String s1) throws IOException {
        fis = new FileInputStream(s1);
        byte[] data = new byte[MAX_BUFF_SIZE];
        int len = 0;
        fis = new FileInputStream(s1);
        int times = 0;
        int lst = 0;
        while ((len=fis.read(data))!=-1){
            lst=len;
            os.write(data,0,len);
            times++;
        }
        os.flush();
        System.out.println("lst = " + lst);
    }
    
    //通过路径获取文件长度
    private long getFileLenByPath(String path) throws IOException {
        File file = new File(path);
        return file.length();
    }
    
    //发送获取成功的回复
    private void sendResSuccess(long len) throws IOException {
        sendRes(200,"len = "+len);
    }
    
    //发送获取失败的回复
    private void sendResFailed() throws IOException {
        sendRes(404,"文件不存在");
    }
    
    //发送回复
    private void sendRes(int code,String message) throws IOException {
        Datagram datagram = new Datagram(code,message);
        oos.writeObject(datagram);
        oos.flush();
    }
    
    //将Object转化为字节
    private byte[] ObjectToByte(Datagram datagram){
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
        byte[] data = new byte[MAX_BUFF_SIZE];
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(datagram);
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(oos!=null){ oos.close(); }
                if(baos!=null){ baos.close(); }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new byte[0];
    }
    
    
    //以字节的方式发送回复
    private void sendResByte(int code,String message) throws IOException {
        Datagram datagram = new Datagram(code,message);
        byte[] bytes = ObjectToByte(datagram);
        os.write(bytes);
        System.out.println(bytes.length);
    }
    
    @Override
    public void run() {
        try {
    
            is = socket.getInputStream();
            os = socket.getOutputStream();
            oos = new ObjectOutputStream(os);
    
            String path = receURL();
            System.out.println("path = " + path);
            long len = 0;
            try{
                long fileLen = getFileLenByPath(path);
                System.out.println(fileLen);
                sendResSuccess(fileLen);
                sendFile(path);
                sendResByte(200,"东西收到了吧,哈哈");
            }catch (IOException e){
                sendResFailed();
                System.out.println("没有找到对应的资源");
            }
        } catch (IOException e) {
            System.err.println("socket输入输出流创建失败");
        } finally {
            try {
    
                if(oos!=null)oos.close();
                if(os!=null)os.close();
                if(fis!=null)fis.close();
                if(is!=null)is.close();
                if(socket!=null)socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("OVERD
    ");
    }
    

    }

    public class Task1 {
    //单例模式
    static Task1 task=null;
    ExecutorService threadPool = null;
    private Task1(){
    threadPool=Executors.newFixedThreadPool(50);
    }
    public static Task1 getInstance(){
    if (task == null) {
    synchronized (Task1.class) {
    if (task == null) {
    task = new Task1();
    }
    }
    }
    return task;
    }

    //调用此函数开始服务
    public void server()  {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(9527);
            while (true){
                threadPool.execute(new ServerUtil(serverSocket.accept()));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(serverSocket!=null)serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    }

    客户端代码
    
    package TCP;
    import DatagramBean.Datagram;
    import com.sun.istack.internal.ByteArrayDataSource;
    import com.sun.scenario.effect.Merge;
    
    import java.io.*;
    import java.net.Socket;
    
    /**
     * @Author : ZGQ
     * @Date : 2020/2/16 19:01
     * @Version : 1.0
     */
    public class Task1 {
    
        public static final int MAX_BUF_SIZE = 1024;
        Socket socket = null;
        OutputStream os = null;
        InputStream is = null;
        FileOutputStream fos = null;
        ObjectInputStream ois = null;
    
        //发送要访问的服务端文件地址
        private void sendURL() throws IOException {
            os.write("D:\Java\server_source\setu.png".getBytes());
            os.flush();
        }
    
        //将byte[]转化为Object
        private Object byteToObject(byte[] bytes) throws IOException, ClassNotFoundException {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(byteArrayInputStream);
            return ois.readObject();
    
        }
    
        //接收文件
        //注意TCP面向字节流,收到的数据中结尾可能存在不属于文件的部分
        //此方法的返回值即为收到的多余数据,它属于TCP传送的下一个(或多个)对象
        private byte[] receFile(String dest, int fileLen) throws IOException {
            fos = new FileOutputStream(dest);
            byte[] data = new byte[MAX_BUF_SIZE];
            int receivedLen = 0;
            while (receivedLen < fileLen) {
                int len = is.read(data);
                if (len == -1) {
                    return new byte[0];
                }
                int len_of_remain = Math.min(fileLen - receivedLen, MAX_BUF_SIZE);
                fos.write(data, 0, len_of_remain);
                receivedLen += len;
                if (receivedLen == fileLen) {
                    System.out.println("没有多余数据");
                    return new byte[0];
                } else if (receivedLen > fileLen) {
                    return subByte(data, len_of_remain, len);
                }
            }
            return new byte[0];
        }
    
        //接收服务端的回复,内容可能包括文件是否存在,文件长度等
        private Datagram receRes() throws IOException, ClassNotFoundException {
            return (Datagram) ois.readObject();
        }
    
        //通过服务端的回复,获取文件长度
        private int getLen(Datagram datagram) {
            return Integer.parseInt(datagram.getMessage().substring(6));
        }
    
        //获取一个byte[] 的一段
        private byte[] subByte(byte[] data, int start, int totLen) {
            byte[] newData = new byte[totLen - start];
            int cnt = 0;
            for (int i = start; i < totLen; i++) {
                newData[cnt++] = data[i];
            }
            return newData;
        }
    
        //合并两个byte[]
        private byte[] byteMerge(byte[] bytes1, byte[] bytes2) {
            int len = bytes1.length + bytes2.length;
            byte[] newByte = new byte[len];
            int cnt = 0;
            for (byte b : bytes1) {
                newByte[cnt++] = b;
            }
            for (byte b : bytes2) {
                newByte[cnt++] = b;
            }
            return newByte;
        }
    
        //receFile()中,可能已经读取了下一个对象的一部分,这里读取剩下的所有部分
        private byte[] receRemain() throws IOException {
            byte[] data = new byte[MAX_BUF_SIZE];
            byte[] ret = new byte[0];
            int len = 0;
            int totlen = 0;
            while ((len = is.read(data)) != -1) {
                ret = byteMerge(ret, subByte(data, 0, len));
                totlen += len;
            }
            if (totlen >= 0) return ret;
            else return new byte[0];
        }
    
        //客户端程序
        public void client() {
            try {
    
                socket = new Socket("127.0.0.1", 9527);
                os = socket.getOutputStream();
                is = socket.getInputStream();
                ois = new ObjectInputStream(is);
                sendURL();
                Datagram datagram = receRes();
                long fileLen = getLen(datagram);
                if(fileLen==0){
                    System.out.println("文件缺失");
                    return;
                }
    
                System.out.println("fineLen = "+fileLen);
    
                byte[] remain1 = receFile("D:\Java\client_workspace\name.png", (int) fileLen);
                byte[] remain2 = receRemain();
                byte[] Obj = byteMerge(remain1,remain2);
    
                datagram = (Datagram) byteToObject(Obj);
                System.out.println(datagram);
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (ois != null) ois.close();
                    if (fos != null) fos.close();
                    if (is != null) is.close();
                    if (os != null) os.close();
                    if (socket != null) socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    
  • 相关阅读:
    安卓模拟器BlueStacks 安装使用教程(图解)
    照相机滤镜使用,优化解码和滤镜导致的预览卡屏现象
    移动语音引擎相关开发笔记
    Linux下查看硬件信息的方法
    linux下彻底卸载mysql 图解教程
    linux下yum安装及配置
    mybatis中的resultMap
    项目管理模式之如何去除SVN标记
    myeclipse中的classpath
    Spring的AOP配置
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/12325405.html
Copyright © 2020-2023  润新知