Java socket编程详解,TCPUDP实现
用一張圖來認(rèn)識一下TCP和UDP
TCP點對點的傳輸方式,保證了數(shù)據(jù)的可達性;UDP只管發(fā)送數(shù)據(jù),至于服務(wù)端能否接收到數(shù)據(jù),不在它的保證范圍之內(nèi)。
下面,我們進入正題。
一、網(wǎng)絡(luò)架構(gòu)模型
TCP/IP協(xié)議分為分層架構(gòu):物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層。
應(yīng)用層:能被用戶感知到的一層,如瀏覽器的http、https協(xié)議,遠程連接工具的ftp、ftps協(xié)議等。
傳輸層:兩臺計算機之間的交互數(shù)據(jù)傳輸,就在這一層完成,傳輸層為上層協(xié)議提供端到端的可靠和透明的服務(wù)。TCP和UDP就是在這一層,是應(yīng)用層協(xié)議的基礎(chǔ)。進程的端口號,就是在這一層。
網(wǎng)絡(luò)層:也就是IP層,兩臺計算機之間進行通信,先通過IP找到目標(biāo)計算機,再根據(jù)端口號定位到具體的進程,然后進行數(shù)據(jù)傳輸。IP協(xié)議是internet 的基礎(chǔ)。
二、socket編程
socket本質(zhì)是編程語言的API,在Java中就是具體的類,socket 類對TCP/IP和UDP/IP協(xié)議進行了封裝,提供一個可以供程序員做開發(fā)使用的接口。
TCP和UDP的區(qū)別
TCP:
UDP:
TCP 的三次握手
第一次握手:客戶端向服務(wù)端發(fā)送sny包,包括標(biāo)記sny=1和數(shù)據(jù)seq=x,并進入sny_send狀態(tài)。
第二次握手:服務(wù)端接收到客戶端的TCP報文,確認(rèn)客戶端發(fā)送的數(shù)據(jù)seq=x沒有問題,向客戶端返回TCP報文,包括標(biāo)記sny=1,確認(rèn)ack=x+1,數(shù)據(jù)seq=y,并進入sny_recv狀態(tài)。
第三次握手:客戶端接收到服務(wù)端返回的TCP報文,確認(rèn)服務(wù)端發(fā)送的數(shù)據(jù)seq=y沒有問題,向服務(wù)端發(fā)送TCP報文,包括標(biāo)記sny=1,確認(rèn)ack=y+1, seq=x+1,并進入established狀態(tài)。
服務(wù)端接收到客戶端的TCP報文,進入established狀態(tài),雙方可以進行數(shù)據(jù)傳輸。
三、TCP協(xié)議實現(xiàn)
socket實現(xiàn)的TCP協(xié)議客戶端: TcpClient.java
import java.io.*; import java.net.UnknownHostException; import java.util.Scanner; public class TcpClient {private final static String SERVER_IP = "127.0.0.1";private final static int SERVER_PORT = 10888;public static void main(String[] args) throws Exception {TcpClient tcpClient = new TcpClient();Scanner in = new Scanner(System.in);while(true) {String msg = in.next();if("exit".equals(msg)) {break;}tcpClient.startTcpClient(SERVER_IP, SERVER_PORT, msg);}}public void startTcpClient(String ip, int port, String msg) throws Exception {//創(chuàng)建客戶端 socket,并連接服務(wù)端IP:portSocket socket = new Socket(ip, port);//打開socket數(shù)據(jù)輸出流OutputStream outputStream = socket.getOutputStream();PrintWriter printWriter = new PrintWriter(outputStream);//將數(shù)據(jù) 寫入輸出流printWriter.write(msg);//刷新緩沖區(qū),輸出緩沖區(qū)的數(shù)據(jù);如果不刷新,服務(wù)端接收到數(shù)據(jù)printWriter.flush();//關(guān)閉socket客戶端的輸出流//當(dāng)socket的寫操作打開后,會一直阻塞,所以,每次寫完數(shù)據(jù),需要關(guān)閉socket的寫操作socket.shutdownOutput();/************* 客戶端接受服務(wù)器返回的數(shù)據(jù) ***************///打開socket輸入流InputStream inputStream = socket.getInputStream();//字節(jié)流轉(zhuǎn)字符流,并指定編碼InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8");//字符流封裝成緩沖流BufferedReader bufferedReader = new BufferedReader(inputStreamReader);StringBuffer stringBuffer = new StringBuffer();String len = null;while((len = bufferedReader.readLine()) != null) {stringBuffer.append(len);} System.out.println("接收到客戶端返回的數(shù)據(jù):" + stringBuffer.toString());//關(guān)閉流bufferedReader.close();inputStreamReader.close();inputStream.close();printWriter.close();outputStream.close(); } }socket實現(xiàn)的TCP協(xié)議服務(wù)端:TcpServer.java
import java.net.ServerSocket; import java.net.Socket; public class TcpServer {public static void main(String[] args) throws Exception {TcpServer tcpServer = new TcpServer();tcpServer.startServer(10888);}public void startServer(int port) throws Exception {//創(chuàng)建一個socket服務(wù)端,監(jiān)聽port 端口ServerSocket serverSocket = new ServerSocket(port);System.out.println("TCP服務(wù)器啟動,并監(jiān)聽端口:" + port);//創(chuàng)建一個連接客戶端的socketSocket socket = null;//創(chuàng)建一個循環(huán),web項目以監(jiān)聽實現(xiàn)int count = 0;while(true){//監(jiān)聽客戶端,等待客戶端連接socket = serverSocket.accept();System.out.println("第" + count + "個客戶端請求開始處理!");//創(chuàng)建一個線程,專門處理當(dāng)前客戶端的請求,以便繼續(xù)處理其他客戶端請求,避免阻塞TcpServerThread tcpServerThread = new TcpServerThread(socket);Thread thread = new Thread(tcpServerThread);thread.start();count++;}} }服務(wù)端處理客戶端請求的線程: TcpServerThread.java
import java.io.*; import java.net.Socket; public class TcpServerThread implements Runnable {private Socket socket;public TcpServerThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {InputStream inputStream = null;InputStreamReader inputStreamReader = null;BufferedReader bufferedReader = null;PrintWriter printWriter = null;OutputStream outputStream = null;try {//獲取socket 字節(jié)輸入流inputStream = socket.getInputStream();//字節(jié)流轉(zhuǎn)字符流,指定編碼inputStreamReader = new InputStreamReader(inputStream,"utf-8");//字符流封裝成緩沖字符流bufferedReader = new BufferedReader(inputStreamReader);StringBuffer stringBuffer = new StringBuffer();String len = null;while((len = bufferedReader.readLine()) != null){stringBuffer.append(len);}System.out.println("客戶端的地址:" + socket.getInetAddress() + ",客戶端的端口:" + socket.getPort() + "接收到客戶端的數(shù)據(jù)為:" + stringBuffer.toString());/*************** 服務(wù)端返回數(shù)據(jù)給客戶端 ***************/outputStream = socket.getOutputStream();printWriter = new PrintWriter(outputStream);printWriter.write("客戶端你好,我接受到了你的數(shù)據(jù):" + stringBuffer.toString());//刷新緩沖區(qū),輸出緩沖區(qū)的數(shù)據(jù),如果不刷新,客戶端接受不到數(shù)據(jù)printWriter.flush();//當(dāng)socket的寫操作打開后,會一直阻塞,所以,每次寫完數(shù)據(jù),需要關(guān)閉socket的寫操作socket.shutdownOutput();} catch (IOException e) {e.printStackTrace();}finally {try {printWriter.close();outputStream.close();bufferedReader.close();inputStreamReader.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}}} }先啟動服務(wù)端,再啟動客戶端,演示動畫:
四、UDP協(xié)議的實現(xiàn)
UDP客戶端:UdpClient.java
import java.net.*; import java.util.Scanner; public class UdpClient {public final static String SERVER_IP = "127.0.0.1";public final static int SERVER_PORT = 10999;public final static int BYTE_LENGTH = 1024;public static void main(String[] args) throws Exception {UdpClient udpClient = new UdpClient();Scanner in = new Scanner(System.in);while(true) {String msg = in.next();if("exit".equals(msg)) {break;}udpClient.startUdpClient(SERVER_IP, SERVER_PORT, msg);}}public void startUdpClient(String ip, int port, String msg) throws Exception {//創(chuàng)建一個UDP的客戶端DatagramSocket datagramSocket = new DatagramSocket();//發(fā)送給服務(wù)端的數(shù)據(jù)byte[] bytes = msg.getBytes("UTF-8");//封裝數(shù)據(jù)包,包括數(shù)據(jù)、服務(wù)端IP、服務(wù)端端口DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(ip), port);//發(fā)送數(shù)據(jù)datagramSocket.send(datagramPacket);/*********** 接收服務(wù)端發(fā)來的消息, 也可以像服務(wù)端開啟新的線程 ************///用來接受服務(wù)端發(fā)來的數(shù)據(jù)byte[] receiveData = new byte[BYTE_LENGTH];//封裝數(shù)據(jù)報,用來接收數(shù)據(jù)DatagramPacket receiveDataPacket = new DatagramPacket(receiveData, receiveData.length);//接受數(shù)據(jù),阻塞,直到有數(shù)據(jù)發(fā)來datagramSocket.receive(receiveDataPacket);String serverSendData = new String(receiveDataPacket.getData(),0,receiveDataPacket.getLength(),"UTF-8");System.out.println("服務(wù)端返回的數(shù)據(jù):" + serverSendData + ",服務(wù)端IP:" + receiveDataPacket.getAddress() + ",服務(wù)端端口:" + receiveDataPacket.getPort());} }UDP服務(wù)端:UdpServer.java
import java.net.*; public class UdpServer {public final static int SERVER_PORT = 10999;public final static int BYTE_LENGTH = 1024;public static void main(String[] args) throws Exception {UdpServer udpServer = new UdpServer();udpServer.startUdpServer(SERVER_PORT);}public void startUdpServer(int port) throws Exception {// 創(chuàng)建UDP服務(wù)端,監(jiān)聽port端口DatagramSocket datagramSocket = new DatagramSocket(port);//創(chuàng)建一個字節(jié)數(shù)組,用來接受客戶端發(fā)來的數(shù)據(jù)byte[] bytes = new byte[BYTE_LENGTH];//創(chuàng)建數(shù)據(jù)報,用來接收數(shù)據(jù)DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);System.out.println("開啟UDP服務(wù)端,并監(jiān)聽端口:" + port);//創(chuàng)建一個循環(huán),接收和處理客戶端發(fā)送的數(shù)據(jù)while (true){//接收客戶端發(fā)送的數(shù)據(jù),阻塞,直到有客戶端發(fā)送數(shù)據(jù)datagramSocket.receive(datagramPacket);//獲取接收到的數(shù)據(jù)String receiveData = new String(datagramPacket.getData(),0,datagramPacket.getLength(),"UTF-8");//獲取客戶端IPInetAddress clientIP = datagramPacket.getAddress();//獲取客戶端端口號int clientPort = datagramPacket.getPort();System.out.println("接收到客戶端發(fā)送的數(shù)據(jù):" + receiveData + ",客戶端IP:" + clientIP + ",客戶端端口:" + clientPort);//服務(wù)端返回給客戶端的數(shù)據(jù)byte[] sendData = ("客戶端你好!"+System.currentTimeMillis()).getBytes("UTF-8");//將數(shù)據(jù)、客戶端IP、客戶端端口封裝進數(shù)據(jù)報DatagramPacket里面DatagramPacket sendDataPacket = new DatagramPacket(sendData, sendData.length, clientIP, clientPort);//發(fā)送數(shù)據(jù)datagramSocket.send(sendDataPacket);}} }先啟動服務(wù)端,再啟動客戶端,演示動畫:
總結(jié)
以上是生活随笔為你收集整理的Java socket编程详解,TCPUDP实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php xirr,一个傻瓜式的计算方法,
- 下一篇: API是什么意思