參考資料:
http://haohaoxuexi.iteye.com/blog/1979837
http://zhidao.baidu.com/link?url=OeOSa0YbOzSbMVPa8sgPXcwtyyHsWB1lPkh1XopETtNK_lVtbd9lL7NH3qlFxjC-4kNUmCkIXgcfRW7KJq9_FK
http://www.cnblogs.com/rond/p/3565113.html
http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html
TCP/IP客戶套接字
Java中有兩種類型的TCP套接字,一種用於服務器,一種用於客戶端。
ServerSocket類型設計成“監聽器”,等待客戶端連接的到來。因此,ServerSocket用於服務器。
Socket類用於客戶端,它被設計成連接服務器套接字並且初始化協議的交換。
圖片來自:http://www.cnblogs.com/rond/p/3565113.html
Socket
Socket可以使客戶程序與服務器程序通信,使用Socket連接服務器的過程包含以下4個基本的步驟
(1)創建Socket
(2)打開連接到Socket的輸入/輸出流
(3)按照一定協議對Socket執行讀寫操作
(4)關閉Socket
Socket()
boolean isConnection=socket.isConnected() && !socket.isClosed(); //判斷當前是否處於連接
半關閉Socket
- 自定義標識符(譬如下面的例子,當受到“bye”字符串的時候,關閉Socket)
- 告知讀取長度(有些自定義協議的,固定前幾個字節表示讀取的長度的)
- 讀完所有數據
- 當Socket調用close的時候關閉的時候,關閉其輸入輸出流
例:
public class TestPort { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub String hostname = "www.baidu.com"; InetAddress addr = InetAddress.getByName(hostname); Socket so = new Socket(addr,80); System.out.println("遠程連接地址:"+so.getInetAddress()); System.out.println("遠程連接端口:"+so.getPort()); System.out.println("本地連接地址:"+so.getLocalAddress()); System.out.println("本地連接端口:"+so.getLocalPort()); InputStream is = so.getInputStream(); /* * 這部分為客戶端操作,需要一台遠程服務器 int c; while((c=is.read())!=-1){ System.out.println("gas"); }*/ is.close(); so.close(); } }
運行結果:
遠程連接地址:www.baidu.com/119.75.218.70 遠程連接端口:80 本地連接地址:/192.168.1.160 本地連接端口:4338
public class GetServerIP { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub InetAddress addr =InetAddress.getByName("localhost"); ServerSocket se = new ServerSocket(10000,10,addr); System.out.println(se.getInetAddress()); System.out.println(se.getLocalPort()); } }
運行結果
localhost/127.0.0.1 10000
附:完整例子
發送端 socket
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class SocketSender { public static void main(String[] args) { Socket socket = null; try { socket = new Socket("127.0.0.1", 27401); // 向服務端發送信息 OutputStream outer = socket.getOutputStream(); byte[] b = "客戶端:向服務端發送文字,\"這是一行測試....\"".getBytes(); outer.write(b); outer.flush(); System.out.println("發送完畢!"); // 接收服務端的返回值 InputStream inner = socket.getInputStream(); int count = 0; while (count == 0) { count = inner.available(); } byte[] recv = new byte[count]; inner.read(recv); String str_recv = new String(recv); System.out.println("客戶端:接收到服務端的文字:" + str_recv); } catch (IOException e) { System.out.println("發送端出現異常"); } finally { if (socket != null) try { socket.close(); } catch (IOException e) { System.out.println("發送端 finally 出現異常"); } } } }
接收端:ServerSocket
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class SocketReceiver { public static void main(String[] args) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(27401); while (true) { Socket socket = serverSocket.accept(); // 接收客戶端的信息 InputStream in = socket.getInputStream(); int count = 0; while (count == 0) { count = in.available(); } byte[] b = new byte[count]; in.read(b); String str = new String(b); System.out.println(str); // 向客戶端發送確認消息 OutputStream outer = socket.getOutputStream(); byte[] b_out = "已經收到,返回消息碼200".getBytes(); outer.write(b_out); outer.flush(); // 關閉socket socket.close(); } } catch (IOException e) { System.out.println("接收端出現異常"); } finally { if (serverSocket != null) try { serverSocket.close(); } catch (IOException e) { System.out.println("接收端 finally 出現異常"); } } } }
接收端:方法2,利用多線程,每一個發送端對應接收端的一個線程
public class SocketReceiverWithThread { public static void main(String[] args) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(27401); while (true) { Socket socket = serverSocket.accept(); new Thread(new Handler(socket)).start(); //socket.close(); 注意 關閉socket不能在這里,而應該寫在線程內,否則可能線程沒結束就關閉了socket } } catch (IOException e) { System.out.println("接收端出現異常"); } finally { if (serverSocket != null) try { serverSocket.close(); } catch (IOException e) { System.out.println("接收端 finally 出現異常"); } } } } class Handler implements Runnable { private Socket socket; public Handler(Socket socket) { this.socket = socket; } public void run() { try { // 接收客戶端的信息 InputStream in = socket.getInputStream(); int count = 0; while (count == 0) { count = in.available(); } byte[] b = new byte[count]; in.read(b); String str = new String(b); System.out.println(str); // 向客戶端發送確認消息 OutputStream outer = socket.getOutputStream(); byte[] b_out = "已經收到,返回消息碼200".getBytes(); outer.write(b_out); outer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 關閉socket try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
附2:使用socket進行文件傳送
客戶端 ClienPart.java

package com.client; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.Socket; public class ClientPart { private Socket socket=null; private String ip ;// 設置成服務器IP private int port ; public ClientPart(String ip,int port) { this.ip=ip; this.port=port; } private boolean createConnection() { try { socket = new Socket(ip, port); System.out.print("連接服務器成功!" + "\n"); return true; } catch (Exception e) { e.printStackTrace(); return false; } } //向服務器發送消息 private void sentMessage(String msg){ if (socket == null) return; Writer writer; try { writer = new OutputStreamWriter(socket.getOutputStream()); writer.write(msg); writer.flush(); //寫完后要記得flush writer.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } private void getFile() { if (socket == null) return; DataInputStream inputStream = null; try { inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream())); } catch (Exception e) { System.out.print("接收消息緩存錯誤\n"); return; } try { //本地保存路徑,文件名會自動從服務器端繼承而來。 String savePath = "F:\\client\\"; int bufferSize = 8192; byte[] buf = new byte[bufferSize]; long len=0; savePath += inputStream.readUTF(); //讀取文件名 DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath)))); len = inputStream.readLong(); //讀取文件長度 System.out.println("文件的長度為:" + len); System.out.println("開始接收文件!"); while (true) { int read = 0; if (inputStream != null) { read = inputStream.read(buf); } if (read == -1) { break; } //客戶端讀出文件 fileOut.write(buf, 0, read); } System.out.println("接收完成,文件存為" + savePath + "\n"); fileOut.close(); socket.close(); } catch (Exception e) { System.out.println("接收消息錯誤" + "\n"); return; } } private void putFile(String filename){ String filePath = filename; File client_file = new File(filePath); //要傳輸的文件路徑 long filelen = client_file.length(); System.out.println("要上傳的文件長度:" + (int)filelen); try{ DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath))); DataOutputStream out_stream = new DataOutputStream(socket.getOutputStream()); out_stream.writeUTF(client_file.getName()); out_stream.flush(); out_stream.writeLong((long) client_file.length()); out_stream.flush(); int bufferSize = 8192; byte[] buf = new byte[bufferSize]; while (true) { int read = 0; if (in_stream != null) { read = in_stream.read(buf); } if (read == -1) { break; } out_stream.write(buf, 0, read); } out_stream.flush(); // 注意關閉socket鏈接,不然客戶端會等待server的數據過來, // 直到socket超時,會導致數據不完整。 in_stream.close(); socket.close(); System.out.println("文件傳輸完成"); }catch(Exception e){ e.printStackTrace(); } } public static void main(String arg[]) { ClientPart client = new ClientPart("127.0.0.1",7005); client.createConnection(); client.sentMessage("read");//向服務器發送信息,說明需要讀取文件 client.createConnection(); client.getFile(); //獲取服務器發送的文件 client.createConnection(); client.sentMessage("write");//向服務器發送信息,說明需要上傳文件 client.createConnection(); client.putFile("F:\\client\\1.txt");//向服務器發送文件 } }
服務端 ServerPart.java

package com.server; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.ServerSocket; import java.net.Socket; public class ServerPart { int port = 7005; public void serverStart() { try { ServerSocket ssocket = new ServerSocket(port); System.out.println("服務器准備完畢!"); while (true) { String msg = checkIO(ssocket); //獲取來自客戶端的請求是讀/寫 if(msg.compareTo("read")==0){ sendFile(ssocket); //向客戶端發送文件 } else if(msg.compareTo("write")==0){ saveFile(ssocket); //存儲來自客戶端的文件 } } } catch (Exception e) { e.printStackTrace(); } } //服務器檢查來自客戶端的請求是讀/寫 public String checkIO(ServerSocket ssocket) throws IOException{ Socket socket=ssocket.accept(); Reader reader = new InputStreamReader(socket.getInputStream()); char chars[] = new char[10]; int len; StringBuilder sb = new StringBuilder(); while ((len=reader.read(chars)) != -1) { sb.append(new String(chars, 0, len)); } reader.close(); socket.close(); return sb.toString(); } public void sendFile(ServerSocket ssocket) throws IOException{ Socket soc = null; // 選擇進行傳輸的文件 String filePath = "F:\\server\\info.bin"; File myfile = new File(filePath); System.out.println("服務器文件長度:" + (int) myfile.length()); soc = ssocket.accept(); System.out.println("建立socket鏈接"); DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath))); DataOutputStream out_stream = new DataOutputStream(soc.getOutputStream()); //將文件名及長度傳給客戶端。這里要真正適用所有平台,例如中文名的處理,還需要加工,具體可以參見Think In Java 4th里有現成的代碼。 out_stream.writeUTF(myfile.getName()); out_stream.flush(); out_stream.writeLong((long) myfile.length()); out_stream.flush(); int bufferSize = 8192; byte[] buf = new byte[bufferSize]; while (true) { int read = 0; if (in_stream != null) { read = in_stream.read(buf); } if (read == -1) { break; } out_stream.write(buf, 0, read); } out_stream.flush(); // 注意關閉socket鏈接哦,不然客戶端會等待server的數據過來, // 直到socket超時,導致數據不完整。 out_stream.close(); in_stream.close(); soc.close(); System.out.println("服務器文件傳輸完成"); } //服務器保存文件的方法 public void saveFile(ServerSocket ssocket) throws IOException{ Socket soc = ssocket.accept(); if (soc == null) return; DataInputStream inputStream = null; try { inputStream = new DataInputStream(new BufferedInputStream(soc.getInputStream())); } catch (Exception e) { System.out.print("服務器接收消息緩存錯誤\n"); return; } try { //服務器文件的保存路徑 String savePath = "f:\\server\\"; int bufferSize = 8192; byte[] buf = new byte[bufferSize]; long len=0; savePath += inputStream.readUTF(); //讀取文件名 DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath)))); len = inputStream.readLong(); //讀取文件長度 //控制台輸出 System.out.println("文件的長度為:" + len); System.out.println("開始接收文件!"); while (true) { int read = 0; if (inputStream != null) { read = inputStream.read(buf); } if (read == -1) { break; } //客戶端讀出文件 fileOut.write(buf, 0, read); } System.out.println("接收完成,文件存為" + savePath + "\n"); fileOut.close(); soc.close(); } catch (Exception e) { System.out.println("接收消息錯誤" + "\n"); e.printStackTrace(); return; } } public static void main(String arg[]) { //啟動服務器 new ServerPart().serverStart(); } }