网络编程
文章内容来源于疯狂Java讲义(李刚)
1. Java的网络支持
1.1 InetAddress
Java为网络提供了java.net包。
Java提供了InetAddress类来代表IP地址,InetAddress下还有2个子类:Inet4Addres、Inet6Address,它们分别代表Intermet Protocol version 4(IPv4)地址和Intermet Protocol version 6(IPv6)地址。
InetAddress没有构造器,而是提供了两个静态方法获取InetAddress实例。
- getByName(String host)
根据主机获取InetAddress对象 - getByAddress(byte[] addr)
根据原始IP地址获取InetAddress
package com.xh.test01;
import java.io.IOException;
import java.net.InetAddress;
public class InetAddressTest {
public static void main(String[] args) throws IOException {
//通过主机名获取InetAddress实例
InetAddress ip = InetAddress.getByName("www.baidu.com");
//判断是否可达
System.out.println("baidu是否可达?"+ip.isReachable(2000));
//获取改InetAddress实例的ip字符串
System.out.println("ip address: "+ip.getHostAddress());
//通过原始ip地址获取InetAddress实例
InetAddress local = InetAddress.getByAddress(new byte[]{127,0,0,1});
//判断是否可达
System.out.println("local是否可达?"+ip.isReachable(2000));
//获取全限定名
System.out.println(local.getCanonicalHostName());
}
}
上面程序简单地示范了InetAddress类几个方法用法,InetAddress类本身并没有提供太多功能,它代表一个IP地址对象,是网络通信的基础,在后面介绍中将大量使用该类。
1.2 URLDecoder和URLEncoder
URLDecoder和URLEncoder用于完成普通字符串和application/x-www-fom-urlencoded MIME字符串之间的相互转换。
当URL地址里包含非西欧字符的字符串时,系统会将这些非西欧字符串转换成特殊字符串。那么编程过程中可能涉及将普通字符串和这种特殊字符串的相关转换,这就需要使用URLDecoder和URLEncoder类,其中:URLDecoder 类包含一个decode(String s,String enc)静态方法,将乱码转换为普通字符串;URLEncoder类包含一个encode(String s,String enc)静态方法,将普通字符串转换为乱码。
1.3 URL和URLConnection
**URL:**统一资源定位符,指向互联网资源的指针,可以提供该资源的路径。它是一种具体的URI,不仅标识了一个资源,还提供了该资源的路径。资源可以是简单的目录或文件,也可以是复杂的引用。通常,URL可以由协议名、主机、端口和资源组成。
ps:URI是统一资源标识符,可以唯一标识一个资源。不能定位,只能解析。
1.3.1 多线程下载例子
2. 基于TCP协议的网络编程
TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。
2.1 ServletSocket创建TCP服务器端
两个实体通信在没有建立通信之前,需要有一个实体处于出动接受状态,即主动接收来自其他通信的实体请求。
Java中能接受其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。ServerSocket包含一个监听来自客户端连接请求的方法:Socker accept()
2.2 Socket进行通信
2.2.1 单线程版本
客户端通常可使用Socket的构造器来连接到指定服务器。构造器中指定远程主机时既可使用InetAddress来指定,也可直接使用String对象来指定,但程序通常使用String对象(如192.168.2.23)来指定远程IP。
当两端都有Socket之后,就进行通信。通信方式可以使用输入流和输出流实现。InputStream(让程序通过输入流向Socket中取出数据)和OutputStream(让程序通过输出流向Socket中输出数据)。
ps:
输入流 程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道
输出流 程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
Server:
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//创建一个ServerSocket,用于监听客户端的Socket的请求
ServerSocket ss = new ServerSocket(30000);
//循环等待
while (true){
//一旦接收到客户端的Socket请求,服务端也产生一个Socket
Socket s = ss.accept();
//将Socket的输出流包装到PrintStream中
PrintStream ps = new PrintStream(s.getOutputStream());
ps.println("加油");
ps.close();
s.close();
}
}
}
Client:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//创建一个目标是本地主机,端口号是30000的Socket
Socket s = new Socket("127.0.0.1",30000);
//将输入流包装到BufferedReader中
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();
System.out.println("来自服务器的数据:"+line);
br.close();
s.close();
}
}
2.2.1 多线程版本
MyServer:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MyServer {
public static List<Socket> socketList = new ArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(30002);
while (true){
Socket s = ss.accept();
socketList.add(s);
new Thread(new ServerThread(s)).start();
}
}
}
ServerThread:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerThread implements Runnable {
Socket s = null;
BufferedReader br = null;
public ServerThread(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
}
@Override
public void run() {
try{
String content = null;
while ((content = readFromClient())!=null){
for(Socket s : MyServer.socketList){
PrintStream ps = new PrintStream(s.getOutputStream());
ps.println(content);
}
}}catch (Exception e){
e.printStackTrace();
}
}
private String readFromClient(){
try{
return br.readLine();
}catch (Exception e){
MyServer.socketList.remove(s);
}
return null;
}
}
MyClient:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class MyClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket();
new Thread(new ClientThread(s)).start();
PrintStream ps = new PrintStream(s.getOutputStream());
String line = null;
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
while ((line = bf.readLine()) != null){
ps.println(line);
}
}
}
ClientThread:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientThread implements Runnable {
private Socket s;
BufferedReader br = null;
public ClientThread(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
}
@Override
public void run() {
try{
String content = null;
while ((content = br.readLine())!=null){
System.out.println(content);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
ps:IO流