Java的Socket網(wǎng)絡(luò)編程基礎(chǔ)知識入門教程
一、TCP/IP簡介
TCP/IP協(xié)議族是互聯(lián)網(wǎng)使用的協(xié)議,也可以用在獨立的專用網(wǎng)絡(luò)中。
TCP/IP協(xié)議族包括了IP協(xié)議、TCP協(xié)議和UDP協(xié)議。
IP協(xié)議使用IP地址來分發(fā)報文,但它是盡力而為的服務(wù),報文可能丟失、亂序或者
重復(fù)發(fā)送。TCP和UDP協(xié)議在IP協(xié)議基礎(chǔ)上增加了端口號,從而在兩臺主機的應(yīng)用
程序間建立起透明的連接。
不同的是,TCP協(xié)議會對IP層的錯誤進行修復(fù),它通過握手消息在主機間建立連接,
之后通過在消息中加入序列號來恢復(fù)消息中的錯誤。而UDP只是簡單地擴展了IP協(xié)議,
使它能夠在應(yīng)用程序之間工作,而不是主機之間。
關(guān)于IP地址,一臺主機可以有多個網(wǎng)絡(luò)接口,而一個接口又可以有多個地址。
有些IP地址是有特殊用途的:
A.回環(huán)地址:127.0.0.1,總是被分配給一個回環(huán)接口,主要用于測試。
B.私有地址:以10、192.168、172.(16-31)開頭,用于私有網(wǎng)絡(luò)。NAT設(shè)備轉(zhuǎn)發(fā)報文
時,將一個接口中報文的私有地址端口對映射成另一個接口中的公有地址端口對。這
就使一小組主機能夠共享一個IP地址對。
C.多播地址:第一個數(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實例程序
要注意一點,雖然在Client端只用了一個write()方法發(fā)送字符串,服務(wù)器端也可能從
多個塊中接受該信息。即使回饋字符串在服務(wù)器返回時存于一個塊中,也可能被TCP
協(xié)議分割成多個部分。
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時指定的是遠端服務(wù)器監(jiān)聽的端口號而沒有指定本地端口,因此將
采用默認地址和可用的端口號。在我的機器上Client端口是4593,連接到服務(wù)器的
端口7。
3.UDP實例程序
為什么使用UDP協(xié)議?如果應(yīng)用程序只交換少量的數(shù)據(jù),TCP連接的建立階段就至少
要傳輸其兩倍的信息量(還有兩倍的往返時間)。
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);
}
}
通過這個例子與之前TCP的實例進行比較,有如下區(qū)別:
A.DatagramSocket在創(chuàng)建時不需要指定目的地址,因為UDP不需要建立連接,每個
數(shù)據(jù)報文都可以發(fā)送或接收于不同的目的地址。
B.如果像TCP一樣在read()上阻塞等待,將可能永遠阻塞在那里,因為UDP協(xié)議只是
簡單地擴展了IP協(xié)議,UDP報文可能丟失掉。所以一定要設(shè)置阻塞等待的超時時間。
C.UDP協(xié)議保留了消息的邊界信息,每次receive()調(diào)用最多只能接收一次send()方法
調(diào)用所發(fā)送的數(shù)據(jù)。
D.一個UDP報文DatagramPacket能傳輸?shù)淖畲髷?shù)據(jù)是65507字節(jié),超出部分的字節(jié)將
自動被丟棄,而且對接收程序也沒有任何的提示。因此緩存數(shù)組可以設(shè)置成65000字節(jié)
左右是安全的。
E.如果反復(fù)使用同一個DatagramPacket實例調(diào)用receive()方法,每次調(diào)用前都必須顯式
地將消息的內(nèi)部長度重置為緩存區(qū)的實際長度。
相關(guān)文章
關(guān)于BufferedReader的read()和readLine()的區(qū)別
這篇文章主要介紹了關(guān)于BufferedReader的read()和readLine()的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
springboot中RestTemplate發(fā)送HTTP請求的實現(xiàn)示例
RestTemplate是一個 spring-web 提供的執(zhí)行HTTP請求的同步阻塞式工具類,本文就來介紹一下RestTemplate發(fā)送HTTP請求,具有一定的參考價值,感興趣的可以了解一下2024-03-03
MyBatis學(xué)習(xí)教程(八)-Mybatis3.x與Spring4.x整合圖文詳解
這篇文章主要介紹了MyBatis學(xué)習(xí)教程(八)-Mybatis3.x與Spring4.x整合圖文詳解的相關(guān)資料,需要的朋友可以參考下2016-05-05
如何使用BeanUtils.copyProperties進行對象之間的屬性賦值
這篇文章主要介紹了使用BeanUtils.copyProperties進行對象之間的屬性賦值,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

