1. 体系结构
计算机网络各层及其协议的集合,称为网络的体系结构。目前公认的计算机网络体系结构有概念清楚理论完善的OSI七层协议结构和实际上广泛应用的TCP/IP四层体系结构。
2. 各层概述
- 应用层:直接为用户的程序提供服务,如支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等。
- 运输层:负责向两个主机中进程之间的通信提供服务,主要使用面向连接的TCP协议和无连接的UDP协议。
- 网络层:负责为分组交换网上不同主机提供地址交付的通信服务,该层使用IP协议,所以在该层传送的数据称之为IP数据报。路由器工作在该层。
- 数据链路层:负责将IP数据报在相邻的节点之间传输,该层将IP数据报封装成帧,将IP地址转换为MAC硬件地址封装在帧中进行节点间的传输。网桥,桥接器和交换机都工作在该层。
- 物理层:包括数据传输的介质和电气特性,包括电缆,光缆,光信号,电信号的标准等。转发器,信号放大器都工作在该层。
- 每一层的实际运作对上一层都是透明的。
- 应用程序间通过网络交换数据,数据在发送方从上到下层层封装,通过物理层传输,到接收方又从下至上层层拆封还原原始数据。
Java中的网络通讯
1. IP地址
1.1 概述
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
1.2 域名解析
为了方便记忆,出现域名,并且需要从域名转化为IP地址的协议或服务,该服务就是域名解析(DNS)。
特殊地址
- 127.0.0.1:本机回环地址。
- 主机号为全1:该网络广播地址。
1.3 InetAddress类
概述
- 该类没有构造器。
- 该类用于IP地址的封装。
方法
- 返回本机IP原始地址
byte[] getAddress()
- 通过主机名或IP地址获取IP地址对象
static InetAddress[] getAllByName(String host)
static InetAddress getByAddress(byte[] addr)
static InetAddress getByAddress(String host, byte[] addr)
static InetAddress getByName(String host)
- 获取主机IP地址或主机名
- static InetAddress getLocalHost()
String getHostAddress()
String getHostName()
示例
package net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetDemo {
public static void main(String[] args) throws UnknownHostException {
// 获取本机IP对象
InetAddress iphost = InetAddress. getLocalHost();
String ipstr = iphost.getHostAddress(); // "192.168.1.22"
String ipname = iphost.getHostName(); // "YH-PC"
// 获取指定IP对象
InetAddress ip = InetAddress.getByName("192.168.1.1");
ipstr = ip.getHostAddress(); // "192.168.1.1"
ipname = ip.getHostName(); // "192.168.1.1"
// 获取指定主机的IP对象
InetAddress[] ips = InetAddress.getAllByName("www.google.com.hk");
for (int i = 0; i < ips.length; i++) {
ipstr = ips[i].getHostAddress();
ipname = ips[i].getHostName();
System.out.println( ipstr);
System.out.println( ipname);
// 173.194.120.88
// www.google.com.hk
// 216.239.32.39
// www.google.com.hk
}
}
}
2. 端口号
- 全称为TCP/IP端口,是应用层用于识别通讯进程的接口。
- 有效端口:0~65535,系统使用或保留的端口是:0~1024。
3. 传输层协议
3.1 UDP协议
- 无连接的,不保证可靠交付,只能提供尽最大努力交付。
- 无需创建链接,速度较快。
- 支持一对一,一对多,多对一,多对多通讯。
- 面向报文的通信。
- 没有拥塞控制,所以可能会有丢包,但是传输速率不受影响,延时较小,适合实时性要求高的网络视频,聊天等。
3.2 TCP协议
- 面向连接的,提供可靠交付服务。
- TCP链接只能有两个端点,所以只能是点对点(一对一)通讯。
- TCP提供全双工通信,适合大数据量的传输。
- 面向字节流的通信。
- 需要通过三次握手建立链接,速度稍慢。
4. 通讯过程
- 明确IP地址。
- 明确通讯端口。
- 明确使用协议。
- 建立Socket(套接字,类似于通信链接的端口,插口)通信服务。
- 建立通信数据报文Package。
- 使用Socket来发送或接收报文。
UDP协议相关API
1. DatagramSocket类
概述
该类用于UDP通讯套接字的建立。
构造器
- 绑定本地主机创建套接字
DatagramSocket()
DatagramSocket(int port)
- 绑定指定主机创建套接字(当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自指定地址的报文)
DatagramSocket(DatagramSocketImpl impl)
DatagramSocket(int port, InetAddress laddr)
DatagramSocket(SocketAddress bindaddr)
常用方法
- 将套接字绑定到特定的IP地址和端口号
void bind(SocketAddress addr)
- 关闭该套接字
void close()
- 将该套接字连接至远程地址(用于建立单向通讯)
void connect(InetAddress address, int port)
void connect(SocketAddress addr)
- 断开链接
void disconnect()
- 获取与该套接字连接的地址
InetAddress getInetAddress()
SocketAddress getRemoteSocketAddress()
- 获取与该套接字连接的端口号
int getPort()
- 获取与该套接字绑定的本地地址
InetAddress getLocalAddress()
SocketAddress getLocalSocketAddress()
- 获取与该套接字绑定的本地端口号
int getLocalPort()
- 判断套接字状态
boolean isBound()
boolean isClosed()
boolean isConnected()
- 接收和发送
void receive(DatagramPacket p)
void send(DatagramPacket p)
- 设置接收和发送缓冲区大小以及超时限制
void setReceiveBufferSize(int size)
void setSendBufferSize(int size)
void setSoTimeout(int timeout)
2. DatagramPacket类
概述
该类用于数据报文的建立。
构造器
- 建立发送报文
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
DatagramPacket(byte[] buf, int length, SocketAddress address)
- 建立接收报文
DatagramPacket(byte[] buf, int length)
DatagramPacket(byte[] buf, int offset, int length)
常用方法
- 获取该数据报发送的目的地址或接收的源地址。
InetAddress getAddress()
SocketAddress getSocketAddress()
int getPort()
- 获取数据报数据长度和偏移量
int getLength()
int getOffset()
- 设置数据报发送地址
void setAddress(InetAddress iaddr)
void setSocketAddress(SocketAddress address)
void setPort(int iport)
- 设置数据报数据
void setData(byte[] buf)
void setData(byte[] buf, int offset, int length)
void setLength(int length)
3. 通信步骤
发送端
- 建立套接字。
- 明确目标地址和端口号,建立带地址的数据报文。
- 使用套接字发送数据报文。
- 关闭套接字。
接收端
- 明确端口号,建立带端口号套接字。
- 建立接收的数据报文。
- 使用套接字接收数据报文。
- 关闭套接字。
4. 示例
UDP发送端
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDP_Send {
public static void main(String[] args) throws IOException {
/*
* UDP发送端
*/
System.out.println( "UDP发送端");
// 获取本地IP地址
InetAddress ip = InetAddress.getLocalHost();
System.out.println( "Destination IP = " + ip.getHostAddress());
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket();
// 2. 将数据封装到数据包中
byte[] buf = "hello UDP!".getBytes();
DatagramPacket dp = new DatagramPacket( buf, buf.length , ip , 22222);
// 3. 使用Socket对象的send方法将数据包发送出去
ds.send(dp);
// 4. 关闭资源。
ds.close();
}
}
UDP接收端
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDP_Receive {
public static void main(String[] args) throws IOException {
/*
* UDP接收端
*/
System.out.println( "UDP接收端");
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket(22222);
// 2. 定义数据包方便接收数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
// 3. 使用Socket对象的receive方法阻塞接收数据
ds.receive(dp);
// 4. 获取接收到的数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String data = new String( dp.getData(), 0, dp.getLength());
System.out.println( "ip = " + ip + "\nport = " + port + "\ndata = " + data );
// 4. 关闭资源。
ds.close();
}
}
实例
聊天室程序
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Test_ChatByUDP {
public static void main(String[] args) throws SocketException, UnknownHostException {
// 创建用于发送和接收的套接字,端口33333只用于聊天室接收程序。
DatagramSocket sendDs = new DatagramSocket();
DatagramSocket receDs = new DatagramSocket(33333);
// 创建广播地址
InetAddress ip = InetAddress.getByName("192.168.1.255");
// 创建发送和接收对象
Send sd = new Send( sendDs, ip);
Rece rc = new Rece( receDs);
new Thread( sd).start();
new Thread( rc).start();
}
}
class Send implements Runnable {
private DatagramSocket ds;
private InetAddress ip;
public Send(DatagramSocket ds, InetAddress ip) {
super();
this.ds = ds;
this.ip = ip;
}
@Override
public void run() {
// 读取键盘录入
BufferedReader br = new BufferedReader( new InputStreamReader(System.in ));
String str = null;
try {
while (( str = br.readLine()) != null) {
// 发送键盘录入的字符
DatagramPacket dp = new DatagramPacket(str.getBytes(), str .length(), ip , 33333);
ds.send( dp);
if ( "exit".equals(str )) {
break;
} else if ( "close".equals(str )) {
break;
}
}
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
while ( true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
try {
// 阻塞接收
ds.receive( dp);
} catch (IOException e) {
e.printStackTrace();
}
// 获取发送方地址
String ip = dp.getAddress().getHostAddress();
String str = new String( dp.getData(), 0, dp.getLength());
int port = dp.getPort();
if ("exit".equals( str)) {
// 判断发送字符
System. out.println(ip + ":" + port + " 离开了聊天室!" );
} else if ("close".equals( str)) {
System. out.println(ip + ":" + port + " 请求关闭聊天室!" );
break;
} else {
System. out.println(ip + ":" + port + " 说:" + str);
}
}
ds.close();
System.out.println( "聊天室已关闭!" );
}
}
1. 体系结构
计算机网络各层及其协议的集合,称为网络的体系结构。目前公认的计算机网络体系结构有概念清楚理论完善的OSI七层协议结构和实际上广泛应用的TCP/IP四层体系结构。
2. 各层概述
- 应用层:直接为用户的程序提供服务,如支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等。
- 运输层:负责向两个主机中进程之间的通信提供服务,主要使用面向连接的TCP协议和无连接的UDP协议。
- 网络层:负责为分组交换网上不同主机提供地址交付的通信服务,该层使用IP协议,所以在该层传送的数据称之为IP数据报。路由器工作在该层。
- 数据链路层:负责将IP数据报在相邻的节点之间传输,该层将IP数据报封装成帧,将IP地址转换为MAC硬件地址封装在帧中进行节点间的传输。网桥,桥接器和交换机都工作在该层。
- 物理层:包括数据传输的介质和电气特性,包括电缆,光缆,光信号,电信号的标准等。转发器,信号放大器都工作在该层。
- 每一层的实际运作对上一层都是透明的。
- 应用程序间通过网络交换数据,数据在发送方从上到下层层封装,通过物理层传输,到接收方又从下至上层层拆封还原原始数据。
Java中的网络通讯
1. IP地址
1.1 概述
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
1.2 域名解析
为了方便记忆,出现域名,并且需要从域名转化为IP地址的协议或服务,该服务就是域名解析(DNS)。
特殊地址
- 127.0.0.1:本机回环地址。
- 主机号为全1:该网络广播地址。
1.3 InetAddress类
概述
- 该类没有构造器。
- 该类用于IP地址的封装。
方法
- 返回本机IP原始地址
byte[] getAddress()
- 通过主机名或IP地址获取IP地址对象
static InetAddress[] getAllByName(String host)
static InetAddress getByAddress(byte[] addr)
static InetAddress getByAddress(String host, byte[] addr)
static InetAddress getByName(String host)
- 获取主机IP地址或主机名
- static InetAddress getLocalHost()
String getHostAddress()
String getHostName()
示例
package net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetDemo {
public static void main(String[] args) throws UnknownHostException {
// 获取本机IP对象
InetAddress iphost = InetAddress. getLocalHost();
String ipstr = iphost.getHostAddress(); // "192.168.1.22"
String ipname = iphost.getHostName(); // "YH-PC"
// 获取指定IP对象
InetAddress ip = InetAddress.getByName("192.168.1.1");
ipstr = ip.getHostAddress(); // "192.168.1.1"
ipname = ip.getHostName(); // "192.168.1.1"
// 获取指定主机的IP对象
InetAddress[] ips = InetAddress.getAllByName("www.google.com.hk");
for (int i = 0; i < ips.length; i++) {
ipstr = ips[i].getHostAddress();
ipname = ips[i].getHostName();
System.out.println( ipstr);
System.out.println( ipname);
// 173.194.120.88
// www.google.com.hk
// 216.239.32.39
// www.google.com.hk
}
}
}
2. 端口号
- 全称为TCP/IP端口,是应用层用于识别通讯进程的接口。
- 有效端口:0~65535,系统使用或保留的端口是:0~1024。
3. 传输层协议
3.1 UDP协议
- 无连接的,不保证可靠交付,只能提供尽最大努力交付。
- 无需创建链接,速度较快。
- 支持一对一,一对多,多对一,多对多通讯。
- 面向报文的通信。
- 没有拥塞控制,所以可能会有丢包,但是传输速率不受影响,延时较小,适合实时性要求高的网络视频,聊天等。
3.2 TCP协议
- 面向连接的,提供可靠交付服务。
- TCP链接只能有两个端点,所以只能是点对点(一对一)通讯。
- TCP提供全双工通信,适合大数据量的传输。
- 面向字节流的通信。
- 需要通过三次握手建立链接,速度稍慢。
4. 通讯过程
- 明确IP地址。
- 明确通讯端口。
- 明确使用协议。
- 建立Socket(套接字,类似于通信链接的端口,插口)通信服务。
- 建立通信数据报文Package。
- 使用Socket来发送或接收报文。
UDP协议相关API
1. DatagramSocket类
概述
该类用于UDP通讯套接字的建立。
构造器
- 绑定本地主机创建套接字
DatagramSocket()
DatagramSocket(int port)
- 绑定指定主机创建套接字(当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自指定地址的报文)
DatagramSocket(DatagramSocketImpl impl)
DatagramSocket(int port, InetAddress laddr)
DatagramSocket(SocketAddress bindaddr)
常用方法
- 将套接字绑定到特定的IP地址和端口号
void bind(SocketAddress addr)
- 关闭该套接字
void close()
- 将该套接字连接至远程地址(用于建立单向通讯)
void connect(InetAddress address, int port)
void connect(SocketAddress addr)
- 断开链接
void disconnect()
- 获取与该套接字连接的地址
InetAddress getInetAddress()
SocketAddress getRemoteSocketAddress()
- 获取与该套接字连接的端口号
int getPort()
- 获取与该套接字绑定的本地地址
InetAddress getLocalAddress()
SocketAddress getLocalSocketAddress()
- 获取与该套接字绑定的本地端口号
int getLocalPort()
- 判断套接字状态
boolean isBound()
boolean isClosed()
boolean isConnected()
- 接收和发送
void receive(DatagramPacket p)
void send(DatagramPacket p)
- 设置接收和发送缓冲区大小以及超时限制
void setReceiveBufferSize(int size)
void setSendBufferSize(int size)
void setSoTimeout(int timeout)
2. DatagramPacket类
概述
该类用于数据报文的建立。
构造器
- 建立发送报文
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)
DatagramPacket(byte[] buf, int length, SocketAddress address)
- 建立接收报文
DatagramPacket(byte[] buf, int length)
DatagramPacket(byte[] buf, int offset, int length)
常用方法
- 获取该数据报发送的目的地址或接收的源地址。
InetAddress getAddress()
SocketAddress getSocketAddress()
int getPort()
- 获取数据报数据长度和偏移量
int getLength()
int getOffset()
- 设置数据报发送地址
void setAddress(InetAddress iaddr)
void setSocketAddress(SocketAddress address)
void setPort(int iport)
- 设置数据报数据
void setData(byte[] buf)
void setData(byte[] buf, int offset, int length)
void setLength(int length)
3. 通信步骤
发送端
- 建立套接字。
- 明确目标地址和端口号,建立带地址的数据报文。
- 使用套接字发送数据报文。
- 关闭套接字。
接收端
- 明确端口号,建立带端口号套接字。
- 建立接收的数据报文。
- 使用套接字接收数据报文。
- 关闭套接字。
4. 示例
UDP发送端
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDP_Send {
public static void main(String[] args) throws IOException {
/*
* UDP发送端
*/
System.out.println( "UDP发送端");
// 获取本地IP地址
InetAddress ip = InetAddress.getLocalHost();
System.out.println( "Destination IP = " + ip.getHostAddress());
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket();
// 2. 将数据封装到数据包中
byte[] buf = "hello UDP!".getBytes();
DatagramPacket dp = new DatagramPacket( buf, buf.length , ip , 22222);
// 3. 使用Socket对象的send方法将数据包发送出去
ds.send(dp);
// 4. 关闭资源。
ds.close();
}
}
UDP接收端
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDP_Receive {
public static void main(String[] args) throws IOException {
/*
* UDP接收端
*/
System.out.println( "UDP接收端");
// 1. 建立UDP的socket
DatagramSocket ds = new DatagramSocket(22222);
// 2. 定义数据包方便接收数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
// 3. 使用Socket对象的receive方法阻塞接收数据
ds.receive(dp);
// 4. 获取接收到的数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String data = new String( dp.getData(), 0, dp.getLength());
System.out.println( "ip = " + ip + "\nport = " + port + "\ndata = " + data );
// 4. 关闭资源。
ds.close();
}
}
实例
聊天室程序
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Test_ChatByUDP {
public static void main(String[] args) throws SocketException, UnknownHostException {
// 创建用于发送和接收的套接字,端口33333只用于聊天室接收程序。
DatagramSocket sendDs = new DatagramSocket();
DatagramSocket receDs = new DatagramSocket(33333);
// 创建广播地址
InetAddress ip = InetAddress.getByName("192.168.1.255");
// 创建发送和接收对象
Send sd = new Send( sendDs, ip);
Rece rc = new Rece( receDs);
new Thread( sd).start();
new Thread( rc).start();
}
}
class Send implements Runnable {
private DatagramSocket ds;
private InetAddress ip;
public Send(DatagramSocket ds, InetAddress ip) {
super();
this.ds = ds;
this.ip = ip;
}
@Override
public void run() {
// 读取键盘录入
BufferedReader br = new BufferedReader( new InputStreamReader(System.in ));
String str = null;
try {
while (( str = br.readLine()) != null) {
// 发送键盘录入的字符
DatagramPacket dp = new DatagramPacket(str.getBytes(), str .length(), ip , 33333);
ds.send( dp);
if ( "exit".equals(str )) {
break;
} else if ( "close".equals(str )) {
break;
}
}
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
while ( true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket( buf, buf.length );
try {
// 阻塞接收
ds.receive( dp);
} catch (IOException e) {
e.printStackTrace();
}
// 获取发送方地址
String ip = dp.getAddress().getHostAddress();
String str = new String( dp.getData(), 0, dp.getLength());
int port = dp.getPort();
if ("exit".equals( str)) {
// 判断发送字符
System. out.println(ip + ":" + port + " 离开了聊天室!" );
} else if ("close".equals( str)) {
System. out.println(ip + ":" + port + " 请求关闭聊天室!" );
break;
} else {
System. out.println(ip + ":" + port + " 说:" + str);
}
}
ds.close();
System.out.println( "聊天室已关闭!" );
}
}