絮叨
本人学生,往前一年左右的时间用在了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 地址:
简单描述
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, it12 * represents the downloading is finished13 */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. 39 * listen input from socket 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 thread112 * @throws IOException113 */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 file124 * @throws IOException125 */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 client144 * with error message145 */146 if(!file.exists()) {147 //make sure there is nothing in response148 response.setLength(0);149 /* 150 * structure of FILE_NOT_EXISTS should be:151 * @COMMAND:@MESSAGE152 */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:@FileLength161 */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 relevant129 * resources and end this process130 */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 process144 * @throws IOException145 */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 same157 * as server's, transfer ends.158 * @param fileName the name of file that is going to operate159 * @param fileLength the length of file on server160 * @throws IOException161 */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, it173 * represents the downloading is finished174 */175 if(fileLength == file.length()) break;176 }177 f.close();178 }179 180 }
第一次写东西,没什么经验,如有错误、问题请指出
如需讨论 也可发邮件给我哈
email: ancientcoinli@gmail.com
——————————————————————————————————————————————————————