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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

Socket网络编程——(一)

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

什么是Socket

網(wǎng)絡(luò)上的兩個(gè)程序通過(guò)一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)連接的一端稱為一個(gè)socket。端點(diǎn)由IP地址和端口號(hào)共同組成,簡(jiǎn)單的說(shuō)它是IP地址和端口結(jié)合的協(xié)議。

常用的套接字
流式套接字:流套接字用于提供面向連接、可靠的數(shù)據(jù)傳輸服務(wù),該服務(wù)將保證數(shù)據(jù)能夠?qū)崿F(xiàn)無(wú)差錯(cuò)、無(wú)重復(fù)發(fā)送,并按順序接收(基于TCP)。
數(shù)據(jù)報(bào)套接字:數(shù)據(jù)報(bào)套接字提供了一種無(wú)連接的服務(wù)。該服務(wù)并不能保證數(shù)據(jù)傳輸?shù)目煽啃?#xff0c;數(shù)據(jù)有可能在傳輸過(guò)程中丟失或出現(xiàn)數(shù)據(jù)重復(fù),且無(wú)法保證順序地接收到數(shù)據(jù)(基于UDP)。

簡(jiǎn)單的介紹下TCP和UDP。TCP是面向連接的,保證可靠的數(shù)據(jù)傳輸,每次建立連接都需要經(jīng)歷三次握手,數(shù)據(jù)傳輸完成都需要經(jīng)歷4次揮手?jǐn)嚅_連接,由于TCP是面向連接的所以只能用于端到端的通信。而UDP是面向無(wú)連接的通訊協(xié)議,每次發(fā)送數(shù)據(jù)不需要建立連接,因此可以用于廣播發(fā)送并不局限于端到端。

本篇主要學(xué)習(xí)ServerSocket和Socket的用法,它們分別對(duì)應(yīng)了服務(wù)端的套接字和客戶端的套接字,都是基于TCP協(xié)議封裝的。

ServerSocket構(gòu)造方法

ServerSocket()創(chuàng)建一個(gè)未綁定到任何端口的服務(wù)器套接字
ServerSocket(int port)創(chuàng)建一個(gè)綁定到指定端口的服務(wù)器套接字。
ServerSocket(int port, int backlog)創(chuàng)建一個(gè)綁定到指定端口的服務(wù)器套接字,backlog是客戶端連接請(qǐng)求的隊(duì)列長(zhǎng)度。
ServerSocket(int port, int backlog, InetAddress bindAddr)創(chuàng)建一個(gè)綁定到指定端口、具有指定大小的請(qǐng)求隊(duì)列、綁定到本地IP地址的服務(wù)器套接字。

簡(jiǎn)單的說(shuō)明一下,第一個(gè)構(gòu)造創(chuàng)建了一個(gè)未綁定任何端口的套接字,需要我們調(diào)用bind方法手動(dòng)綁定。第2、3構(gòu)造方法在創(chuàng)建的時(shí)候就指定了要綁定的端口。第三個(gè)構(gòu)造方法在創(chuàng)建的時(shí)候就指定了要綁定的端口和本地IP,如果不指定IP會(huì)自動(dòng)分配一個(gè)可用的本機(jī)IP。
需要特別理解backlog這個(gè)參數(shù)
backlog用來(lái)設(shè)置客戶端連接請(qǐng)求隊(duì)列的長(zhǎng)度,客戶端的連接請(qǐng)求是由操作系統(tǒng)負(fù)責(zé)管理的,操作系統(tǒng)會(huì)把連接請(qǐng)求放入到一個(gè)先進(jìn)先出的隊(duì)列里,通常操作系統(tǒng)會(huì)規(guī)定隊(duì)列的最大長(zhǎng)度為50。當(dāng)隊(duì)列到達(dá)上限50后再有其他請(qǐng)求到來(lái)就會(huì)被拒絕掉。只有當(dāng)服務(wù)器執(zhí)行serverSocket.accept(),把請(qǐng)求取出后,隊(duì)列才能騰出空位加入新的請(qǐng)求。如果連接請(qǐng)求被拒絕,則會(huì)拋出ConnectionException。

ServerSocket相關(guān)API

bind(SocketAddress endpoint):綁定到特定的地址(IP地址和端口)。

accept() : 監(jiān)聽,當(dāng)有連接請(qǐng)求到來(lái)就接收它返回一個(gè)新的Socket來(lái)和客戶端通信,這個(gè)方法會(huì)阻塞直到連接請(qǐng)求到來(lái)

setSoTimeout(int timeout):設(shè)置連接超時(shí),當(dāng)設(shè)置了該選項(xiàng),accept()方法在該時(shí)間段沒(méi)有連接請(qǐng)求到來(lái)會(huì)拋出異常。

