InetAddress
类代表IPgetByName(String host)
、 getLocalHost()
getHostName()
、 getHostAddress()
16
位的整数 0~655350~1023
。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)1024~49151
。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)49152~65535
InetAdress 代码示例
public static void main(String[] args) {
try {
InetAddress inet1 = InetAddress.getByName("202.89.233.101");
System.out.println(inet1);// /202.89.233.101
InetAddress inet2 = InetAddress.getByName("cn.bing.com");
System.out.println(inet2);// cn.bing.com/202.89.233.101
InetAddress inet3 = InetAddress.getByName("127.0.0.1");
System.out.println(inet3);// /127.0.0.1
//获取本地ip
InetAddress inet4 = InetAddress.getLocalHost();
System.out.println(inet4);// M-XX-GULJ/192.168.110.1
System.out.println(inet2.getHostName());// cn.bing.com
System.out.println(inet2.getHostAddress());// 202.89.233.101
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
问题:网络协议太复杂
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
通信协议分层的思想
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
TCP/IP协议簇
TCP(Transmission Control Protocol)
UDP(User Datagram Protocol)
传输控制协议(TCP)和网络互联协议(IP)
而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。TCP和UDP
“三次握手”
方式,点对点通信,是可靠的Socket
public Socket(InetAddress address,int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号public Socket(String host,int port)
创建一个流套接字并将其连接到指定主机上的指定端口号public InputStream getInputStream()
返回此套接字的输入流。可以用于接收网络消息public OutputStream getOutputStream()
返回此套接字的输出流。可以用于发送网络消息public InetAddress getInetAddress()
此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。public InetAddress getLocalAddress()
获取套接字绑定的本地地址。 即本端的IP地址public int getPort()
此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。public int getLocalPort()
返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。即本端的端口号。public void close()
关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和OutputStream。public void shutdownInput()
:如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将返回 EOF(文件结束符)
。 即不能在从此套接字的输入流中接收任何数据。public void shutdownOutput()
:禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。基于Socket的TCP编程
Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示:
客户端Socket的工作过程包含以下四个基本的步骤
客户端创建Socket对象
Socket(String host,int port)throws UnknownHostException,IOException
:向服务器(域名是host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。Socket(InetAddress address,int port)throws IOException
:根据InetAddress对象所表示的IP地址以及端口号port发起连接。Socket s = new Socket("192.168.110.148",9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();
服务器程序的工作过程包含以下四个基本的步骤
服务器建立ServerSocket对象
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+":"+str);
s.close();
ss.close();
示例:客户端发送信息给服务端,服务端将数据显示在控制台上
//客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 8899);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("你好,通信测试".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源的关闭
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//服务端
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建服务器端的ServerSocket,指明自己的端口号
ss = new ServerSocket(8899);
//2.调用accept()表示接收来自于客户端的socket
socket = ss.accept();
//3.获取输入流
is = socket.getInputStream();
//4.读取输入流中的数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println("收到了来自于:" + socket.getInetAddress().getHostAddress() + "的数据");
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (baos != null) {
//5.关闭资源
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
示例2:客户端发送文件给服务端,服务端将文件保存在本地
/**
* 客户端
*
* 简化异常处理,这里应该使用try-catch-finally处理
* @throws IOException
*/
@Test
public void client() throws IOException {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("hello.jpg"));
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
fis.close();
os.close();
socket.close();
}
/**
* 服务端
*
* 简化异常处理,这里应该使用try-catch-finally处理
* @throws IOException
*/
@Test
public void server() throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File("hello002.jpg"));
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
fos.close();
is.close();
socket.close();
ss.close();
}
示例3:从客户端发送文件给服务端,服务端保存到本地,并返回"数据传输完成"给客户端
/**
* 客户端
*
* 简化异常处理,这里应该使用try-catch-finally处理
* @throws IOException
*/
@Test
public void client() throws IOException {
// 准备Socket,连接服务器,需要指定服务器的IP地址和端口号
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
// 获取输出流,用来发送数据给服务端
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream(new File("hello.jpg"));
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1){
os.write(buffer,0,len);
}
//关闭数据的输出
socket.shutdownOutput();
//获取输入流,接收来自于服务器端的数据
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[20];
int len1;
while((len1 = is.read(buf)) != -1){
baos.write(buf,0,len1);
}
System.out.println(baos.toString());
// 关闭socket,不再与服务器通信,即断开与服务器的连接
// socket关闭,意味着InputStream和OutputStream也被关闭
socket.close();
}
/**
* 服务端
*
* 简化异常处理,这里应该使用try-catch-finally处理
* @throws IOException
*/
@Test
public void server() throws IOException {
// 准备一个ServerSocket
ServerSocket server = new ServerSocket(9090);
// 监听一个客户端的连接,此方法是个阻塞的方法,如果没有客户端连接,将一直等待
Socket socket = server.accept();
System.out.println("客户端连接成功");
// 获取输入流,用来接收该客户端发送给服务端的数据
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream(new File("hello003.jpg"));
// 接收数据
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer,0,len);
}
System.out.println("数据传输完成");
// 获取输出流,用来发送数据给客户端
OutputStream os = socket.getOutputStream();
// 发送数据
os.write("你好,干的漂亮!".getBytes());
// 关闭socket,不在与客户端通信,socket关闭,意味着InputStream和OutputStream也关闭
socket.close();
// 如果不再接收任何客户端通信,可以关闭ServerSocket
server.close();
}
概述
DatagramSocket
和 DatagramPacket
实现了基于 UDP 协议网络程序。DatagramSocket类的常用方法
public DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被绑定到通配符地址,IP 地址由内核来选择public DatagramSocket(int port,InetAddress laddr)
创建数据报套接字,将其绑定到指定的本地地址。本地端口必须在 0 到 65535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地址,IP 地址由内核选择public void close()
关闭此数据报套接字public void send(DatagramPacket p)
从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号public void receive(DatagramPacket p)
从此套接字接收数据报包。当此方法返回时,DatagramPacket的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的长度长,该信息将被截短public InetAddress getLocalAddress()
获取套接字绑定的本地地址public int getLocalPort()
返回此套接字绑定的本地主机上的端口号public InetAddress getInetAddress()
返回此套接字连接的地址。如果套接字未连接,则返回 nullpublic int getPort()
返回此套接字的端口。如果套接字未连接,则返回 -1DatagramPacket类的常用方法
public DatagramPacket(byte[] buf,int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.lengthpublic DatagramPacket(byte[] buf,int length,InetAddress address,int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length参数必须小于等于 buf.lengthpublic InetAddress getAddress()
返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的public int getPort()
返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的public byte[] getData()
返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度public int getLength()
返回将要发送或接收到的数据的长度UDP网络通信流程
UDP示例
//发送端
@Test
public void client() throws IOException {
DatagramSocket socket = new DatagramSocket();
String str = "UDP方式发送数据";
byte[] data = str.getBytes();
InetAddress inet = InetAddress.getLocalHost();
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
socket.send(packet);
socket.close();
}
//接收端
@Test
public void server() throws IOException {
DatagramSocket socket = new DatagramSocket(9090);
byte[] buffer = new byte[100];
DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));
socket.close();
}
URL类
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
URL类的构造方法
为了表示URL,java.net 中实现了类 URL。我们可以通过下面的构造器来初始化一个 URL 对象:
public URL (String spec)
:通过一个表示URL地址的字符串可以构造一个URL对象。
例如:URL url = new URL (“http://www. 123.com/”);
public URL(URL context, String spec)
:通过基 URL 和相对 URL 构造一个 URL 对象。
例如:URL downloadUrl = new URL(url, “download.html")
public URL(String protocol, String host, String file);
例如:new URL(“http”, “www.123.com”, “download.html");
public URL(String protocol, String host, int port, String file);
例如: URL gamelan = new URL(“http”, “www.123.com”, 80, “download.html");
URL类的构造器都声明抛出非运行时异常,必须要对这一异常进行处理,通常是用 try-catch 语句进行捕获
URL类的常用方法
一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取这些属性:
public String getProtocol( )
获取该URL的协议名public String getHost( )
获取该URL的主机名public String getPort( )
获取该URL的端口号public String getPath( )
获取该URL的文件路径public String getFile( )
获取该URL的文件名public String getQuery( )
获取该URL的查询名@Test
public void test(){
try {
URL url = new URL("https://cn.bing.com/images/search?q=%e8%b5%b5%e4%b8%bd%e9%a2%96&FORM=ISTRTH&id=537D7F316C569B43065B76F7E1AD8B18F64CC79D&cat=%E6%98%8E%E6%98%9F&lpversion=");
// 获取该URL的协议名
System.out.println("url.getProtocol():"+ url.getProtocol());
// 获取该URL的主机名
System.out.println("url.getHost():"+url.getHost());
// 获取该URL的端口号
System.out.println("url.getPort():"+url.getPort());
// 获取该URL的文件路径
System.out.println("url.getPath():"+url.getPath());
// 获取该URL的文件名
System.out.println("url.getFile():"+url.getFile());
// 获取该URL的查询名
System.out.println("url.getQuery():"+url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
URLConnection类
针对HTTP协议的URLConnection类
URL的方法 openStream():能从网络上读取数据
若希望输出数据,例如向服务器端的 CGI (公共网关接口-Common Gateway Interface-的简称,是用户浏览器和服务器端的应用程序进行连接的接口)程序发送一些数据,则必须先与URL建立连接,然后才能对其进行读写,此时需要使用URLConnection
URLConnection:表示到URL所引用的远程对象的连接。当与一个URL建立连接时,首先要在一个 URL 对象上通过方法 openConnection()生成对应的URLConnection对象。如果连接过程失败,将产生IOException
通过URLConnection对象获取的输入流和输出流,即可以与现有的CGI程序进行交互
public Object getContent( ) throws IOException
public int getContentLength( )
public String getContentType( )
public long getDate( )
public long getLastModified( )
public InputStream getInputStream( )throws IOException
public OutputSteram getOutputStream( )throws IOException
示例:读取、下载对应的url资源
@Test
public void testURL() {
HttpURLConnection urlConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL("http://t8.baidu.com/it/u=1484500186,1503043093&fm=79&app=86&f=JPEG?w=1280&h=853");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.connect();
is = urlConnection.getInputStream();
fos = new FileOutputStream("animal.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("下载完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
URI、URL和URN的区别