网络编程基础类
InetAddress类
- java.net.InetAddress类用来封装计算机的IP地址和DNS(没有端口信息),它包括一个主机名和一个ip地址,是java对IP地址的高层表示。大多数其他网络类都要用到这个类,包括Sorket、ServerSocker、URL、DatagramSorket、DatagramPacket等
- 常用静态方法
-
- getLocalHost()得到本机的InetAddress对象,其中封装了IP地址和主机名
- getByName(String host)传入目标主机的名字或IP地址得到对应的InetAddress对象,其中封装了IP地址和主机名(底层会自动连接DNS服务器进行域名解析)
-
- getHostAddress() 获取IP地址
- getHostName() 获取主机名/域名
public class Test01 {
public static void main(String[] args) throws UnknownHostException {
//获取本机InetAddress对象(包含ip地址和封装对象)
InetAddress ia = InetAddress.getLocalHost();
//获取本机ip地址
String ip = ia.getHostAddress();
//获取本机主机名
String hostName = ia.getHostName();
System.out.println(ip + " -> " + hostName); //10.6.43.36 -> DESKTOP-D4BI28V
//通过getByName(String host) 获取指定地址的InetAddress对象
InetAddress baidu = InetAddress.getByName("baidu.com");
System.out.println(baidu.getHostAddress()); //39.156.66.10
System.out.println(baidu.getHostName()); //baidu.com
}
}
URL类
- URL由4部分组长城:协议、存放资源的主机域名、端口号、资源文件名。未指定端口号则使用协议默认的端口
- 标准格式 <协议>://<域名/IP>:<端口号>/<路径>
-
- <协议>://<域名/IP>是必须的
- <端口号>/<路径>有时可省略
- 为了方便程序员编程,JDK中提供了URL类,该类的全名是java.net.URL,该类封装了大量复杂的涉及从远程站点获取信息的细节,可以使用它的各种方法来对URL对象进行分割、合并等处理
-
-
- Url url = new URL(String url);
public class Test01 {
public static void main(String[] args) throws UnknownHostException, MalformedURLException {
URL url = new URL("https://www.baidu.com/s?wd=%E5%BC%A0%E4%B8%89&rsv_spt=1&rsv_iqid=0x901ef519004b0a02&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=&tn=baiduhome_pg&ch=&rsv_enter=1&rsv_btype=i&rsv_dl=ib&inputT=2117");
//获取协议
String protocol = url.getProtocol();
System.out.println("协议" + protocol);
//获取域名
String host = url.getHost();
System.out.println("域名" + host);
//获取默认端口
int defaultPort = url.getDefaultPort();
System.out.println("默认端口" + defaultPort);
//获取端口
int port = url.getPort();
System.out.println("端口" + port);
//获取路径
String path = url.getPath();
System.out.println("路径" + path);
//获取资源
String file = url.getFile();
System.out.println("资源" + file);
//获取数据
String query = url.getQuery();
System.out.println("数据" + query);
//获取锚点
String ref = url.getRef();
System.out.println("锚点" + ref);
}
}
-
- 使用URL类的openStream()方法可以打开到此URL的连接并返回一个用于从该链接读入的InputStream,实现最简单的网络爬虫
public class Test01 {
public static void main(String[] args){
//获取URL对象指向tianqi.qq.com
URL url = null;
InputStream is = null;
BufferedReader br = null;
try {
url = new URL("https://tianqi.qq.com/");
//获取简单输入流
is = url.openStream();
//通过转换流获取包装流
br = new BufferedReader(new InputStreamReader(is));
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
finally {
//关闭流
if (is != null) {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
TCP协议用到的类
Socket套接字类
- 我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接Socket来进行分离。
- 套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据。而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次工作。
- Socket实际是传输层供给应用层的编程接口。Socket就是应用层与传输层之间的桥梁。使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
- TCP协议和UDP协议是传输层的两种协议。Socket是传输层供给应用层的编程接口,所以Socket编程就分为TCP编程和UDP编程两类。
构造方法
- public Socket(InetAddress a,int p)
- public Socket(String ip,int p)
实例方法
ServerSocket类
- ServerSocket类用于实现服务器套接字(Server服务端)。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果
构造方法
-
- public ServerSocket(int port)
实例方法
UDP协议用到的的类
DatagramPacket类
- DatagramSocket类作为基于UDP协议的Socket,使用DatagramSocket类可以用于接收和发送数据,同时创建接收端时还需指定端口号
- 构造方法
-
- receive(DatagramPacket p)
DatagramPacket类
- DatagramPacket类负责把发送的数据打包(打包的数据为byte类型的数组),并且创建发送端时需指定接收端的IP地址和端口
- 构造方法
-
- DatagramPacket(byte bufp[],int offset,int length)
-
- DatagramPacket(byte bufp[],int offset,int length,InetAddress address,int port)
-
- public synchronized byte[] getDate()
-
- public synchronized int getLength()
基于TCP协议的程序
服务端与客户端的单项通讯(client -> server)
public class Server {
public static void main(String[] args) {
//新建ServerSocket对象
ServerSocket server = null;
Socket client = null;
BufferedReader br = null;
try {
int port = 8888;
server = new ServerSocket(port);
System.out.println("服务器启动成功,端口号为" + port);
//服务端开始侦听并接收请求,获取到来自服务端的Socket对象
client = server.accept();
System.out.println("已经与" + client.getInetAddress() + ':' + client.getPort() + "建立连接");
//获取服务端输入流
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println(br);
String str = null;
while ((str = br.readLine()) != null) {
System.out.println("客户端:");
System.out.println(str);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (server != null) {
try {
server.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
public class Client {
public static void main(String[] args) {
//创建客户端Socket对象
Socket client = null;
BufferedWriter bw = null;
try {
InetAddress localHost = InetAddress.getLocalHost();
client = new Socket(localHost,8888);
//client = new Socket("127.0.0.1",8888); 传ip地址也可以
//转换为增强字符流
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
Scanner sc = new Scanner(System.in);
//发送内容
while (true) {
bw.write(sc.next() + '\n');
bw.flush();
Thread.sleep(1000L);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (client != null) {
try {
client.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 细节:readLine()是读一行,因此必须有换行符才能连续读取
服务端客户端双向通信(客户端--->服务端-->客户端)
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket clientSocket = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
BufferedWriter bw = null;
try {
//新建服务器对象
serverSocket = new ServerSocket(8888);
//开始侦听8888端口接受连接并返回Socket套接字对象
clientSocket = serverSocket.accept();
System.out.println("已经连接到" + clientSocket.getInetAddress() + ':' + clientSocket.getPort());
//获取缓冲字节输入/出流,缓冲字符输出流
bis = new BufferedInputStream(clientSocket.getInputStream());
bos = new BufferedOutputStream(new FileOutputStream("C:\\temp\\temp2.png"));
bw = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
byte[] bytes = new byte[1024];
int i = 0;
int len = 0;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
//刷新缓冲流
bos.flush();
}
bw.write("传输成功");
//传输完成,返回信息
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (serverSocket == null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (clientSocket != null) {
try {
clientSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
public class Client {
public static void main(String[] args) {
Socket clientSocket = null;
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
BufferedReader br = null;
try {
//创建客户端Socket
clientSocket = new Socket(InetAddress.getLocalHost(),8888);
//获取缓冲输出/入字节流,缓冲字符输入流
bis = new BufferedInputStream(new FileInputStream(new File("C:\\temp\\temp1.png")));
bos = new BufferedOutputStream(clientSocket.getOutputStream());
br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
byte[] bytes = new byte[1024];
//发送图片
int len = 0;
while ((len = bis.read(bytes)) != -1) {
//发送图片
bos.write(bytes,0,len);
//刷新缓冲流
bos.flush();
len = bis.read(bytes);
}
//输出结束,Server端停止接收数据
clientSocket.shutdownOutput();
String str = null;
//等待接收消息
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (clientSocket != null) {
try {
clientSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
-
- 服务端的输入流是从客户端得到的,客户端while循环结束后,服务器仍在监听客户端,等待客户端发送消息,因此客户端不会继续往下执行,必须调用shutdownOutput()方法,声明输出流已停止输出,客户端才会继续往下执行
基于UDP协议的程序
public class Recive {
public static void main(String[] args) {
DatagramSocket recive = null;
DatagramPacket datagramPacket = null;
try {
//新建接收端DatagramSocket对象
recive = new DatagramSocket(8888);
//创建数据包
byte[] bytes = new byte[1024];
datagramPacket = new DatagramPacket(bytes,0,bytes.length);
//接收数据报
recive.receive(datagramPacket);
//转实际长度
System.out.println(new String(bytes,0,datagramPacket.getLength()));
} catch (SocketException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (recive != null) {
recive.close();
}
}
}
}
public class Send {
public static void main(String[] args) {
DatagramSocket send = null;
DatagramPacket datagramPacket = null;
try {
//新建发送端DatagramSocket对象
send = new DatagramSocket();
//准备要发送的数据
byte[] bytes = "Hello,world".getBytes();
//创建发送包
datagramPacket = new DatagramPacket(bytes,0,bytes.length,InetAddress.getByName("127.0.0.1"),8888);
//发送数据
send.send(datagramPacket);
} catch (SocketException e) {
throw new RuntimeException(e);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (send != null) {
send.close();
}
}
}
}