zoukankan      html  css  js  c++  java
  • 《计算机网络·自顶向下方法》 第二章 套接字编程作业 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();
                }
            }
        }
    }
    
    
    
  • 相关阅读:
    Realtime crowdsourcing
    maven 常用插件汇总
    fctix
    sencha extjs4 command tools sdk
    首次吃了一颗带奶糖味的消炎药,不知道管用不
    spring mvc3 example
    ubuntu ati driver DO NOT INSTALL recommand driver
    yet another js editor on windows support extjs
    how to use springsource tools suite maven3 on command
    ocr service
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/12325405.html
Copyright © 2011-2022 走看看