日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA 通过 Socket 实现 TCP 编程

發(fā)布時(shí)間:2023/12/20 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA 通过 Socket 实现 TCP 编程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

簡(jiǎn)介

TCP簡(jiǎn)介

TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的可靠的基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC 793定義。在簡(jiǎn)化的計(jì)算機(jī)網(wǎng)絡(luò)OSI模型中,它完成第四層傳輸層所指定的功能,用戶數(shù)據(jù)報(bào)協(xié)議(UDP,下一篇博客會(huì)實(shí)現(xiàn))是同一層內(nèi) 另一個(gè)重要的傳輸協(xié)議。在因特網(wǎng)協(xié)議族(Internet protocol suite)中,TCP層是位于IP層之上,應(yīng)用層之下的中間層。不同主機(jī)的應(yīng)用層之間經(jīng)常需要可靠的、像管道一樣的連接,但是IP層不提供這樣的流機(jī)制,而是提供不可靠的包交換。

應(yīng)用層向TCP層發(fā)送用于網(wǎng)間傳輸?shù)摹⒂?位字節(jié)表示的數(shù)據(jù)流,然后TCP把數(shù)據(jù)流分區(qū)成適當(dāng)長(zhǎng)度的報(bào)文段(通常受該計(jì)算機(jī)連接的網(wǎng)絡(luò)的數(shù)據(jù)鏈路層的最大傳輸單元( MTU)的限制)。之后TCP把結(jié)果包傳給IP層,由它來通過網(wǎng)絡(luò)將包傳送給接收端實(shí)體的TCP層。TCP為了保證不發(fā)生丟包,就給每個(gè)包一個(gè)序號(hào),同時(shí)序號(hào)也保證了傳送到接收端實(shí)體的包的按序接收。然后接收端實(shí)體對(duì)已成功收到的包發(fā)回一個(gè)相應(yīng)的確認(rèn)(ACK);如果發(fā)送端實(shí)體在合理的往返時(shí)延(RTT)內(nèi)未收到確認(rèn),那么對(duì)應(yīng)的數(shù)據(jù)包就被假設(shè)為已丟失將會(huì)被進(jìn)行重傳。TCP用一個(gè)校驗(yàn)和函數(shù)來檢驗(yàn)數(shù)據(jù)是否有錯(cuò)誤;在發(fā)送和接收時(shí)都要計(jì)算校驗(yàn)和。

JAVA Socket簡(jiǎn)介

所謂socket 通常也稱作”套接字“,用于描述IP地址和端口,是一個(gè)通信鏈的句柄。應(yīng)用程序通常通過”套接字”向網(wǎng)絡(luò)發(fā)出請(qǐng)求或者應(yīng)答網(wǎng)絡(luò)請(qǐng)求

以J2SDK-1.3為例,Socket和ServerSocket類庫位于java.net包中。ServerSocket用于服務(wù)器端,Socket是建立網(wǎng)絡(luò)連接時(shí)使用的。在連接成功時(shí),應(yīng)用程序兩端都會(huì)產(chǎn)生一個(gè)Socket實(shí)例,操作這個(gè)實(shí)例,完成所需的會(huì)話。對(duì)于一個(gè)網(wǎng)絡(luò)連接來說,套接字是平等的,并沒有差別,不因?yàn)樵诜?wù)器端或在客戶端而產(chǎn)生不同級(jí)別。不管是Socket還是ServerSocket它們的工作都是通過SocketImpl類及其子類完成的。

重要的Socket API:

java.net.Socket繼承于java.lang.Object,有八個(gè)構(gòu)造器,其方法并不多,下面介紹使用最頻繁的三個(gè)方法,其它方法大家可以見JDK-1.3文檔。

. Accept方法用于產(chǎn)生”阻塞”,直到接受到一個(gè)連接,并且返回一個(gè)客戶端的Socket對(duì)象實(shí)例。”阻塞”是一個(gè)術(shù)語,它使程序運(yùn)行暫時(shí)”停留”在這個(gè)地方,直到一個(gè)會(huì)話產(chǎn)生,然后程序繼續(xù);通常”阻塞”是由循環(huán)產(chǎn)生的。

. getInputStream方法獲得網(wǎng)絡(luò)連接輸入,同時(shí)返回一個(gè)InputStream對(duì)象實(shí)例。
. getOutputStream方法連接的另一端將得到輸入,同時(shí)返回一個(gè)OutputStream對(duì)象實(shí)例。

