IP和端口
Java中使用 java.net.InetAddress 这个类描述IP地址。该类的常用方法如下:
| 方法 | 描述 |
|---|---|
| public static InetAddress getLocalHost() throws UnknowHostException | 获取本地主机的 InetAddress 对象 |
| public static InetAddress getByName(String host) throws UnknowHostException | 通过主机名称创建 InetAddress 对象 |
| String getHostName() | 获取主机名称 |
| public String getHostAddress() | 获取主机 IP 地址 |
| public static InetAddress getByAddress(String host, byte[] addr) throws UnknowHostException | 通过主机名称和IP地址创建 InetAddress对象 |
| public static InetAddress getByAddress(byte[] addr) throws UnknowHostException | 通过IP地址创建 InetAddress 对象 |
public class Main {
public static void main(String[] args) throws UnknownHostException {
InetAddress inetAddress = InetAddress.getLocalHost();
System.out.println("主机IP地址:" + inetAddress.getHostAddress());
System.out.println("主机名称:" + inetAddress.getHostName());
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress2 = InetAddress.getByAddress(new byte[]{'a','b','c','d'});
System.out.println(inetAddress2);
}
}
如果把IP比作一栋大厦的地址,那么端口 ( port)就是不同房间的门牌号。IP地址需要和端口结合起来使用,好比快递小哥必须要通过大厦地址和房间号才能准确找到你。计算机主机就相当于大厦,网络中的请求需要通过IP地址来找到主机,同时一台主机上会同时运行很多个服务,如何把不同的请求分配给不同的服务就需要使用到端口了。
URL 和 URLConnection
通常讲的网络资源实际是指网络中真实存在的一个实体,比如文字、图片、视频、音频等,如果我们要在程序获取网络实体,应该怎么做呢?可以用URI (Uniform ResourceIdentifier)统一资源定位符指向目标实体,URI的作用是用特定的语法来标识某个网络资源。Java中专门封装了一个类用来描述URI,这个类就是 java.net.URI,使用URI 的实例化对象,可以用面向对象的方式来管理网络资源。
public class Main {
public static void main(String[] args) throws URISyntaxException {
URI uri = new URI("http://localhost:8080/index/login.html");
System.out.println(uri.getHost());
System.out.println(uri.getPort());
System.out.println(uri.getPath());
}
}
// 输出
localhost
8080
/index/login.html
URL (Uniform Resource Locator)是统一资源位置,在URI的基础上进行了扩充,在定位资源的同时还提供了对应的网络地址,Java也对URL进行了封装,java.net.URL类常用方法如下。
| 方法 | 描述 |
|---|---|
| public URL(String protocol, String host, int port, String file) throws MalformedURLException | 根据协议、IP地址、端口号、资源名称获取URL对象 |
| public final InputStream openStream() throws java.io.IOException | 获取输入流对象 |
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
Reader reader = null;
BufferedReader bufferedReader = null;
try {
URL url = new URL("http","127.0.0.1",80,"/URLtest/index.html");
inputStream = url.openStream();
reader = new InputStreamReader(inputStream);
bufferedReader = new BufferedReader(reader);
String str = null;
while ((str = bufferedReader.readLine()) != null){
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
bufferedReader.close();
reader.close();
inputStream.close();
}
}
}
// 输出
<html>
<head>
<title>URL测试页面</title>
</head>
<body>
<h1>url1</h1>
<h2>url2</h2>
<h3>url3</h3>
</body>
</html>
同时也可以通过URL获取资源的其他信息,如主机地址,端口等。
public class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("http","127.0.0.1",80,"/URLtest/index.html");
System.out.println("url中的IP地址:" + url.getHost());
System.out.println("url中的资源地址:" + url.getPath());
System.out.println("url访问的端口:" + url.getPort());
}
}
// 输出
url中的IP地址:127.0.0.1
url中的资源地址:/URLtest/index.html
url访问的端口:80
URLConnection
URLConnection用来描述 URL 指定资源的连接,是一个抽象类,常用的子类有HttpURLConnection,URLConnection 底层是通过HTTP协议来处理的,它定义了访问远程网络资源的方法。通过URLConnection可以获取到URL资源的相关信息,该类常用的方法如下所示。
| 方法 | 描述 |
|---|---|
| public int getContentLength() | 返回资源的长度,返回值为 int 类型 |
| public long getContentLengthLong() | 返回资源的长度,返回值为 long 类型 |
| public String getContentType() | 返回资源的类型 |
| public abstract void connect() throws IOException | 判断连接的打开或关闭状态 |
| public Inputstream getInputStream() throws IOException | 获取输入流对象 |
public class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("http://localhost/URLtest/index.html");
URLConnection urlConnection = url.openConnection();
System.out.println(urlConnection.getURL());
System.out.println(urlConnection.getContentLength());
System.out.println(urlConnection.getContentLengthLong());
System.out.println(urlConnection.getContentType());
System.out.println("----- 网页资源内容 -----");
InputStream inputStream = urlConnection.getInputStream();
Reader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
String str = null;
while ((str = bufferedReader.readLine()) != null){
System.out.println(str);
}
}
}
// 输出
http://localhost/URLtest/index.html
130
130
text/html
----- 网页资源内容 -----
<html>
<head>
<title>URL测试页面</title>
</head>
<body>
<h1>url1</h1>
<h2>url2</h2>
<h3>url3</h3>
</body>
</html>
TCP协议
Java通过Socket来完成TCP程序的开发,Socket是一个类,使用该类可以在服务端与客户端之间建立可靠的连接。在实际开发中,Socket表示客户端,服务端使用ServerSocket来表示,ServerSocket也是一个类,ServerSocket和 Socket都存放在java.net包中。具体的开发思路是在服务端创建ServerSocket对象,然后通过该对象的 accept()方法可以接收到若干个表示客户端的Sokcet对象。
ServerSocket类的常用方法
| 方法 | 描述 |
|---|---|
| public ServerSocket(int port) throws IOException | 根据端口创建ServerSocket实例对象 |
| public ServerSocket(int port, int backlog) throws IOException | 根据端口和backlog创建ServerSocket实例对象 |
| public ServerSocket(int port,int backlog, InetAddress address) throws IOException | 根据端口、backlog和IP地址创建ServerSocket实例对象 |
| public ServerSocket() throws IOException | 创建没有绑定服务器的ServerSocket实例对象 |
| public synchronized int getSoTimeout() throws IOException | 获取Sotimeout的设置 |
| public InetAddress getInetAddress() | 获取服务器的IP地址 |
| public Socket accept() throws IOException | 等待客户端请求,并返回Socket对象 |
| public void close() throws IOException | 关闭ServerSocket |
| public boolean isClosed() | 返回ServerSocket的关闭状态 |
| public void bind(SocketAddress endpoint) throws IOException | 将ServerSocket实例对象绑定到指定地址 |
| public int getLocalPort() | 返回ServerSocket的端口 |
Socket类的常用方法
| 方法 | 描述 |
|---|---|
| public Socket(String host,int port) throws IOException,UnknowHostException | 根据主机,端口创建要连接的Socket对象 |
| public Socket(InetAddress host,int port) throws IOException | 根据IP地址,端口创建要连接的Socket对象 |
| public Socket(String host,int port,InetAddress localAddress, int localPort) throws IOException | 根据主机,端口创建要连接的Socket对象并将其连接到指定的远程主机上的指定端口 |
| public Socket(InetAddress host,int port,InetAddress localAddress,int localPort) throws IOException | 根据主机,端口创建要连接的Socket对象并将其连接到指定远程地址上的指定端口 |
| public Socket() | 创建没有连接的Socket对象 |
| public InputStream getInputStream() throws IOException | 返回Socket的输入流 |
| public synchronized void close() throws IOException | 关闭Socket |
| public boolean isClose() | 返回Socket的关闭状态 |
首先启动ServerSocket,等待接收客户端请求,当接收到客户端请求后,打印信息,并向客户端返回信息。
// 服务端
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
Socket socket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
DataOutputStream dataOutputStream = null;
DataInputStream dataInputStream = null;
Scanner scanner = new Scanner(System.in);
try{
serverSocket = new ServerSocket(8081);
System.out.println("------ 服务器 ------");
System.out.println("服务端已启动,等待接收客户端请求 ... ");
while (true){
socket = serverSocket.accept();
inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
String request = dataInputStream.readUTF();
System.out.println("接收到了客户端的请求:" + request);
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
System.out.print("请输入发送给客户端的信息:");
String response = scanner.next();
dataOutputStream.writeUTF(response);
System.out.println("发送给客户端的信息:" + response);
}
} catch (IOException e){
e.printStackTrace();
} finally {
dataOutputStream.close();
outputStream.close();
dataInputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = null;
OutputStream outputStream = null;
DataOutputStream dataOutputStream = null;
InputStream inputStream = null;
DataInputStream dataInputStream = null;
Scanner scanner = new Scanner(System.in);
try{
socket = new Socket("127.0.0.1", 8081);
System.out.println("------客户端------");
System.out.print("请输入发送给服务端的信息:");
String request = scanner.next();
System.out.println("客户端发送:" + request);
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF(request);
inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
String response = dataInputStream.readUTF();
System.out.println("服务端响应:" + response);
} catch (IOException e){
e.printStackTrace();
} finally {
dataInputStream.close();
inputStream.close();
dataOutputStream.close();
outputStream.close();
socket.close();
}
}
}
UDP协议
TCP协议可以建立稳定可靠的连接,保证数据的完整。但是TCP协议的缺点也很明显,先建立可靠连接,再进行操作的方式必然会造成系统运行效率低下。在实际开发中,某些业务场景对系统的运行效率要求较高,使用TCP协议很显然就不合适了,这时候就需要使用另外一种传输协议 -— UDP。
UDP所有的连接都是不可靠的,即不需要建立连接,直接发送数据即可。例如通过微信聊天,发送方只需要把信息发出去,接收方可能因为网络差或者其他原因没有收到信息,但是发送方并不会因为接收方无法接收到信息而等待,它可以连续发送数据。所以UDP的速度更快,但是可能会造成数据丢失,安全性不高,追求速度的应用可以选择UDP。例如语音聊天或者视频聊天,对于这类应用流畅性更重要,偶尔丢失几个数据包并不会有太大影响。Java提供了DatagramSocket类和 DatagramPacket类,来帮助开发者编写基于UDP协议的程序。
DatagramSocket类的常用方法
| 方法 | 描述 |
|---|---|
| public DatagramSocket(int port) throws SocketException | 根据端口创建DatagramSocket实例对象 |
| public void send(DatagramPacket p) throws IOException | 发送数据报 |
| public synchronized void receive(DatagramPacket p) throws IOException | 接收数据报 |
| public InetAddress getInetAddress() | 获取DatagramSocket对应的 InetAddress对象 |
| public boolean isConnected() | 判断是否连接到服务 |
| 方法 | 描述 |
|---|---|
| public DatagramPacket(byte buf[], int length, InetAddress address, int port) | 根据发送的数据,数据长度,IP地址,端口创建DatagramPacket实例对象 |
| public synchronized byte[] getData() | 获取接收的数据 |
| public synchronized int getLength() | 获取数据长度 |
| public synchronized int getPort() | 获取发送数据的Socket端口 |
| public synchronized SocketAddress getSocketAddress() | 获取发送数据的Socket信息 |
public class UserA {
public static void main(String[] args) throws IOException {
// 接收数据
byte[] buf = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length);
DatagramSocket datagramSocket = new DatagramSocket(8888);
datagramSocket.receive(datagramPacket);
String message = new String(datagramPacket.getData(),0,datagramPacket.getLength());
System.out.println("我是UserA,收到了 " + datagramPacket.getPort() + " 发送的数据:" + message);
// 发送数据
String reply = "我是UserA,收到";
SocketAddress socketAddress = datagramPacket.getSocketAddress();
DatagramPacket datagramPacket1 = new DatagramPacket(reply.getBytes(), reply.getBytes().length,socketAddress);
datagramSocket.send(datagramPacket1);
}
}
public class UserB {
public static void main(String[] args) throws IOException {
String message = "我是UserB,UserA你好!";
// 发送数据
InetAddress inetAddress = InetAddress.getByName("localhost");
DatagramPacket datagramPacket = new DatagramPacket(message.getBytes(), message.getBytes().length,inetAddress,8888);
DatagramSocket datagramSocket = new DatagramSocket(6666);
datagramSocket.send(datagramPacket);
// 接收数据
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket1 = new DatagramPacket(bytes, bytes.length);
datagramSocket.receive(datagramPacket1);
String reply = new String(datagramPacket1.getData(),0, datagramPacket1.getLength());
System.out.println("我是UserB,收到了 " + datagramPacket1.getPort() + " 的数据:" + reply);
}
}
多线程下的网络编程
public class ServerRunnable implements Runnable{
private Socket socket;
public ServerRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
DataInputStream dataInputStream = null;
try {
inputStream = this.socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
String message = dataInputStream.readUTF();
System.out.println(message);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
dataInputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class ServerThread {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
serverSocket = new ServerSocket(9999);
System.out.println("服务端启动 ... ");
while (true){
Socket socket = serverSocket.accept();
new Thread(new ServerRunnable(socket)).start();
}
}
}
public class ClientRunnable implements Runnable{
private int num;
public ClientRunnable(int num){
this.num = num;
}
@Override
public void run() {
Socket socket = null;
OutputStream outputStream = null;
DataOutputStream dataOutputStream = null;
try {
socket = new Socket("localhost", 9999);
String message = "我是客户端 " + this.num;
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF(message);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
dataOutputStream.close();
outputStream.close();
socket.close();
} catch (IOException e) {
}
}
}
}
public class ClientThread {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new ClientRunnable(i)).start();
}
}
}
简单的聊天小程序
public class SocketRunnable implements Runnable{
private Socket socket;
public SocketRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
DataInputStream dataInputStream = null;
Scanner scanner = new Scanner(System.in);
String message = null;
try {
while (true){
// 读
inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
message = dataInputStream.readUTF();
System.out.println("客户端:" + message);
// 写
System.out.print("服务端:");
message = scanner.next();
OutputStream outputStream = null;
DataOutputStream dataOutputStream = null;
try {
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF(message);
} catch (IOException e){
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ServerTalk {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8088);
Socket socket = null;
System.out.println("服务器已启动......");
while (true){
socket = serverSocket.accept();
new Thread(new SocketRunnable(socket)).start();
}
}
}
public class ClientTalk {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8088);
System.out.println("客户端已启动......");
Scanner scanner = new Scanner(System.in);
String message = null;
while (true){
// 写
System.out.print("客户端:");
message = scanner.next();
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF(message);
// 读
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
message = dataInputStream.readUTF();
System.out.println("服务端:" + message);
}
}
}