絮叨
本人学生,往前一年左右的时间用在了Java上
都说写博客、随笔是百利一害的事情-->一害是费时间
近期也是在此申请开通了博客
此篇也算是开博第一篇,所以絮叨一下
——————————————————————————————————————————————————————
问题发现与解决
今天在写Socket的文件传输
程序涉及到Socket、线程、文件操作、流等
目标是Client可以向Server提交文件名
然后Server进行响应-->文件存在则传输,不存在则回复文件不存在的消息
因为想要完成多次文件传输,所以把方法块放进了循环
于是希望所有打开的资源,像InputStream, OutputStream,这些可以再运行过程中一直打开, 直到程序结束才关闭
——————————————————————————————————————————————————————
文件传输用的是 DataInputStream 中的 read(byte[] b, int off, int len) 和 DataOutputStream 中的 write(byte[] b, int off, int len)
JavaDoc 地址:
https://docs.oracle.com/javase/8/docs/api/java/io/DataInputStream.html
https://docs.oracle.com/javase/8/docs/api/java/io/DataOutputStream.html
简单描述
DataInputStream 和 DataOutputStream 是字节流 可以视为-->他们操作的对象是每字节的编码-->机器认识而我不认识的东西
相对应的Writer和Reader 是字符流 他们操作的是将字节编码转化为字符的我能看懂的东西
所以在传输文件的时候使用字节流是更为合适的选择
——————————————————————————————————————————————————————
网上有关文件传输的代码大多是这个样子
通过(FileInputStream) fileInput 从文件读取,并通过(DataOutputStream) out 发送:
1 while((length = fileInput.read(bytes)) != -1) { 2 out.write(bytes, 0, length); 3 out.flush(); 4 }
通过(DataInputStream) in 读取数据,并通过(FileOutputStream) fileOutput 写入文件
1 while((length = in.read(bytes, 0, bytes.length)) != -1) { 2 fileOutput.write(bytes, 0, length); 3 fileOutput.flush(); 4 }
这两段代码没有问题,都是可以运行的
我的问题出现在接收文件数据的时候,文件数据已经发送完毕,但是程序没有跳出循环
其症结是while()中的判断条件是要 length = -1 才会跳出循环
可以通过上方贴出来的JavaDoc的截图看到-1表示的读取到流的结尾
但是想要让它读取到流的结尾 是要发送方关闭流
在发送方发送完成后需要有类似这样一句 out.close()
执行out.close() 实际上它所包装的socket也随之关闭了
也就是说整条连接都断了 这不是我想要的
我想要的是发送完一个 程序还能够继续响应我从Client发送来的消息
于是需要引入新的判断条件 让接收方知道它想要的文件已经传输完毕
——————————————————————————————————————————————————————
我找到的一个简单的方法是 利用文件的length
传输文件之前,在Server检查文件名是否存在,向Client反馈的时候,将文件的length也发送过去
这样就可在接收文件的时候对本地文件的length和传输过来的length进行比较
从而判断文件传输是否完成
1 private static void getBytes(String fileName, long fileLength) throws IOException { 2 File file = new File(fileName); 3 FileOutputStream f = new FileOutputStream(file); 4 byte[] bytes = new byte[1024]; 5 int length = 0; 6 while(true) { 7 length = in.read(bytes, 0, bytes.length); 8 f.write(bytes, 0, length); 9 f.flush(); 10 /* 11 * when fileLength equals to length of local file, it 12 * represents the downloading is finished 13 */ 14 if(fileLength == file.length()) break; 15 } 16 f.close(); 17 }
——————————————————————————————————————————————————————
代码
下面是我所写的Socket文件传输的代码
Server
在Server端循环监听绑定端口
在listenRequest()中为每个新连接进来的socket新建一个线程
以此来并发响应每个对应Client的请求
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 import java.util.logging.Level; 9 import java.util.logging.Logger; 10 11 public class Server { 12 13 14 private static ServerSocket server; 15 private static Socket socket; 16 private static final int port = 123456; 17 private static Logger logger; 18 19 static{ 20 logger = Logger.getLogger("Server"); 21 logger.setLevel(Level.INFO); 22 } 23 24 public static void main(String[] args) throws IOException { 25 //create server with port 26 server = new ServerSocket(port); 27 logger.info("Server Starts"); 28 while(true) { 29 //listen server and bind socket when accept a connection 30 socket = server.accept(); 31 //record incoming connections 32 logger.info("Get connection from " + socket.getRemoteSocketAddress()); 33 listenRequest(socket); 34 } 35 } 36 37 /** 38 * create a new thread for socket.<br> 39 * listen input from socket<br> 40 * process requests and give responses to client. 41 * @param socket the specific socket that connects to the server 42 */ 43 private static void listenRequest(Socket socket) { 44 /* 45 * create a new thread for this socket so that it 46 * can be responsible for this socket only 47 */ 48 new Thread(new Runnable(){ 49 DataInputStream in; 50 DataOutputStream out; 51 boolean status; 52 53 @Override 54 public void run() { 55 try { 56 logger.info("Start Listening Socket Input"); 57 /* 58 * create DataInputStream and DataOutputStream objects to get 59 * the stream of socket 60 */ 61 in = new DataInputStream(socket.getInputStream()); 62 out = new DataOutputStream(socket.getOutputStream()); 63 status = true; 64 while(status) { 65 processInput(); 66 } 67 } catch (IOException e) { 68 e.printStackTrace(); 69 } 70 } 71 72 /** 73 * process requests from client and give response respectively 74 * @throws IOException 75 */ 76 private void processInput() throws IOException { 77 String content = in.readUTF(); 78 String[] command = content.split(":"); 79 String fileName = null; 80 switch(command[0]) { 81 case "FILE_TRANSFER_REQUEST": 82 /* 83 * structure of FILE_REQUEST should be: 84 * @COMMAND:@FILENAME 85 */ 86 logger.info("Get File Request From " + socket.getRemoteSocketAddress()); 87 fileName = command[1]; 88 fileTransferResponse(fileName); 89 break; 90 case "FILE_BYTES_REQUEST": 91 /* 92 * structure of FILE_BYTES_REQUEST 93 * @Command:@FileName 94 */ 95 fileName = command[1]; 96 logger.info("Get File Bytes Request From " + socket.getRemoteSocketAddress()); 97 fileBytesResponse(fileName); 98 break; 99 case "DISCONNECT": 100 logger.info("Get Disconnect From " + socket.getRemoteSocketAddress()); 101 stop(); 102 break; 103 default: 104 break; 105 } 106 107 } 108 109 /** 110 * close relevant resources and change status to 111 * false to end this thread 112 * @throws IOException 113 */ 114 private void stop() throws IOException { 115 in.close(); 116 out.close(); 117 socket.close(); 118 status = false; 119 } 120 121 /** 122 * send file bytes(raw data) to client 123 * @param fileName name of file 124 * @throws IOException 125 */ 126 private void fileBytesResponse(String fileName) throws IOException { 127 File file = new File(fileName); 128 FileInputStream fileInput = new FileInputStream(file); 129 byte[] bytes = new byte[1024]; 130 int length = 0; 131 while((length = fileInput.read(bytes)) != -1) { 132 out.write(bytes, 0, length); 133 out.flush(); 134 } 135 logger.info("File Transfer Finished"); 136 fileInput.close(); 137 } 138 139 private void fileTransferResponse(String fileName) throws IOException { 140 File file = new File(fileName); 141 StringBuffer response = new StringBuffer(); 142 /* 143 * if file does not exist, response to the client 144 * with error message 145 */ 146 if(!file.exists()) { 147 //make sure there is nothing in response 148 response.setLength(0); 149 /* 150 * structure of FILE_NOT_EXISTS should be: 151 * @COMMAND:@MESSAGE 152 */ 153 response.append("FILE_NOT_EXISTS:"); 154 response.append("The file \"" + fileName+ "\" your request does not exist."); 155 out.writeUTF(response.toString()); 156 out.flush(); 157 }else { 158 /* 159 * structure of FILE_EXISTS should be; 160 * @Command:@FileName:@FileLength 161 */ 162 response.setLength(0); 163 response.append("FILE_EXISTS:"); 164 response.append(fileName+":"); 165 response.append(file.length()); 166 out.writeUTF(response.toString()); 167 out.flush(); 168 } 169 } 170 171 }).start(); 172 } 173 174 175 176 }
Client
Client中不需要多线程 因此一个主线程完成所有工作
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.PrintStream; 7 import java.net.Socket; 8 import java.util.Scanner; 9 import java.util.logging.Level; 10 import java.util.logging.Logger; 11 12 public class Client { 13 14 private static Socket socket; 15 private static final int port = 123456; 16 private static final String host = "***********"; 17 private static Logger logger; 18 private static PrintStream display; 19 private static DataOutputStream out; 20 private static DataInputStream in; 21 private static Scanner sc; 22 23 static{ 24 logger = Logger.getLogger("Client"); 25 logger.setLevel(Level.INFO); 26 display = System.out; 27 sc = new Scanner(System.in); 28 } 29 public static void main(String[] args) { 30 try { 31 /* create a new object of Socket and try to connect to 32 * the server with host and port 33 */ 34 socket = new Socket(host, port); 35 36 //logger.info("Connect to Server "+ host + " with Port "+ port); 37 out = new DataOutputStream(socket.getOutputStream()); 38 in = new DataInputStream(socket.getInputStream()); 39 while(true) { 40 welcomeInterface(display, sc); 41 } 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } 45 } 46 /** 47 * An interface that instruct users to interact with this program 48 * @param display system out stream 49 * @param sc system input stream 50 * @throws IOException 51 */ 52 private static void welcomeInterface(PrintStream display, Scanner sc) throws IOException { 53 display.println(); 54 for(int i = 0; i < 15; i++) { 55 display.append("#"); 56 } 57 display.append(" Welcome To Week6Client "); 58 for(int i = 0; i < 15; i++) { 59 display.append("#"); 60 } 61 display.println(); 62 display.println(); 63 display.printf("%5s%-30s", " ", "Please Enter The File Name: "); 64 String fileName = sc.nextLine(); 65 display.println(); 66 fileRequest(fileName); 67 } 68 69 /** 70 * send file request to server, and process responses of this request 71 * from server. 72 * @param fileName the name of file that expect to download 73 * @throws IOException 74 */ 75 private static void fileRequest(String fileName) throws IOException { 76 StringBuffer content = new StringBuffer(); 77 /* 78 * structure of FILE_REQUEST: 79 * @command:@fileName 80 */ 81 content.append("FILE_TRANSFER_REQUEST:"); 82 content.append(fileName); 83 out.writeUTF(content.toString()); 84 out.flush(); 85 //logger.info("Send File Request to Server"); 86 // after send request to server, waiting for response from server 87 String[] response = in.readUTF().split(":"); 88 //logger.info("Get Response From Server: " + response[0]); 89 switch(response[0]) { 90 case "FILE_EXISTS": 91 fileName = response[1]; 92 File file = new File(fileName); 93 /* 94 * structure of FILE_EXISTS: 95 * @Command:@FileName:@FileLength 96 */ 97 if(file.exists()) { 98 display.printf("%5s%s", " ", "File \"" +fileName + "\" exists in current director.\n"); 99 display.printf("%5s%s", " ", "Do you want to overrid it? \n"); 100 display.printf("%5s%s", " ", "Y/y -> Override, Other -> Cancel\n"); 101 display.printf("%5s", " "); 102 if(!sc.nextLine().toLowerCase().equals("y")) break; 103 else { 104 file.delete(); 105 file.createNewFile(); 106 } 107 } 108 content.setLength(0); 109 content.append("FILE_BYTES_REQUEST:"); 110 content.append(fileName); 111 out.writeUTF(content.toString()); 112 display.printf("%5s%-30s%s", "", "Downloading ", fileName+"\n"); 113 long fileLength = Long.parseLong(response[2]); 114 getBytes(fileName, fileLength); 115 display.printf("%5s%-20s", "", "Download Done\n"); 116 break; 117 case "FILE_NOT_EXISTS": 118 display.printf("%5s%-50s", "", response[1]); 119 break; 120 default: 121 break; 122 } 123 display.println(); 124 display.printf("%5s%s", " ", "Do you want to continue? \n"); 125 display.printf("%5s%s", " ", "Y/y -> Continue, Other -> Quit \n"); 126 display.printf("%5s", ""); 127 /* 128 * if user enter other character, call stop() to close relevant 129 * resources and end this process 130 */ 131 if(!sc.nextLine().toLowerCase().equals("y")) { 132 display.println(); 133 display.println(); 134 stop(); 135 } 136 display.println(); 137 display.println(); 138 139 } 140 141 /** 142 * close relevant resources and call System.exit(int status) to 143 * end this process 144 * @throws IOException 145 */ 146 private static void stop() throws IOException { 147 out.writeUTF("DISCONNECT"); 148 out.close(); 149 in.close(); 150 sc.close(); 151 socket.close(); 152 System.exit(0); 153 } 154 155 /** 156 * Receive bytes from server. Once the length of local file is same 157 * as server's, transfer ends. 158 * @param fileName the name of file that is going to operate 159 * @param fileLength the length of file on server 160 * @throws IOException 161 */ 162 private static void getBytes(String fileName, long fileLength) throws IOException { 163 File file = new File(fileName); 164 FileOutputStream f = new FileOutputStream(file); 165 byte[] bytes = new byte[1024]; 166 int length = 0; 167 while(true) { 168 length = in.read(bytes, 0, bytes.length); 169 f.write(bytes, 0, length); 170 f.flush(); 171 /* 172 * when fileLength equals to length of local file, it 173 * represents the downloading is finished 174 */ 175 if(fileLength == file.length()) break; 176 } 177 f.close(); 178 } 179 180 }
第一次写东西,没什么经验,如有错误、问题请指出
如需讨论 也可发邮件给我哈
email: ancientcoinli@gmail.com
——————————————————————————————————————————————————————