更新時(shí)間:2022-11-22 09:03:03 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽1448次
套接字是一種軟件(邏輯)端點(diǎn),它在服務(wù)器和一個(gè)或多個(gè)客戶端程序之間建立雙向通信。套接字綁定到端口號(hào),以便 TCP 層可以識(shí)別數(shù)據(jù)要發(fā)送到的應(yīng)用程序。應(yīng)用程序軟件定義了一個(gè)套接字,以便它利用底層計(jì)算機(jī)中的端口來(lái)實(shí)現(xiàn)它。這使程序員能夠在其應(yīng)用程序代碼中輕松處理網(wǎng)絡(luò)通信的低級(jí)細(xì)節(jié),例如端口、路由等。
客戶端和服務(wù)器之間的 TCP 套接字通信由幾個(gè)階段組成。
在開(kāi)始研究文件傳輸之前,讓我們看看如何在 Java 中實(shí)現(xiàn)套接字通信。在這里,我們將編寫兩個(gè) Java 程序。一個(gè)是在服務(wù)器上運(yùn)行的程序,另一個(gè)是將與服務(wù)器通信的客戶端程序。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static DataOutputStream dataOutputStream = null;
private static DataInputStream dataInputStream = null;
public static void main(String[] args) {
try(ServerSocket serverSocket = new ServerSocket(5000)){
System.out.println("listening to port:5000");
Socket clientSocket = serverSocket.accept();
System.out.println(clientSocket+" connected\n");
dataInputStream = new DataInputStream(clientSocket.getInputStream());
dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
String message;
while (true) {
message = dataInputStream.readUTF();
System.out.println(message);
if(message.equalsIgnoreCase("exit()"))
break;
}
clientSocket.close();
} catch (Exception e){
System.out.println(e.toString());
}
}
}
在這里,ServerSocket 在本地機(jī)器的 5000 端口打開(kāi)一個(gè)套接字并等待客戶端,server.accept()接受傳入的客戶端連接。
連接客戶端后,將實(shí)例化輸出和輸入流,可用于使用流提供的方法之一與連接的客戶端進(jìn)行read通信write。
在上面的程序中,服務(wù)器循環(huán)并接受來(lái)自客戶端的字符串輸入,dataInputStream.readUTF()然后將其打印在控制臺(tái)上,并在接收到的輸入為exit().
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private static DataOutputStream dataOutputStream = null;
private static DataInputStream dataInputStream = null;
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
try(Socket socket = new Socket("localhost",5000)){
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream = new DataOutputStream(socket.getOutputStream());
while (true) {
System.out.print("input> ");
String message = scanner.nextLine();
dataOutputStream.writeUTF(message);
if(message.equalsIgnoreCase("exit()"))
break;
}
}catch (Exception e){
System.out.println(e.toString());
}
}
}
在這里,Socket 將客戶端應(yīng)用程序連接到在 localhost:5000 偵聽(tīng)的上述服務(wù)器,在連接被接受后輸出和輸入流被實(shí)例化。
該程序接受來(lái)自控制臺(tái)的字符串輸入,然后使用 將其發(fā)送到服務(wù)器,dataOutputStream.writeUTF()并在掃描的輸入匹配時(shí)退出循環(huán)exit()。
運(yùn)行程序:
首先,運(yùn)行Server.java程序,然后運(yùn)行Client.java程序。您將在服務(wù)器端看到連接已接受消息,客戶端將接受輸入??蛻舳随I入的輸入將在服務(wù)器端回顯,以exit()在打破while(true)循環(huán)的客戶端應(yīng)用程序中退出類型。
如果你做到了這一步,你就已經(jīng)獲得了足夠的知識(shí)來(lái)完成任務(wù)。
文件傳輸會(huì)像這樣簡(jiǎn)單:
void sendFile(String filePath){
byte[] buffer = new byte[Integer.MAX_VALUE];
fileInputStream = new FileInputStream(文件路徑);
int bytes = fileInputStream.read(buffer,0,buffer.length);
dataOutputStream.write(buffer,0,bytes);
}
void receiveFile(String newFileName){
byte[] buffer = new byte[Integer.MAX_VALUE];
int bytes = dataInputStream.read(buffer,0,buffer.length);
fileOutputStream = new FileOutputStream(newFileName);
fileOutputStream.write(buffer,0,bytes);
}
但是這段代碼有兩個(gè)主要問(wèn)題。
文件大?。?/strong>
有人可能會(huì)說(shuō),如果文件大小大于Integer.MAX_VALUE字節(jié),即 2GB,如果是這種情況,這就足夠了。
真正的問(wèn)題是套接字一次只允許 65482 字節(jié),即 64KB,這比上傳到上的大多數(shù)文檔文件大小要小得多網(wǎng)。
多個(gè)文件:
即使文件小于 64KB,此代碼的另一個(gè)問(wèn)題是在發(fā)送多個(gè)文件并檢測(cè) EOF 時(shí),發(fā)送方不會(huì)有任何問(wèn)題,因?yàn)閒ileInputStream.read()它隱式地用于打開(kāi)的文件(因?yàn)?EOF 導(dǎo)致流結(jié)束本身)。
但是在從多個(gè)文件接收數(shù)據(jù)的接收端,dataInputStream一個(gè)接一個(gè)地排隊(duì),并且沒(méi)有任何方法可以分別檢測(cè)每個(gè)文件的 EOF,因此,它將讀取排隊(duì)的整個(gè)字節(jié)作為一個(gè)文件。fileOutputStream.write(buffer)當(dāng)檢測(cè)到第一個(gè)文件的 EOF 并且剩余文件丟失時(shí),使用寫入停止寫入此字節(jié)緩沖區(qū)。
因此,上述代碼僅適用于大小小于 64KB 的單個(gè)文件。
文件大?。哼@個(gè)問(wèn)題的一個(gè)明顯解決方案是將文件分成多個(gè)塊,比如 4KB,然后在循環(huán)語(yǔ)句中通過(guò)套接字發(fā)送它們,并以類似的方式接收,即wile(stream.read(buffer)!=-1){ ... }
多個(gè)文件:這個(gè)問(wèn)題的主要原因是我們不知道接收到的字節(jié)中每個(gè)文件的 EOF dataInputStream,這可以通過(guò)在發(fā)送每個(gè)文件之前發(fā)送文件大小(以字節(jié)為單位)來(lái)解決,因此我們可以從dataInputStream之后中斷讀取文件大小并對(duì)字節(jié)緩沖區(qū)中排隊(duì)的剩余文件重復(fù)相同的操作。
import java.io.*;
import java.net.Socket;
public class Client {
private static DataOutputStream dataOutputStream = null;
private static DataInputStream dataInputStream = null;
public static void main(String[] args) {
try(Socket socket = new Socket("localhost",5000)) {
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream = new DataOutputStream(socket.getOutputStream());
sendFile("path/to/file1.pdf");
sendFile("path/to/file2.pdf");
dataInputStream.close();
dataInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
private static void sendFile(String path) throws Exception{
int bytes = 0;
File file = new File(path);
FileInputStream fileInputStream = new FileInputStream(file);
// send file size
dataOutputStream.writeLong(file.length());
// break file into chunks
byte[] buffer = new byte[4*1024];
while ((bytes=fileInputStream.read(buffer))!=-1){
dataOutputStream.write(buffer,0,bytes);
dataOutputStream.flush();
}
fileInputStream.close();
}
}
文件發(fā)送者
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static DataOutputStream dataOutputStream = null;
private static DataInputStream dataInputStream = null;
public static void main(String[] args) {
try(ServerSocket serverSocket = new ServerSocket(5000)){
System.out.println("listening to port:5000");
Socket clientSocket = serverSocket.accept();
System.out.println(clientSocket+" connected.");
dataInputStream = new DataInputStream(clientSocket.getInputStream());
dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
receiveFile("NewFile1.pdf");
receiveFile("NewFile2.pdf");
dataInputStream.close();
dataOutputStream.close();
clientSocket.close();
} catch (Exception e){
e.printStackTrace();
}
}
private static void receiveFile(String fileName) throws Exception{
int bytes = 0;
FileOutputStream fileOutputStream = new FileOutputStream(fileName);
long size = dataInputStream.readLong(); // read file size
byte[] buffer = new byte[4*1024];
while (size > 0 && (bytes = dataInputStream.read(buffer, 0, (int)Math.min(buffer.length, size))) != -1) {
fileOutputStream.write(buffer,0,bytes);
size -= bytes; // read upto file size
}
fileOutputStream.close();
}
}
文件接收器
相關(guān)閱讀
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743