setReuseAddress(boolean on):
設(shè)置端口重用。當(dāng)ServerSocket關(guān)閉時(shí),如果網(wǎng)絡(luò)上還有發(fā)送到該ServerSocket的數(shù)據(jù),這個(gè)ServerSocket不會(huì)立即釋放本地端口,而是等待一段時(shí)間,以確保接收到了網(wǎng)絡(luò)上發(fā)送過(guò)來(lái)的延遲數(shù)據(jù),然后再釋放端口。為了確保一個(gè)進(jìn)程關(guān)閉了ServerSocket 后,即使操作系統(tǒng)還沒(méi)釋放端口,同一個(gè)主機(jī)上的其他進(jìn)程任然可以立刻重用該端口,可以調(diào)用ServerSocket .setResuseAddress(true)方法。需要注意兩點(diǎn):1、該方法必須在綁定到指定端口之前調(diào)用,否則無(wú)效。2、綁定到該公用端口的其他套接字也需要調(diào)用setReuseAddress方法。

setReceiveBufferSize (int size):
為所有accept方法返回的socket對(duì)象設(shè)置接收緩存區(qū)大小,單位為字節(jié),默認(rèn)大小與操作系統(tǒng)有關(guān)。一般說(shuō)來(lái),傳輸大的連續(xù)的數(shù)據(jù)塊(基于HTTP或FTP協(xié)議的數(shù)據(jù)傳輸)可以使用較大的緩沖區(qū),這可以減少傳輸數(shù)據(jù)的次數(shù),從而提高傳輸數(shù)據(jù)的效率。而對(duì)于交互式的通信如Telent或者希望即時(shí)獲取數(shù)據(jù)如游戲,則應(yīng)該采用小的緩沖區(qū),確保能及時(shí)把小批量的數(shù)據(jù)發(fā)送給對(duì)方。

setPerformancePreferences(int connectionTime,int latency,int bandwidth):
設(shè)置服務(wù)器套接字的性能首選項(xiàng)。用來(lái)設(shè)置短連接時(shí)間、低延遲和高帶寬的相對(duì)重要性。

Socket構(gòu)造方法

Socket()創(chuàng)建一個(gè)未連接的套接字
Socket(Proxy proxy)創(chuàng)建一個(gè)未連接的套接字,該套接字使用特定的代理 。
Socket(SocketImpl impl)使用用戶指定的socketimpl創(chuàng)建未連接的套接字。
Socket(String host, int port)創(chuàng)建流套接字并將其連接到指定主機(jī)上的指定端口號(hào)。
Socket(InetAddress address, int port)創(chuàng)建流套接字并將其連接到指定IP地址處的指定端口號(hào)。
Socket(String host, int port, InetAddress localAddr, int localPort)創(chuàng)建套接字并將其連接到指定遠(yuǎn)程主機(jī)上的指定端口。套接字還將綁定到指定的本地地址和端口。

構(gòu)造方法還有其他,用法都類似,需要注意的是創(chuàng)建未連接的套接字需要我們自己手都調(diào)用socket.connect()方法進(jìn)行連接,而如果調(diào)用那些創(chuàng)建時(shí)就進(jìn)行連接的構(gòu)造不需要再調(diào)用connect()方法進(jìn)行連接。通常都是創(chuàng)建未連接的套接字,因?yàn)槲覀兺枰谶B接之前對(duì)它進(jìn)行一些初始化的配置。

Socket相關(guān)API

很多api的用法和ServerSocket的一致,只說(shuō)幾個(gè)特殊的。
setSoTimeout(int timeout):設(shè)置讀超時(shí),當(dāng)此選項(xiàng)設(shè)置為非零時(shí)間,對(duì)此套接字關(guān)聯(lián)的輸入流的read()調(diào)用將僅阻塞此時(shí)間量,超時(shí)將拋出SocketTimeoutException: Read timed out。