注意:其中g(shù)etInputStream和getOutputStream方法均會(huì)產(chǎn)生一個(gè)IOException,它必須被捕獲,因?yàn)樗鼈兎祷氐牧鲗?duì)象,通常都會(huì)被另一個(gè)流對(duì)象使用。

SocketImpl介紹

既然不管是Socket還是ServerSocket它們的工作都是通**過SocketImpl類及其子類完成的,那么當(dāng)然要介紹啦。

抽象類 SocketImpl 是實(shí)際實(shí)現(xiàn)套接字的所有類的通用超類。創(chuàng)建客戶端和服務(wù)器套接字都可以使用它。

具體JDK見:
http://www.javaweb.cc/help/JavaAPI1.6/index.html?java/nio/ReadOnlyBufferException.html

由于它是超類具體代碼實(shí)現(xiàn)還是見下面的Socket

TCP 編程

構(gòu)造ServerSocket

具體API見:http://www.javaweb.cc/help/JavaAPI1.6/index.html?java/nio/ReadOnlyBufferException.html

構(gòu)造方法:

ServerSocket() ~創(chuàng)建非綁定服務(wù)器套接字。

ServerSocket(int port) ~創(chuàng)建綁定到特定端口的服務(wù)器套接字。

ServerSocket(int port, int backlog) ~利用指定的 backlog 創(chuàng)建服務(wù)器套接字并將其綁定到指定的本地端口號(hào)。

ServerSocket(int port, int backlog, InetAddress bindAddr) ~使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 地址創(chuàng)建服務(wù)器。

1.1 綁定端口

除了第一個(gè)不帶參數(shù)的構(gòu)造方法以外, 其他構(gòu)造方法都會(huì)使服務(wù)器與特定端口綁定, 該端口有參數(shù) port 指定. 例如, 以下代碼創(chuàng)建了一個(gè)與 80 端口綁定的服務(wù)器:

ServerSocket serverSocket = new ServerSocket(80);
  • 1

