[TOC]
TCP/IP協(xié)議分層模型
- TCP/IP分層模型
自頂向下 | 協(xié)議 | 功能 | PDU(協(xié)議數(shù)據(jù)單元) |
---|---|---|---|
應(yīng)用層 | HTTP危队、SSH射众、FTP话肖、TELNET等等 | 通過應(yīng)用進(jìn)程間的交互來完成特定網(wǎng)絡(luò)應(yīng)用 | 報文 |
運輸層 | TCP剥纷、UDP | 在應(yīng)用程序端點之間傳輸應(yīng)用層報文 | 報文段 |
網(wǎng)絡(luò)層 | IP | 將數(shù)據(jù)包從一臺主機(jī)傳輸?shù)搅硪慌_主機(jī) | 數(shù)據(jù)報 |
鏈路層 | 以太網(wǎng)抒钱、wifi、電纜接入網(wǎng)的DOCSIS等 | 數(shù)據(jù)鏈路層將網(wǎng)絡(luò)層交下來的IP數(shù)據(jù)報組裝成幀鼎天,在兩個相鄰節(jié)點間的鏈路上傳送幀 | 幀 |
物理層 | 與傳輸媒介相關(guān) | 實現(xiàn)相鄰計算機(jī)節(jié)點之間比特流的透明傳送舀奶,盡可能屏蔽掉具體傳輸介質(zhì)和物理設(shè)備的差異 | 比特 |
下層提供接口供上層使用,為上層服務(wù)斋射。
Socket簡述
Socket是運輸層提供給應(yīng)用層的接口育勺,使得應(yīng)用層可以基于socket接口實現(xiàn)基于TCP/UDP協(xié)議的通信但荤。它處于應(yīng)用層和運輸層之間。Socket是面向應(yīng)用程序端點的涧至,封裝了IP地址和端口號纱兑,二者標(biāo)識了網(wǎng)絡(luò)主機(jī)上的某一個應(yīng)用程序,IP地址標(biāo)識網(wǎng)絡(luò)主機(jī)化借,端口號標(biāo)識主機(jī)上的應(yīng)用程序,這樣唯一指定了通信地址捡多,便可以發(fā)起應(yīng)用程序端之間的網(wǎng)絡(luò)通信蓖康。
在操作系統(tǒng)層面,socket是抽象成文件的垒手,它和文件相似蒜焊,都是數(shù)據(jù)來源,只不過socket底層還有網(wǎng)絡(luò)通信鏈路和各種底層協(xié)議的支持科贬,可以簡單的看成一個遠(yuǎn)端的文件泳梆,使用socket打開文件,對這個特殊的文件進(jìn)行讀寫操作和交互榜掌,從而達(dá)到通信(分享)的目的优妙。
基于BIO的TCP通信
當(dāng)客戶端想要打開一個連接到服務(wù)器的TCP/IP連接時,就要使用到Java Socket類憎账。socket類只需要被告知連接的IP地址和TCP端口套硼,其余的都有Java實現(xiàn)。
假如我們想要打開一個監(jiān)聽服務(wù)胞皱,來監(jiān)聽客戶端連接某些指定TCP端口的連接邪意,那就需要使用Java ServerSocket類。當(dāng)客戶端通過Socket連接服務(wù)器端的ServerSocket監(jiān)聽時反砌,服務(wù)器端會指定這個連接的一個Socket雾鬼,此時客戶端與服務(wù)器端間的通信就變成Socket與Socket之間的通信。
ServerSocket類
ServerSocket宴树,由其名稱即可知策菜,是作為服務(wù)端的socket對象,在網(wǎng)絡(luò)編程中森渐,它又稱為歡迎套接字做入,專門監(jiān)聽主機(jī)上的某個端口,如果有連接到此端口同衣,則產(chǎn)生一個與該連接相連的Socket類對象竟块。
Socket類是對網(wǎng)絡(luò)套接字的封裝,也即為主機(jī)應(yīng)用端通信接口的抽象(可能是遠(yuǎn)程也可能是本地耐齐,但是是通過網(wǎng)絡(luò)來通信的進(jìn)程)浪秘。
當(dāng)socket對象之間連接建立成功蒋情,進(jìn)程持有的socket即可以抽象為遠(yuǎn)端文件,通過對這個文件進(jìn)行讀寫進(jìn)行數(shù)據(jù)交換也就是所謂的通信耸携。
ServerSocket的使用目的是監(jiān)聽主機(jī)端口棵癣,接收到連接后返回一個Socket對象建立通信,這是基于TCP的可靠連接通信夺衍。UDP通信中無需雙方建立連接狈谊。
- ServerSocket(縮寫成SS)的核心屬性:
- bindAddr, 宿主主機(jī)的IP地址
- port沟沙,監(jiān)聽的端口號
- backlog河劝,請求傳入連接隊列的最大長度
- 核心API
- 實例化對象
// 創(chuàng)建一個未綁定的SS.此方法創(chuàng)建的SS需要調(diào)用bind方法綁定端口
public ServerSocket() throws IOException;
// 創(chuàng)建一個綁定到指定端口號的SS
// 端口號指定為0,則自動選擇
// backlog=50
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
public ServerSocket(int port, int backlog) throws IOException;
// 核心方法
// @param bindAddr the local InetAddress the server will bind to
// InetAddress 是對IP地址的封裝
public ServerSocket(int port, int backlog, InetAddress bindAddr)
throws IOException ;
- bind
手動綁定到端
public void bind(SocketAddress endpoint) throws IOException
public void bind(SocketAddress endpoint, int backlog) throws IOException ;
endpoint是SocketAddress
類型矛紫,使用其實現(xiàn)類InetSocketAddress
來構(gòu)造地址(IP+Port)赎瞎。指定端地址的創(chuàng)建SS方法不需要再手動綁定。
- 監(jiān)聽連接
public Socket accept() throws IOException ;
阻塞方法颊咬,在連接到達(dá)之前一直阻塞务甥,有連接時返回一個對該連接抽象的Socket對象
- 其他
獲取地址信息:getInetAddress(),getLocalPort(),getLocalSocketAddress()[IP+Port]
檢查狀態(tài):isBound(),isClosed()
關(guān)閉:close()
(ServerSocket實現(xiàn)了Closeable接口)
accept超時時間:setSoTimeout(int timeout),getSoTimeout()
- 示例
public class TestServerSocket {
private static final int PORT = 12345;
private static final int BACKLOG = 100;
public static void main(String[] args) throws IOException {
ServerSocket ss = getServerSocket();
System.out.println("server start...");
while (true) {
Socket socket = ss.accept();
System.out.println("connect incoming: "
+ socket.getRemoteSocketAddress());
}
}
private static ServerSocket getServerSocket() throws IOException {
ServerSocket ss = new ServerSocket(PORT);
// or
ss = new ServerSocket(PORT, BACKLOG);
// or
InetAddress bindAddr = InetAddress.getLocalHost();
ss = new ServerSocket(PORT, BACKLOG, bindAddr);
// or
ss = new ServerSocket();
SocketAddress endpoint = new InetSocketAddress("localhost", PORT);
ss.bind(endpoint, BACKLOG);
return ss;
}
}
Socket類
A socket is an endpoint for communication between two machines.
- Socket使用:
- 創(chuàng)建連接到遠(yuǎn)端端點的Socket對象(實例化和連接)
- 讀寫socket進(jìn)行面向流的通信
- 創(chuàng)建socket(連接)
// 創(chuàng)建一個未連接的socket對象,可顯式調(diào)用connect連接
public Socket()喳篇;
// 指定代理創(chuàng)建一個未連接的socket對象敞临,可顯式調(diào)用connect連接
public Socket(Proxy proxy);
// 創(chuàng)建一個連接到指定地址的socket對象
public Socket(String host, int port)
throws UnknownHostException, IOException杭隙;
public Socket(InetAddress address, int port) throws IOException哟绊;
// 同時為socket綁定本地地址,也可以顯式調(diào)用bind方法來綁定本地地址痰憎;默認(rèn)是自動分配(端口號)
public Socket(String host, int port, InetAddress localAddr,
int localPort) throws IOException
/**
連接票髓、bind
*/
public void connect(SocketAddress endpoint) throws IOException ;
/*
* Connects this socket to the server with a specified timeout value.
* A timeout of zero is interpreted as an infinite timeout. The connection
* will then block until established or an error occurs.
*/
public void connect(SocketAddress endpoint, int timeout) throws IOException 铣耘;
// Binds the socket to a local address.
public void bind(SocketAddress bindpoint) throws IOException
- 讀寫socket進(jìn)行通信
socket.getInputStream();
socket.getOutputStream();
- 示例
private static final int PORT = 12345;
private static final String HOST = "localhost";
private static final int LOCAL_PORT = 23456;
public static void main(String[] args) throws IOException {
Socket socket = getSocket();
// communicate
OutputStream os = socket.getOutputStream();
os.write("hellosocket".getBytes());
os.flush();
socket.close();
}
private static Socket getSocket() throws IOException {
Socket sock = new Socket();
SocketAddress endpoint = new InetSocketAddress(HOST, PORT);
sock.connect(endpoint);
// or sock = new Socket(HOST, PORT);
// or sock = new Socket(InetAddress.getLocalHost(), PORT);
// or sock = new Socket(HOST, PORT, InetAddress.getLocalHost(),
// LOCAL_PORT);
return sock;
}
基于BIO的UDP通信
UDP的工作方式與TCP相比略有不同洽沟。使用UDP通信時,在客戶端與服務(wù)器之間并沒有建立連接的概念蜗细,客戶端發(fā)送到服務(wù)器的數(shù)據(jù)裆操,服務(wù)器可能(也可能并沒有)收到這些數(shù)據(jù),而且客戶端也并不知道這些數(shù)據(jù)是否被服務(wù)器成功接收炉媒。當(dāng)服務(wù)器向客戶端發(fā)送數(shù)據(jù)時也是如此踪区。
正因為是不可靠的數(shù)據(jù)傳輸,UDP相比與TCP來說少了很多的協(xié)議開銷吊骤。
使用UDP通信不需要建立連接缎岗,沒有TCP三次握手的連接動作,只需要在通信時指定消息的接收者地址即可白粉,類似于短信传泊,交互需要發(fā)送-接收-發(fā)送鼠渺,每一步都是獨立的。
DatagramSocket
DatagramSocket類實現(xiàn)“UDP通信”眷细,包括客戶端和服務(wù)器端往堡。雖然UDP方式的網(wǎng)絡(luò)通訊不需要建立專用的網(wǎng)絡(luò)連接醒陆,但是畢竟還是需要發(fā)送和接收數(shù)據(jù)钓猬,DatagramSocket實現(xiàn)的就是發(fā)送數(shù)據(jù)時的發(fā)射器誊锭,以及接收數(shù)據(jù)時的監(jiān)聽器的角色。
- 創(chuàng)建實例
// 創(chuàng)建一個自動分配本地端口的DatagramSocket
public DatagramSocket() throws SocketException ;
// 指定綁定的本地地址
public DatagramSocket(SocketAddress bindaddr) throws SocketException 校读;
// 指定本地綁定端口
public DatagramSocket(int port) throws SocketException 奔害;
// 指定本地綁定地址和端口
public DatagramSocket(int port, InetAddress laddr) throws SocketException;
bind
public synchronized void bind(SocketAddress addr) throws SocketException
send
發(fā)送信息
/**
* Sends a datagram packet from this socket. The
* {@code DatagramPacket} includes information indicating the
* data to be sent, its length, the IP address of the remote host,
* and the port number on the remote host.
*
*/
public void send(DatagramPacket p) throws IOException
發(fā)送的目的地址在報文對象內(nèi)配置地熄。
- receive
/**
* Receives a datagram packet from this socket. When this method
* returns, the {@code DatagramPacket}'s buffer is filled with
* the data received. The datagram packet also contains the sender's
* IP address, and the port number on the sender's machine.
* <p>
* This method blocks until a datagram is received. The
* {@code length} field of the datagram packet object contains
* the length of the received message. If the message is longer than
* the packet's length, the message is truncated.
* <p>
*/
public synchronized void receive(DatagramPacket p) throws IOException
DatagramPacket
DatagramPacket類實現(xiàn)對于網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)封裝,也就是說芯杀,該類的對象代表網(wǎng)絡(luò)中交換的數(shù)據(jù)端考。在UDP方式的網(wǎng)絡(luò)編程中,無論是需要發(fā)送的數(shù)據(jù)還是需要接收的數(shù)據(jù)揭厚,都必須被處理成DatagramPacket類型的對象却特,該對象中包含發(fā)送到的地址、發(fā)送到的端口號以及發(fā)送的內(nèi)容等筛圆。其實DatagramPacket類的作用類似于現(xiàn)實中的信件裂明,在信件中包含信件發(fā)送到的地址以及接收人,還有發(fā)送的內(nèi)容等太援,郵局只需要按照地址傳遞即可闽晦。在接收數(shù)據(jù)時,接收到的數(shù)據(jù)也必須被處理成DatagramPacket類型的對象提岔,在該對象中包含發(fā)送方的地址仙蛉、端口號等信息,也包含數(shù)據(jù)的內(nèi)容碱蒙。和TCP方式的網(wǎng)絡(luò)傳輸相比荠瘪,IO編程在UDP方式的網(wǎng)絡(luò)編程中變得不是必須的內(nèi)容,結(jié)構(gòu)也要比TCP方式的網(wǎng)絡(luò)編程簡單一些赛惩。
/**
* Constructs a {@code DatagramPacket} for receiving packets of
* length {@code length}, specifying an offset into the buffer.
* <p>
* The {@code length} argument must be less than or equal to
* {@code buf.length}.
*
* @param buf buffer for holding the incoming datagram.
* @param offset the offset for the buffer
* @param length the number of bytes to read.
*
* @since 1.2
*/
public DatagramPacket(byte buf[], int offset, int length) {
setData(buf, offset, length);
this.address = null;
this.port = -1;
}
public DatagramPacket(byte buf[], int length) {
this (buf, 0, length);
}
/**
* Constructs a datagram packet for sending packets of length
* {@code length} with offset {@code ioffset}to the
* specified port number on the specified host. The
* {@code length} argument must be less than or equal to
* {@code buf.length}.
*
* @param buf the packet data.
* @param offset the packet data offset.
* @param length the packet data length.
* @param address the destination address.
* @param port the destination port number.
* @see java.net.InetAddress
*
* @since 1.2
*/
public DatagramPacket(byte buf[], int offset, int length,
InetAddress address, int port) {
setData(buf, offset, length);
setAddress(address);
setPort(port);
}
/**
* Constructs a datagram packet for sending packets of length
* {@code length} with offset {@code ioffset}to the
* specified port number on the specified host. The
* {@code length} argument must be less than or equal to
* {@code buf.length}.
*
* @param buf the packet data.
* @param offset the packet data offset.
* @param length the packet data length.
* @param address the destination socket address.
* @throws IllegalArgumentException if address type is not supported
* @see java.net.InetAddress
*
* @since 1.4
*/
public DatagramPacket(byte buf[], int offset, int length,
SocketAddress address) {
setData(buf, offset, length);
setSocketAddress(address);
}
public DatagramPacket(byte buf[], int length,
InetAddress address, int port) {
this(buf, 0, length, address, port);
}
public DatagramPacket(byte buf[], int SocketAddress address) {
this(buf, 0, length, address);
}
指定address的用作發(fā)送的數(shù)據(jù)封裝哀墓,沒有address的用來封裝接收到的數(shù)據(jù)。
示例
- UDPserver
/*
* UDP 服務(wù)端
*/
public class UDPServer {
public static void main(String[] args) throws Exception {
// 綁定端口用于在端口監(jiān)聽數(shù)據(jù)到來
DatagramSocket ds = new DatagramSocket(12345);
// 接收數(shù)據(jù)的buffer
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 1024);
// 阻塞方法喷兼,阻塞直到有數(shù)據(jù)到達(dá)
ds.receive(dp);
// 解析數(shù)據(jù)
String data = new String(dp.getData(), 0, dp.getLength());
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
System.out.println("ip地址:" + ip + " 端口號:" + port + " 消息:" + data);
// 關(guān)閉socket
ds.close();
}
}
- UDPclient
public class UDPClient {
public static void main(String[] args) throws Exception {
// 創(chuàng)建socket
DatagramSocket ds = new DatagramSocket();
String message = "Hello Java World!";
// 封裝報文對象
DatagramPacket dp = new DatagramPacket(message.getBytes(),
message.length(), InetAddress.getByName("127.0.0.1"),
12345);
// 發(fā)送報文
ds.send(dp);
ds.close();
}
}
參考資料
[1] Java網(wǎng)絡(luò)教程
[2] Java網(wǎng)絡(luò)編程:UDP通信