setKeepAlive(boolean on)
當(dāng)設(shè)置為true的時(shí)候,底層的TCP實(shí)現(xiàn)會(huì)監(jiān)視該連接是否有效。當(dāng)通信的兩端套接字兩個(gè)小時(shí)內(nèi)沒(méi)有數(shù)據(jù)交換(注意:實(shí)際值取決于實(shí)現(xiàn)),TCP實(shí)現(xiàn)會(huì)自動(dòng)向遠(yuǎn)程套接字發(fā)送keepalive探測(cè)包,如果遠(yuǎn)程套接字響應(yīng)了,說(shuō)明連接正常。如果遠(yuǎn)程套接字沒(méi)響應(yīng),TCP實(shí)現(xiàn)會(huì)繼續(xù)探測(cè),一定時(shí)間后依舊沒(méi)響應(yīng)代表遠(yuǎn)程服務(wù)器可能崩潰,TCP嘗試關(guān)閉socket連接。默認(rèn)值為 false, 表示TCP 不會(huì)監(jiān)視連接是否有效, 不活動(dòng)的客戶端套接字可能會(huì)永遠(yuǎn)存在下去, 而不會(huì)注意到服務(wù)器已經(jīng)關(guān)閉??赡艿?種情況
1、遠(yuǎn)程套接字以預(yù)期的ACK響應(yīng)。TCP將在另外2小時(shí)不活動(dòng)后發(fā)送另一個(gè)探測(cè)器。
2、遠(yuǎn)程套接字以RST響應(yīng),該RST告訴本地TCP對(duì)等主機(jī)已崩潰并重新啟動(dòng)。套接字已關(guān)閉。
3、對(duì)等端沒(méi)有響應(yīng)。套接字已關(guān)閉。

setOOBInline(boolean on)
默認(rèn)情況下,此選項(xiàng)被禁用,在套接字上接收的TCP緊急數(shù)據(jù)將被靜默丟棄。如果用戶希望接收緊急數(shù)據(jù),則必須啟用此選項(xiàng)。啟用后,緊急數(shù)據(jù)與正常數(shù)據(jù)一起接收。緊急數(shù)據(jù)是通過(guò)sendUrgentData發(fā)出的一個(gè)單字節(jié)數(shù)據(jù),它不經(jīng)過(guò)發(fā)送緩沖區(qū)而是直接發(fā)送出去的,需要注意的是對(duì)于服務(wù)器端緊急數(shù)據(jù)和普通數(shù)據(jù)是混在一起的,服務(wù)器并不知道客戶端發(fā)送到數(shù)據(jù)是通過(guò)sendUrgentData發(fā)出的,還是通過(guò)OutputStream發(fā)出的。

setSendBufferSize(int size)
設(shè)置發(fā)送緩沖區(qū)大小(size>0),最好不要將發(fā)送緩沖區(qū)設(shè)得太小,太小會(huì)導(dǎo)致傳輸數(shù)據(jù)過(guò)于頻繁,從而降低網(wǎng)絡(luò)傳輸?shù)男省?/p>

setTcpNoDelay(boolean on)
這個(gè)方法的作用是開啟或關(guān)閉Nagle算法,默認(rèn)Nagle算法是開啟的,通過(guò)setTcpNoDelay(true)可以關(guān)閉該算法。它有兩個(gè)作用:a、Nagle算法要求一個(gè)TCP連接上最多只能有一個(gè)未被確認(rèn)的小分組,在該小分組的確認(rèn)到來(lái)之前,不能發(fā)送其他小分組。也就是說(shuō)發(fā)送方發(fā)送了一個(gè)分組后,只有等到接收方回復(fù)的ack控制段才發(fā)送下一個(gè)分組;2、如果啟用該算法數(shù)據(jù)只有在寫緩存中累積到一定量之后,才會(huì)被發(fā)送出去,這樣通過(guò)減少數(shù)據(jù)傳輸次數(shù)來(lái)提高了網(wǎng)絡(luò)利用率。如果關(guān)閉該算法,在前一個(gè)分組的確認(rèn)沒(méi)到來(lái)時(shí)依舊可以發(fā)送下一個(gè)分組并且客戶端每發(fā)送一次數(shù)據(jù),無(wú)論數(shù)據(jù)包的大小都會(huì)將這些數(shù)據(jù)發(fā)送出去。

setSoLinger(boolean on, int linger):
這個(gè)設(shè)置僅影響套接字的關(guān)閉。
默認(rèn)為false,當(dāng)執(zhí)行socket的close()方法會(huì)立即返回,如果此時(shí)還有數(shù)據(jù)沒(méi)發(fā)送完將由底層系統(tǒng)來(lái)接管輸出流,嘗試將緩沖區(qū)數(shù)據(jù)發(fā)送出去。
如果設(shè)置為setSoLinger(true, 0)調(diào)用socket.close()關(guān)閉套接字會(huì)立即返回,如果緩沖區(qū)還有數(shù)據(jù)未發(fā)送直接拋棄,并發(fā)送RST結(jié)束命令給對(duì)方。
如果設(shè)置為setSoLinger(true, n),關(guān)閉套接字時(shí)如果緩沖區(qū)還有數(shù)據(jù)沒(méi)發(fā)送,最長(zhǎng)阻塞n毫秒,在這個(gè)時(shí)間內(nèi)如果緩沖區(qū)數(shù)據(jù)發(fā)送了close方法就返回,如果超過(guò)200毫秒任然沒(méi)發(fā)送完,剩下的數(shù)據(jù)直接拋棄,立即返回。