如果運(yùn)行時(shí)無法綁定到 80 端口, 以上代碼會(huì)拋出 IOException, 更確切地說, 是拋出 BindException, 它是 IOException 的子類. BindException 一般是由以下原因造成的:

  • 端口已經(jīng)被其他服務(wù)器進(jìn)程占用;
  • 在某些操作系統(tǒng)中, 如果沒有以超級(jí)用戶的身份來運(yùn)行服務(wù)器程序, 那么操作系統(tǒng)不允許服務(wù)器綁定到 1-1023 之間的端口.
  • 如果把參數(shù) port 設(shè)為 0, 表示由操作系統(tǒng)來為服務(wù)器分配一個(gè)任意可用的端口. 有操作系統(tǒng)分配的端口也稱為匿名端口. 對(duì)于多數(shù)服務(wù)器, 會(huì)使用明確的端口, 而不會(huì)使用匿名端口, 因?yàn)?strong>客戶程序需要事先知道服務(wù)器的端口, 才能方便地訪問服務(wù)器.

    1.2 設(shè)定客戶連接請(qǐng)求隊(duì)列的長(zhǎng)度

    當(dāng)服務(wù)器進(jìn)程運(yùn)行時(shí), 可能會(huì)同時(shí)監(jiān)聽到多個(gè)客戶的連接請(qǐng)求. 例如, 每當(dāng)一個(gè)客戶進(jìn)程執(zhí)行以下代碼:

    Socket socket = new Socket("www.javathinker.org", 80);

    就意味著在遠(yuǎn)程 www.javathinker.org 主機(jī)的 80 端口上, 監(jiān)聽到了一個(gè)客戶的連接請(qǐng)求. 管理客戶連接請(qǐng)求的任務(wù)是由操作系統(tǒng)來完成的. 操作系統(tǒng)把這些連接請(qǐng)求存儲(chǔ)在一個(gè)先進(jìn)先出的隊(duì)列中. 許多操作系統(tǒng)限定了隊(duì)列的最大長(zhǎng)度, 一般為 50 . 當(dāng)隊(duì)列中的連接請(qǐng)求達(dá)到了隊(duì)列的最大容量時(shí), 服務(wù)器進(jìn)程所在的主機(jī)會(huì)拒絕新的連接請(qǐng)求. 只有當(dāng)服務(wù)器進(jìn)程通過 ServerSocket 的 accept() 方法從隊(duì)列中取出連接請(qǐng)求, 使隊(duì)列騰出空位時(shí), 隊(duì)列才能繼續(xù)加入新的連接請(qǐng)求.

    對(duì)于客戶進(jìn)程, 如果它發(fā)出的連接請(qǐng)求被加入到服務(wù)器的請(qǐng)求連接隊(duì)列中, 就意味著客戶與服務(wù)器的連接建立成功, 客戶進(jìn)程從 Socket 構(gòu)造方法中正常返回. 如果客戶進(jìn)程發(fā)出的連接請(qǐng)求被服務(wù)器拒絕, Socket 構(gòu)造方法就會(huì)拋出 ConnectionException.

    Tips: 創(chuàng)建綁定端口的服務(wù)器進(jìn)程后, 當(dāng)客戶進(jìn)程的 Socket構(gòu)造方法返回成功, 表示客戶進(jìn)程的連接請(qǐng)求被加入到服務(wù)器進(jìn)程的請(qǐng)求連接隊(duì)列中. 雖然客戶端成功返回 Socket對(duì)象, 但是還沒跟服務(wù)器進(jìn)程形成一條通信線路. 必須在服務(wù)器進(jìn)程通過 ServerSocket 的 accept() 方法從請(qǐng)求連接隊(duì)列中取出連接請(qǐng)求, 并返回一個(gè)Socket 對(duì)象后, 服務(wù)器進(jìn)程這個(gè)Socket 對(duì)象才與客戶端的 Socket 對(duì)象形成一條通信線路.

    ServerSocket 構(gòu)造方法的 backlog 參數(shù)用來顯式設(shè)置連接請(qǐng)求隊(duì)列的長(zhǎng)度, 它將覆蓋操作系統(tǒng)限定的隊(duì)列的最大長(zhǎng)度. 值得注意的是, 在以下幾種情況中, 仍然會(huì)采用操作系統(tǒng)限定的隊(duì)列的最大長(zhǎng)度:

  • backlog 參數(shù)的值大于操作系統(tǒng)限定的隊(duì)列的最大長(zhǎng)度;
  • backlog 參數(shù)的值小于或等于0;
  • 在ServerSocket 構(gòu)造方法中沒有設(shè)置 backlog 參數(shù).

    以下的 Client.java 和 Server.java 用來演示服務(wù)器的連接請(qǐng)求隊(duì)列的特性.
    Client.java

  • 1 import java.net.Socket; 2 public class Client { 3 public static void main(String[] args) throws Exception{ 4 final int length = 100; 5 String host = "localhost"; 6 int port = 1122; 7 Socket[] socket = new Socket[length]; 8 for(int i = 0;i<length;i++){ 9 socket[i] = new Socket(host,port); 10 System.out.println("第"+(i+1)+"次連接成功!"); 11 } 12 Thread.sleep(3000); 13 for(int i=0;i<length;i++){ 14 socket[i].close(); 15 } 16 } 17 }

      Server.java

    1 import java.io.IOException; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 public class Server { 5 private int port = 1122; 6 private ServerSocket serverSocket; 7 8 public Server() throws Exception{ 9 serverSocket = new ServerSocket(port,3); 10 System.out.println("服務(wù)器啟動(dòng)!"); 11 } 12 public void service(){ 13 while(true){ 14 Socket socket = null; 15 try { 16 socket = serverSocket.accept(); 17 System.out.println("New connection accepted "+ 18 socket.getInetAddress()+":"+socket.getPort()); 19 } catch (IOException e) { 20 e.printStackTrace(); 21 }finally{ 22 if(socket!=null){ 23 try { 24 socket.close(); 25 } catch (IOException e) { 26 e.printStackTrace(); 27 } 28 } 29 } 30 } 31 } 32 33 public static void main(String[] args) throws Exception{ 34 Server server = new Server(); 35 Thread.sleep(60000*10); 36 server.service(); 37 } 38 }

    ⑴ 在Server 中只創(chuàng)建一個(gè) ServerSocket 對(duì)象, 在構(gòu)造方法中指定監(jiān)聽的端口為1122 和 連接請(qǐng)求隊(duì)列的長(zhǎng)度為 3 . 構(gòu)造 Server 對(duì)象后, Server 程序睡眠 10 分鐘, 并且在 Server 中不執(zhí)行 serverSocket.accept() 方法. 這意味著隊(duì)列中的連接請(qǐng)求永遠(yuǎn)不會(huì)被取出. 運(yùn)行Server 程序和 Client 程序后, Client程序的打印結(jié)果如下:
    第 1 次連接成功
    第 2 次連接成功
    第 3 次連接成功
    Exception in thread “main” java.net.ConnectException: Connection refused: connect
    …………….
    從以上打印的結(jié)果可以看出, Client 與 Server 在成功地建立了3 個(gè)連接后, 就無法再創(chuàng)建其余的連接了, 因?yàn)榉?wù)器的隊(duì)已經(jīng)滿了.

    ⑵ 在Server中構(gòu)造一個(gè)跟 ⑴ 相同的 ServerSocket對(duì)象, Server程序不睡眠, 在一個(gè) while 循環(huán)中不斷執(zhí)行 serverSocket.accept()方法, 該方法從隊(duì)列中取出連接請(qǐng)求, 使得隊(duì)列能及時(shí)騰出空位, 以容納新的連接請(qǐng)求. Client 程序的打印結(jié)果如下:
    第 1 次連接成功
    第 2 次連接成功
    第 3 次連接成功
    ………..
    第 100 次連接成功
    從以上打印結(jié)果可以看出, 此時(shí) Client 能順利與 Server 建立 100 次連接.(每次while的循環(huán)要夠快才行, 如果太慢, 從隊(duì)列取連接請(qǐng)求的速度比放連接請(qǐng)求的速度慢的話, 不一定都能成功連接)

    1.3 設(shè)定綁定的IP 地址

    如果主機(jī)只有一個(gè)IP 地址, 那么默認(rèn)情況下, 服務(wù)器程序就與該IP 地址綁定. ServerSocket 的第 4 個(gè)構(gòu)造方法 ServerSocket(int port, int backlog, InetAddress bingAddr) 有一個(gè) bindAddr 參數(shù), 它顯式指定服務(wù)器要綁定的IP 地址, 該構(gòu)造方法適用于具有多個(gè)IP 地址的主機(jī). 假定一個(gè)主機(jī)有兩個(gè)網(wǎng)卡, 一個(gè)網(wǎng)卡用于連接到 Internet, IP為 222.67.5.94, 還有一個(gè)網(wǎng)卡用于連接到本地局域網(wǎng), IP 地址為 192.168.3.4. 如果服務(wù)器僅僅被本地局域網(wǎng)中的客戶訪問, 那么可以按如下方式創(chuàng)建 ServerSocket:

    ServerSocket serverSocket = new ServerSocket(8000, 10, InetAddress.getByName(“192.168.3.4”));

    1.4 默認(rèn)構(gòu)造方法的作用

    ServerSocket 有一個(gè)不帶參數(shù)的默認(rèn)構(gòu)造方法. 通過該方法創(chuàng)建的 ServerSocket 不與任何端口綁定, 接下來還需要通過 bind() 方法與特定端口綁定.

    這個(gè)默認(rèn)構(gòu)造方法的用途是, 允許服務(wù)器在綁定到特定端口之前, 先設(shè)置ServerSocket 的一些選項(xiàng). 因?yàn)橐坏┓?wù)器與特定端口綁定, 有些選項(xiàng)就不能再改變了.比如:SO_REUSEADDR 選項(xiàng)

    在以下代碼中, 先把 ServerSocket 的 SO_REUSEADDR 選項(xiàng)設(shè)為 true, 然后再把它與 8000 端口綁定: ServerSocket serverSocket = new ServerSocket();serverSocket.setReuseAddress(true); //設(shè)置 ServerSocket 的選項(xiàng)serverSocket.bind(new InetSocketAddress(8000)); //與8000端口綁定

    如果把以上程序代碼改為:

    ServerSocket serverSocket = new ServerSocket(8000);serverSocket.setReuseAddress(true);//設(shè)置 ServerSocket 的選項(xiàng)

    那么 serverSocket.setReuseAddress(true) 方法就不起任何作用了, 因?yàn)?SO_REUSEADDR 選項(xiàng)必須在服務(wù)器綁定端口之前設(shè)置才有效.

    多線程示例

    客戶端:

    1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.OutputStream; 6 import java.io.PrintWriter; 7 import java.net.Socket; 8 import java.net.UnknownHostException; 9 10 /* 11 * 客戶端 12 */ 13 public class Client { 14 public static void main(String[] args) { 15 try { 16 //1.創(chuàng)建客戶端Socket,指定服務(wù)器地址和端口 17 Socket socket=new Socket("localhost", 8888); 18 //2.獲取輸出流,向服務(wù)器端發(fā)送信息 19 OutputStream os=socket.getOutputStream();//字節(jié)輸出流 20 PrintWriter pw=new PrintWriter(os);//將輸出流包裝為打印流 21 pw.write("用戶名:whf;密碼:789"); 22 pw.flush(); 23 socket.shutdownOutput();//關(guān)閉輸出流 24 //3.獲取輸入流,并讀取服務(wù)器端的響應(yīng)信息 25 InputStream is=socket.getInputStream(); 26 BufferedReader br=new BufferedReader(new InputStreamReader(is)); 27 String info=null; 28 while((info=br.readLine())!=null){ 29 System.out.println("我是客戶端,服務(wù)器說:"+info); 30 } 31 //4.關(guān)閉資源 32 br.close(); 33 is.close(); 34 pw.close(); 35 os.close(); 36 socket.close(); 37 } catch (UnknownHostException e) { 38 e.printStackTrace(); 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 } 43 }

    服務(wù)器:

    1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.OutputStream; 6 import java.io.PrintWriter; 7 import java.net.InetAddress; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 11 /* 12 * 基于TCP協(xié)議的Socket通信,實(shí)現(xiàn)用戶登陸 13 * 服務(wù)器端 14 */ 15 public class Server { 16 public static void main(String[] args) { 17 try { 18 //1.創(chuàng)建一個(gè)服務(wù)器端Socket,即ServerSocket,指定綁定的端口,并監(jiān)聽此端口 19 ServerSocket serverSocket=new ServerSocket(8888); 20 Socket socket=null; 21 //記錄客戶端的數(shù)量 22 int count=0; 23 System.out.println("***服務(wù)器即將啟動(dòng),等待客戶端的連接***"); 24 //循環(huán)監(jiān)聽等待客戶端的連接 25 while(true){ 26 //調(diào)用accept()方法開始監(jiān)聽,等待客戶端的連接 27 socket=serverSocket.accept(); 28 //創(chuàng)建一個(gè)新的線程 29 ServerThread serverThread=new ServerThread(socket); 30 //啟動(dòng)線程 31 serverThread.start(); 32 33 count++;//統(tǒng)計(jì)客戶端的數(shù)量 34 System.out.println("客戶端的數(shù)量:"+count); 35 InetAddress address=socket.getInetAddress(); 36 System.out.println("當(dāng)前客戶端的IP:"+address.getHostAddress()); 37 } 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 } 42 }

    服務(wù)器處理類:

    1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.OutputStream; 6 import java.io.PrintWriter; 7 import java.net.Socket; 8 9 /* 10 * 服務(wù)器線程處理類 11 */ 12 public class ServerThread extends Thread { 13 // 和本線程相關(guān)的Socket 14 Socket socket = null; 15 16 public ServerThread(Socket socket) { 17 this.socket = socket; 18 } 19 20 //線程執(zhí)行的操作,響應(yīng)客戶端的請(qǐng)求 21 public void run(){ 22 InputStream is=null; 23 InputStreamReader isr=null; 24 BufferedReader br=null; 25 OutputStream os=null; 26 PrintWriter pw=null; 27 try { 28 //獲取輸入流,并讀取客戶端信息 29 is = socket.getInputStream(); 30 isr = new InputStreamReader(is); 31 br = new BufferedReader(isr); 32 String info=null; 33 while((info=br.readLine())!=null){//循環(huán)讀取客戶端的信息 34 System.out.println("我是服務(wù)器,客戶端說:"+info); 35 } 36 socket.shutdownInput();//關(guān)閉輸入流 37 //獲取輸出流,響應(yīng)客戶端的請(qǐng)求 38 os = socket.getOutputStream(); 39 pw = new PrintWriter(os); 40 pw.write("歡迎您!"); 41 pw.flush();//調(diào)用flush()方法將緩沖輸出 42 } catch (IOException e) { 43 // TODO Auto-generated catch block 44 e.printStackTrace(); 45 }finally{ 46 //關(guān)閉資源 47 try { 48 if(pw!=null) 49 pw.close(); 50 if(os!=null) 51 os.close(); 52 if(br!=null) 53 br.close(); 54 if(isr!=null) 55 isr.close(); 56 if(is!=null) 57 is.close(); 58 if(socket!=null) 59 socket.close(); 60 } catch (IOException e) { 61 e.printStackTrace(); 62 } 63 } 64 } 65 }

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/alimjan/p/7708892.html

    總結(jié)

    以上是生活随笔為你收集整理的JAVA 通过 Socket 实现 TCP 编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。