上一篇寫到使用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內容太過繁雜,這里就不一一說了
這里涉及到了多線程請見下回分解