通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)認(rèn)識(shí)下Socket:
編寫了服務(wù)端Socket和客戶端Socket,客戶端連接服務(wù)器后向服務(wù)器發(fā)送數(shù)據(jù),服務(wù)端收到后回送給客戶端一個(gè)信息,當(dāng)服務(wù)端收到客戶端發(fā)送的bye也給客戶端回送一個(gè)bye,然后客戶端和服務(wù)端斷開連接。這里的客戶端和服務(wù)端都是在本機(jī)上。
服務(wù)端:

public class Server {public static void main(String args[]) throws IOException {//創(chuàng)建服務(wù)端套接字,監(jiān)聽20000端口ServerSocket server=createSocket();initServerConfig(server);System.out.println("服務(wù)端準(zhǔn)備就緒>> IP:"+server.getLocalSocketAddress().toString()+" PORT:"+server.getLocalPort());while (true){//監(jiān)聽Socket client=null;try {client=server.accept();System.out.println("有客戶端連接:IP:"+client.getInetAddress().getHostAddress()+" PORT:"+client.getPort());//開個(gè)線程去處理和客戶端的交互new ClientHandler(client).start();}catch (SocketTimeoutException e){}}}private static void initServerConfig(ServerSocket server) throws IOException {server.setSoTimeout(5000);server.setReuseAddress(true);server.setReceiveBufferSize(64*1024*1024);server.setPerformancePreferences(1,1,1);server.bind(new InetSocketAddress(20000));}private static ServerSocket createSocket() throws IOException {ServerSocket serverSocket=new ServerSocket();return serverSocket;}private static class ClientHandler extends Thread{private Socket mClient;private boolean flag=true;public ClientHandler(Socket socket){this.mClient=socket;}@Overridepublic void run() {super.run();BufferedReader reader=null;PrintStream writer=null;try {//獲取輸入流 接收客戶端的輸入 mClient.getInputStream()reader=new BufferedReader(new InputStreamReader(mClient.getInputStream()));//獲取輸出流 用于向客戶端發(fā)送數(shù)據(jù) mClient.getOutputStream()writer=new PrintStream(mClient.getOutputStream());//讀取客戶端發(fā)送到數(shù)據(jù)do {String msg=reader.readLine();System.out.println("Client:"+msg);if (msg.equalsIgnoreCase("bye")){writer.println("bye");flag=false;}else {writer.println("len="+msg.length());}}while (flag);} catch (IOException e) {e.printStackTrace();} finally {try {//客戶端退出后釋放資源reader.close();writer.close();mClient.close();System.out.println("客戶端退出");} catch (IOException e) {e.printStackTrace();}}}} }

客戶端:

public class Client {public static void main(String args[]) throws IOException {Socket socket=createSocket();initSocketConfig(socket);//連接到本地 端口為20000的服務(wù)端 連接超時(shí)5000socket.connect(new InetSocketAddress(InetAddress.getLocalHost(),20000),5000);System.out.println("成功連接到服務(wù)器");//輸入流 用于獲取服務(wù)端發(fā)送到的數(shù)據(jù) socket.getInputStream()BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));//輸出流 用于向服務(wù)端發(fā)送數(shù)據(jù) socket.getOutputStream()PrintStream writer=new PrintStream(socket.getOutputStream());//鍵盤流BufferedReader cin=new BufferedReader(new InputStreamReader(System.in));boolean flag=true;do {String line=cin.readLine();writer.println(line);//發(fā)送數(shù)據(jù)String rcv=reader.readLine();//接收數(shù)據(jù)System.out.println("Server:"+rcv);if (rcv.equalsIgnoreCase("bye")){flag=false;}}while (flag);//釋放資源System.out.println("客戶端退出");cin.close();writer.close();reader.close();socket.close();}private static void initSocketConfig(Socket socket) throws SocketException {socket.setSoTimeout(5000);//讀超時(shí)時(shí)間socket.setReuseAddress(true);socket.setKeepAlive(true);socket.setOOBInline(true);socket.setTcpNoDelay(true);socket.setSoLinger(true,200);socket.setSendBufferSize(64*1024*1024);socket.setReceiveBufferSize(64*1024*1024);}private static Socket createSocket() throws IOException {Socket socket=new Socket();return socket;} }

結(jié)果:
客戶端打印:

服務(wù)端打印:

可以看到服務(wù)端和客戶端之間的通訊就是通過(guò)套接字進(jìn)行的。

學(xué)習(xí)記錄,如有錯(cuò)誤歡迎指正。

總結(jié)

以上是生活随笔為你收集整理的Socket网络编程——(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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