上一篇寫到使用UDP實現文字交互,UDP和TCP各有千秋,所以現在寫一個TCP上傳圖片的demo。
TCP通信同UDP通信區別在於,UDP中只有發送端和接收端,不區分客戶端與服務器端,計算機之間可以任意地發送數據。而TCP通信是嚴格區分客戶端與服務器端的,在通信時,必須先由客戶端去連接服務器端才能實現通信,服務器端不可以主動連接客戶端,並且服務器端程序需要事先啟動,等待客戶端的連接。
在開發TCP程序時,首先需要創建服務器端程序,JDK的java.net包中提供了一個ServerSocket類。
使用該構造方法在創建ServerSocket對象時,就可以將其綁定到一個指定的端口號上(參數port就是端口號)。
ServerSocket對象負責監聽某台計算機的某個端口號,在創建ServerSocket對象后,需要繼續調用該對象的accept()方法,接收來自客戶端的請求。當執行了accept()方法之后,服務器端程序會發生阻塞,直到客戶端發出連接請求,accept()方法才會返回一個Scoket對象用於和客戶端實現通信,程序才能繼續向下執行。
ServerSocket對象可以實現服務端程序,但只實現服務器端程序還不能完成通信,此時還需要一個客戶端程序與之交互,為此JDK提供了一個Socket類,用於實現TCP客戶端程序。
參數address用於接收一個InetAddress類型的對象,該對象用於封裝一個IP地址。
Socket的常用方法,如圖所示。
在Socket類的常用方法中,getInputStream()和getOutStream()方法分別用於獲取輸入流和輸出流。當客戶端和服務端建立連接后,數據是以IO流的形式進行交互的,從而實現通信。
下面上代碼
客戶端
// 創建Socket對象,連接服務器Socket socket = new Socket("127.0.0.1",8000);
這段代碼只要寫TCP客戶端必定出現
/*
* 實現TCP客戶端,連接到服務器
* 和服務器實現數據交換
* 實現TCP客戶端程序的類java.net.Socket
*
* 構造方法:
* Socket(String host,int port) 傳遞服務器IP和端口號
* 注意:構造方法只要運行,就會和服務器進行連接,連接失敗,拋出異常
* OutputStream getOutputStream() 返回套接字的輸出流
* 作用:將數據輸出,輸出到服務器
* InputStream getInputStream() 返回套接字的輸入流
* 作用:從服務器端讀取數據
* 客戶端服務器數據交換,必須使用套接字對象Socket中的獲取IO流,自己new流,不行
*/
package com.lty.wangluotcpPhotoUp; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; /* * 實現TCP圖片上傳客戶端 * 實現步驟 * 1.Socket套接字連接服務器 * 2.通過Socket獲取字節輸出流,寫圖片 * 3.使用自己的流對象,讀取圖片數據源 * FileInputStream * 4.讀取圖片使用字節輸出流,將圖片寫到服務器 * 采用字節數組進行緩沖 * 5.通過Socket套接字獲取字節輸入流 * 讀取服務器發回來的上傳成功 * 6.關閉資源 */ public class TCPClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8000); //獲取字節輸出流,圖片寫到服務器 OutputStream out = socket.getOutputStream(); // 創建字節輸入流,讀取本機上的數據源圖片 FileInputStream fis = new FileInputStream("c:\\t.jpg"); //開始讀寫字節數組 int len = 0; byte[] bytes =new byte[1024]; while((len = fis.read(bytes))!=-1) { out.write(bytes,0,len); } //給服務器寫終止序列 socket.shutdownOutput(); //獲取字節輸入流,讀取服務器的上傳成功 InputStream in = socket.getInputStream(); len = in.read(bytes); System.out.println(new String(bytes,0,len)); fis.close(); socket.close(); } }
服務器:
這個demo是多線程的,所以有如下代碼
package com.lty.wangluotcpPhotoUp; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.Socket; import java.util.Random; public class Upload implements Runnable{ private Socket socket; public Upload(Socket socket) {this.socket=socket;} public void run() { //通過客戶端連接對象,獲取字節輸入流,獲取客戶端圖片 try { InputStream in = socket.getInputStream(); //將目的文件夾封裝到File對象 File upload = new File("d:\\upload"); if(!upload.exists()) upload.mkdirs(); //防止文件同名被覆蓋,重新定義文件名字 //規則: 域名+毫秒值+6位隨機數 String filename="itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg"; //創建字節輸出流,將圖片寫入到目的文件夾中 FileOutputStream fos = new FileOutputStream(upload+File.separator+filename); //讀寫字節數組 byte[] bytes=new byte[1024]; int len=0; while ((len=in.read(bytes))!=-1) { fos.write(bytes,0,len); } //通過客戶端連接對象獲取字節輸出流 //上次成功寫回到客戶端 socket.getOutputStream().write("上傳成功".getBytes()); fos.close(); socket.close(); } catch (Exception ex) { } } }
寫服務器端下面兩端代碼是肯定會出現
ServerSocket server = new ServerSocket(8000);
//調用服務器套接字對象中的方法accept()獲取套接字對象
Socket socket =server.accept();
/*
* 實現TCP服務程序
* 表示服務器程序的類java.net.ServerSocket
* 構造方法:
* SreverSoceket(int port)傳遞端口號
*
* 很重要的事情:必須要獲得客戶端的套接字對象Socket
* Socket accept()
* 服務器可以獲取到客戶端的套接字對象Socket
*
*/
package com.lty.wangluotcpPhotoUp; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class TCPThreadServer { public static void main(String[] args)throws IOException { ServerSocket server = new ServerSocket(8000); while(true) { //獲取一個客戶端,必須開啟新線程 Socket socket=server.accept(); new Thread(new Upload(socket)).start(); } } }
效果圖
網絡編程與IO密不可分,但是IO內容太過繁雜,這里就不一一說了
這里涉及到了多線程請見下回分解