Java的Socket網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)入門教程
一、TCP/IP簡(jiǎn)介
TCP/IP協(xié)議族是互聯(lián)網(wǎng)使用的協(xié)議,也可以用在獨(dú)立的專用網(wǎng)絡(luò)中。
TCP/IP協(xié)議族包括了IP協(xié)議、TCP協(xié)議和UDP協(xié)議。
IP協(xié)議使用IP地址來(lái)分發(fā)報(bào)文,但它是盡力而為的服務(wù),報(bào)文可能丟失、亂序或者
重復(fù)發(fā)送。TCP和UDP協(xié)議在IP協(xié)議基礎(chǔ)上增加了端口號(hào),從而在兩臺(tái)主機(jī)的應(yīng)用
程序間建立起透明的連接。
不同的是,TCP協(xié)議會(huì)對(duì)IP層的錯(cuò)誤進(jìn)行修復(fù),它通過(guò)握手消息在主機(jī)間建立連接,
之后通過(guò)在消息中加入序列號(hào)來(lái)恢復(fù)消息中的錯(cuò)誤。而UDP只是簡(jiǎn)單地?cái)U(kuò)展了IP協(xié)議,
使它能夠在應(yīng)用程序之間工作,而不是主機(jī)之間。
關(guān)于IP地址,一臺(tái)主機(jī)可以有多個(gè)網(wǎng)絡(luò)接口,而一個(gè)接口又可以有多個(gè)地址。
有些IP地址是有特殊用途的:
A.回環(huán)地址:127.0.0.1,總是被分配給一個(gè)回環(huán)接口,主要用于測(cè)試。
B.私有地址:以10、192.168、172.(16-31)開頭,用于私有網(wǎng)絡(luò)。NAT設(shè)備轉(zhuǎn)發(fā)報(bào)文
時(shí),將一個(gè)接口中報(bào)文的私有地址端口對(duì)映射成另一個(gè)接口中的公有地址端口對(duì)。這
就使一小組主機(jī)能夠共享一個(gè)IP地址對(duì)。
C.多播地址:第一個(gè)數(shù)字在224~239之間。
二、Socket基礎(chǔ)
1.地址的獲得
public static void main(String[] args) { try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); System.out.println("Interface: " + iface.getName()); Enumeration<InetAddress> addrList = iface.getInetAddresses(); if (!addrList.hasMoreElements()) System.out.println("No address"); while (addrList.hasMoreElements()) { InetAddress address = addrList.nextElement(); System.out.println("Address: " + address.getHostAddress()); } } } catch (SocketException e) { e.printStackTrace(); } }
2.TCP實(shí)例程序
要注意一點(diǎn),雖然在Client端只用了一個(gè)write()方法發(fā)送字符串,服務(wù)器端也可能從
多個(gè)塊中接受該信息。即使回饋?zhàn)址诜?wù)器返回時(shí)存于一個(gè)塊中,也可能被TCP
協(xié)議分割成多個(gè)部分。
TCPEchoClientTest.java
public static void main(String[] args) throws IOException { String server = args[0]; byte[] data = args[1].getBytes(); int port = 7; Socket socket = new Socket(server, port); System.out.println("Connected to server..."); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write(data); int totalBytesRcvd = 0; int bytesRcvd; while (totalBytesRcvd < data.length) { if ((bytesRcvd = in.read(data, totalBytesRcvd, data.length - totalBytesRcvd)) == -1) throw new SocketException("Connection closed"); totalBytesRcvd += bytesRcvd; } System.out.println("Received: " + new String(data)); socket.close(); }
TCPEchoServerTest.java
private static final int BUFSIZE = 32; public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(7); int recvMsgSize; byte[] receiveBuf = new byte[BUFSIZE]; while (true) { Socket socket = serverSocket.accept(); System.out.println("Handling client " + " from remote " + socket.getRemoteSocketAddress() + " at local " + socket.getLocalSocketAddress()); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } socket.close(); } }
注意new Socket時(shí)指定的是遠(yuǎn)端服務(wù)器監(jiān)聽的端口號(hào)而沒有指定本地端口,因此將
采用默認(rèn)地址和可用的端口號(hào)。在我的機(jī)器上Client端口是4593,連接到服務(wù)器的
端口7。
3.UDP實(shí)例程序
為什么使用UDP協(xié)議?如果應(yīng)用程序只交換少量的數(shù)據(jù),TCP連接的建立階段就至少
要傳輸其兩倍的信息量(還有兩倍的往返時(shí)間)。
UDPEchoClientTest.java
public static void main(String[] args) throws IOException { InetAddress serverAddress = InetAddress.getByName(args[0]); byte[] bytesToSend = args[1].getBytes(); DatagramSocket socket = new DatagramSocket(); socket.setSoTimeout(3000); DatagramPacket sendPacket = new DatagramPacket( bytesToSend, bytesToSend.length, serverAddress, 7); DatagramPacket receivePacket = new DatagramPacket( new byte[bytesToSend.length], bytesToSend.length); // Packets may be lost, so we have to keep trying int tries = 0; boolean receivedResponse = false; do { socket.send(sendPacket); try { socket.receive(receivePacket); if (!receivePacket.getAddress().equals(serverAddress)) throw new IOException("Receive from unknown source"); receivedResponse = true; } catch (IOException e) { tries++; System.out.println("Timeout, try again"); } } while (!receivedResponse && tries < 5); if (receivedResponse) System.out.println("Received: " + new String(receivePacket.getData())); else System.out.println("No response"); socket.close(); }
UDPEchoServerTest.java
private static final int ECHOMAX = 255; public static void main(String[] args) throws IOException { DatagramSocket socket = new DatagramSocket(7); DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX); while (true) { socket.receive(packet); System.out.println("Handling client at " + packet.getAddress()); socket.send(packet); packet.setLength(ECHOMAX); } }
通過(guò)這個(gè)例子與之前TCP的實(shí)例進(jìn)行比較,有如下區(qū)別:
A.DatagramSocket在創(chuàng)建時(shí)不需要指定目的地址,因?yàn)閁DP不需要建立連接,每個(gè)
數(shù)據(jù)報(bào)文都可以發(fā)送或接收于不同的目的地址。
B.如果像TCP一樣在read()上阻塞等待,將可能永遠(yuǎn)阻塞在那里,因?yàn)閁DP協(xié)議只是
簡(jiǎn)單地?cái)U(kuò)展了IP協(xié)議,UDP報(bào)文可能丟失掉。所以一定要設(shè)置阻塞等待的超時(shí)時(shí)間。
C.UDP協(xié)議保留了消息的邊界信息,每次receive()調(diào)用最多只能接收一次send()方法
調(diào)用所發(fā)送的數(shù)據(jù)。
D.一個(gè)UDP報(bào)文DatagramPacket能傳輸?shù)淖畲髷?shù)據(jù)是65507字節(jié),超出部分的字節(jié)將
自動(dòng)被丟棄,而且對(duì)接收程序也沒有任何的提示。因此緩存數(shù)組可以設(shè)置成65000字節(jié)
左右是安全的。
E.如果反復(fù)使用同一個(gè)DatagramPacket實(shí)例調(diào)用receive()方法,每次調(diào)用前都必須顯式
地將消息的內(nèi)部長(zhǎng)度重置為緩存區(qū)的實(shí)際長(zhǎng)度。
相關(guān)文章
Spring如何集成ibatis項(xiàng)目并實(shí)現(xiàn)dao層基類封裝
這篇文章主要介紹了Spring如何集成ibatis項(xiàng)目并實(shí)現(xiàn)dao層基類封裝,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09java中關(guān)于轉(zhuǎn)義字符的一個(gè)bug
本文主要介紹了java中關(guān)于轉(zhuǎn)義字符的一個(gè)bug。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02java后臺(tái)實(shí)現(xiàn)js關(guān)閉本頁(yè)面,父頁(yè)面指定跳轉(zhuǎn)或刷新操作
這篇文章主要介紹了java后臺(tái)實(shí)現(xiàn)js關(guān)閉本頁(yè)面,父頁(yè)面指定跳轉(zhuǎn)或刷新操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11Spring boot 整合CXF開發(fā)web service示例
這篇文章主要介紹了Spring boot 整合CXF開發(fā)web service示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05HttpClient POST請(qǐng)求第三方接口問(wèn)題(多參數(shù)傳參)
這篇文章主要介紹了HttpClient POST請(qǐng)求第三方接口問(wèn)題(多參數(shù)傳參),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例
這篇文章主要介紹了Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-11-11Java中的interrupted()和isInterrupted()
這篇文章主要介紹了Java中的interrupted()和isInterrupted()摸下面文章圍繞interrupted()和isInterrupted()的相關(guān)資料展開詳細(xì)內(nèi)容,需要的小伙伴樂(lè)意參考下面下面文章具體價(jià)紹,希望對(duì)大家有所幫助2021-11-11Java反轉(zhuǎn)字符串和相關(guān)字符編碼的問(wèn)題解決
反轉(zhuǎn)字符串一直被當(dāng)作是簡(jiǎn)單問(wèn)題,大家的思想主要就是利用遍歷,首尾交換字符實(shí)現(xiàn)字符串的反轉(zhuǎn)。例如下面的代碼,就可以簡(jiǎn)單實(shí)現(xiàn)反轉(zhuǎn)。2013-05-05