TCP通訊(單執行緒多執行緒)
TCP通訊
在JDK中提供了兩個類用於實現TCP程式,一個是ServerSocket類,用於表示伺服器端,一個是Socket類,用於表示客戶端。
通訊時,首先建立代表伺服器端的ServerSocket物件,該物件相當於開啟一個服務,並等待客戶端的連線,然後建立代表客戶端的Socket物件向伺服器端發出連線請求,伺服器端響應請求,兩者建立連線開始通訊。
ServerSocket類(伺服器端)
方法
ServerSocket物件負責監聽某臺計算機的某個埠號,在建立ServerSocket物件後,需要繼續呼叫該物件的accept()方法,接收來自客戶端的請求。當執行了accept()方法之後,伺服器端程式會發生阻塞,直到客戶端發出連線請求,accept()方法才會返回一個Scoket物件用於和客戶端實現通訊,程式才能繼續向下執行。
//伺服器物件 ServerSocket server=new ServerSocket(9999); //和客戶端建立連線, Socket socket=server.accept();
Socket類(客戶端)
ServerSocket物件可以實現服務端程式,但只實現伺服器端程式還不能完成通訊,此時還需要一個客戶端程式與之互動,為此JDK提供了一個Socket類,用於實現TCP客戶端程式。
Socket的常用方法
於向伺服器端傳送資料
在Socket類的常用方法中,getInputStream()和getOutStream()方法分別用於獲取輸入流和輸出流。當客戶端和服務端建立連線後,資料是以IO流的形式進行互動的,從而實現通訊。
實現TCP 伺服器程式
* 表示伺服器程式的類 java.net.ServerSocket
* 構造方法:
* ServerSocket(int port)傳遞埠號
* 很重要的事情:必須要獲得客戶端的套接字物件Socket
* Socket accept()
*
* 1,建立伺服器ServerSocket物件(指定伺服器埠號)
* 2,開啟伺服器了,等待客戶端的連線,當客戶端連線後,可以獲取到連線伺服器的客戶端Socket物件
* 3,給客戶端反饋資訊
* 4,關閉流資源
public class TCPServer { public static void main(String[] args) throws IOException {//建立伺服器ServerSocket物件(指定伺服器埠號) ServerSocket server=new ServerSocket(9999); //和客戶端建立連線, 開啟伺服器了,等待客戶端的連線,當客戶端連線後,可以獲取到連線伺服器的客戶端Socket物件 Socket socket=server.accept(); //接收客戶端傳送資料 InputStream in=socket.getInputStream(); byte[] bytes=new byte[1024]; //獲取客戶端傳送的資料 int len=in.read(bytes); //獲取ip String ip=socket.getInetAddress().getHostAddress(); System.out.println("ip地址為:"+ip+"客戶端傳送的資料為"+new String(bytes,0,len)); //回覆客戶端 //獲取位元組輸出流 OutputStream out=socket.getOutputStream(); //向客戶端傳送資料 out.write("收到".getBytes()); //釋放資源 server.close(); } }
實現客戶端伺服器程式
實現TCP 客戶端,連線到伺服器
* 和伺服器實現資料交換
* 實現TCP客戶端程式的類 java.net.Soket
* 構造方法
* Socket(String host,int port) 傳遞伺服器IP和埠號
* 注意:構造方法只要執行,就會和伺服器進行連線,連線失敗,丟擲異常
* OutputStream getOutputStream() 返回套接字的輸出流
* 作用:將資料輸出,輸出到伺服器
* InputStream getInputStream() 返回套接字的輸入流
* 作用:從伺服器端讀取資料
* 客戶端伺服器資料交換,必須使用套接字物件Socket中的獲取的IO流,自己new流,不行
* 1,建立客戶端Socket物件,(指定要連線的伺服器地址與埠號)
* 2,獲取伺服器端的反饋回來的資訊
* 3,關閉流資源
//客戶端TCP public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { //建立客戶端Socket物件,(指定要連線的伺服器地址與埠號) Socket socket=new Socket("127.0.0.1",9999); //獲取位元組輸出流,向客戶端傳送資料 OutputStream out=socket.getOutputStream(); out.write("你好".getBytes()); //接收伺服器端的回覆 //獲取位元組輸入流 InputStream in=socket.getInputStream(); byte[] bytes=new byte[1024]; //把流中的資料儲存到陣列中,並記錄讀取位元組的個數 int len=in.read(bytes); //獲取ip地址 String ip=socket.getInetAddress().getHostAddress(); System.out.println("ip為"+ip+"內容"+new String(bytes,0,len)); socket.close();//釋放資源 } }
檔案上傳
/*
* TCP上傳伺服器
- ServerSocket套接字物件,監聽埠8888
- 方法accept()獲取客戶端的連線物件
- 客戶端連線物件獲取位元組輸入流,讀取客戶端傳送圖片
- 建立File物件,繫結上傳資料夾
判斷資料夾存在,不存在,再建立資料夾
- 建立位元組輸出流,資料目的File物件所在資料夾
- 位元組讀取圖片,位元組流將圖片寫入到目的資料夾中
- 將上傳成功返回客戶端
- 關閉資源
*/
public class TCPServer { public static void main(String[] args) throws IOException { //建立伺服器物件,明確埠號 ServerSocket server=new ServerSocket(9999); //創建於客戶端的連線 Socket socket=server.accept(); //明確資料來源,顯示哪個客戶端Socket連線上了伺服器 InputStream in=socket.getInputStream(); File file=new File("D:\\io0512\\server"); if(!file.exists()){ file.mkdirs(); } String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(99999)+".java"; //明確目的地 FileOutputStream fos=new FileOutputStream(file+File.separator+filename); //開始複製 byte[] bytes=new byte[1024]; int len=0; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回覆客戶端 OutputStream out=socket.getOutputStream(); out.write("上傳成功".getBytes()); server.close();//釋放資源 } }
/*
* 檔案上傳 客戶端
實現步驟:
- Socket套接字連線伺服器
- 通過Socket獲取位元組輸出流,寫圖片
- 使用自己的流物件,讀取圖片資料來源
FileInputStream
- 讀取圖片,使用位元組輸出流,將圖片寫到伺服器
- 通過Socket套接字獲取位元組輸入流
讀取伺服器發回來的上傳成功
6.關閉資源
*
* public void shutdownOutput() 禁用此Socket的輸出流,間接的相當於告知了伺服器資料寫入完畢
*/
//客戶端TCP public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { //建立客戶端Socket物件,(指定要連線的伺服器地址與埠號) Socket socket=new Socket("127.0.0.1",9999); //獲取位元組輸出流,向客戶端傳送資料 OutputStream out=socket.getOutputStream(); out.write("你好".getBytes()); //接收伺服器端的回覆 //獲取位元組輸入流 InputStream in=socket.getInputStream(); byte[] bytes=new byte[1024]; //把流中的資料儲存到陣列中,並記錄讀取位元組的個數 int len=in.read(bytes); //獲取ip地址 String ip=socket.getInetAddress().getHostAddress(); System.out.println("ip為"+ip+"內容"+new String(bytes,0,len)); socket.close();//釋放資源 } }
多執行緒版本
實現伺服器端可以同時接收多個客戶端上傳的檔案。
伺服器端
public class Upload implements Runnable{ private Socket socket; public Upload(Socket socket){ this.socket=socket; } public void run() { //明確資料來源,顯示哪個客戶端Socket連線上了伺服器 InputStream in; try { in = socket.getInputStream(); File file=new File("D:\\io0512\\server"); if(!file.exists()){ file.mkdirs(); } String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(99999)+".jpg"; //明確目的地 FileOutputStream fos=new FileOutputStream(file+File.separator+filename); //開始複製 byte[] bytes=new byte[1024]; int len=0; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回覆客戶端 OutputStream out=socket.getOutputStream(); out.write("上傳成功".getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Demo01 { public static void main(String[] args) throws IOException { //建立伺服器物件 ServerSocket server=new ServerSocket(9999); while(true){ Socket socket=server.accept(); //啟動執行緒,完成與當前客戶端的資料互動過程 new Thread(new Upload(socket)).start();//建立執行緒任務 } } }
客戶端
//客戶端 public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { //建立客戶端物件,明確地址和埠號 Socket socket=new Socket("192.168.1.151",9999); //明確資料來源 FileInputStream fis=new FileInputStream("D:\\背景\\1.jpg"); //明確目的地 OutputStream out=socket.getOutputStream();//伺服器端 //開始複製 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)); socket.close();//釋放資源 } }