URL对象
URL统一资源标识符,在Java中通常放在URL对象中使用。URL对象通常需要包含:①协议②地址③资源。
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
URL url;// URL对象
Thread th;// 线程对象
Look lk = new Look();// 目标对象
System.out.println("从键盘输入URL资源:");
Scanner sc = new Scanner(System.in);
String source = sc.nextLine();// 把输入的这行资源读为字符串
try {
// 利用输入的url构造一个URL对象
url = new URL(source);
lk.setURL(url);// 传给线程的目标对象
th = new Thread(lk);// 用目标对象去构造线程
th.start();// 启动这个子线程
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
class Look implements Runnable {
URL url;
// 获取外部传入的URL
void setURL(URL url) {
this.url = url;
}
// URL资源的读取可能引起阻塞,所以需要在一个线程中读取URL资源
public void run() {
try {
// 返回一个指向URL对象所包含的资源的输入流
InputStream is = url.openStream();
byte[] b = new byte[1024];// 用适当大的字节数组从输入流读信息
int n = -1;
while ((n = is.read(b)) != -1)// 成功读到信息,返回信息的长度
{
String str = new String(b, 0, n);// 截取从0~结尾的信息变成字符串
System.out.print(str);// 输出,不换行,流式信息内该换行的地方自会有换行符
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出(部分)
从键盘输入URL资源:
http://blog.csdn.net/SHU15121856
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="canonical" href="http://blog.csdn.net/SHU15121856" />
......
InetAddress
两种表示因特网上的主机的方式:①域名②ip地址。InetAddress类用其类方法InetAddress.getByName()
获取一个包含域名和ip地址的InetAddress对象:
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Main {
public static void main(String[] args) {
try {
// 通过域名表示主机所在地址,获得InetAddress对象
InetAddress ia1 = InetAddress.getByName("www.baidu.com");
// 通过ip地址表示主机所在地址,获得InetAddress对象
InetAddress ia2 = InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, 0, 108 });
// 获得本机InetAddress对象
InetAddress ia3 = InetAddress.getLocalHost();
// getHostNmae()可以获取该对象所包含的主机名
// getHostAddresss()可以获取该对象包含的ip地址
System.out.println("ia1对应的主机名" + ia1.getHostName() + ",ip地址" + ia1.getHostAddress());
System.out.println("ia2对应的主机名" + ia2.getHostName() + ",ip地址" + ia2.getHostAddress());
System.out.println("ia3对应的主机名" + ia3.getHostName() + ",ip地址" + ia3.getHostAddress());
} catch (UnknownHostException e) {
// 当没法找到这个地址时会抛掷UnknownHostException异常
e.printStackTrace();
}
}
}
注意Linux下使用InetAddress.getLocalHost()
只会返回主机名和127.0.0.1,因为是直接去读了/etc/hosts
文件。另外,域名是通过DNS(域名服务器)转换成ip地址使用的,InetAddress.getByName()
也可以直接传ip地址使用。
因为InetAddress.getByAddress()
传入的是一个Byte类型的数组,对于ipv4只能是4字节长,对于ipv6只能是12字节长。此外因为Byte类型是从-128~127,对于ipv4点分十进制大于127的数字要强制转换一下。
认识Socket
简述
网络上的通信是进程和进程之间的通信,在网际层有ip地址,在传输层确定用TCP还是UDP传输数据,在应用层确定更精确的通信协议和标识进程的端口号。
Socket本质是提供给程序员开发用的网络编程API,从描述ip地址和端口角度来看,也可以称套接字(端口号和ip地址的组合)。
在Java里,客户端用Socket类建立连接到服务器的Socket对象,服务端用ServerSocket类的对象的.accept()
方法建立与客户端相连接的服务端的Socket对象,两者才能通信。
端口号从1~65535(0保留),自己使用时,应使用1024~65535,前面的端口被实现定义的服务使用。
客户端首先需要知道服务器的地址和端口号,反之则不需要。服务器也可以从收到的户端通信消息中获取其地址信息。
两类阻塞
①服务器端的ServerSocket对象调用.accept()
方法会阻塞本线程的执行,直到接收到客户端Socket的呼叫,才能建立连接并返回服务器端Socket对象。
②客户端和服务端两者的Socket建立连接后,各自使用.getOutputStream()
获得输出流,使用.getInputStream()
获得输入流,一方的输入流作为另一方的输出流(连接的实质):
不同于文件的读写,在Socket通信时,读数据可能发生在另一端输出数据之前,这时会阻塞本线程,直到这个读方法读到信息。
有关InetAddress
服务器端的Socket对象通过.getInetAddress()
获取客户端的InetAddress对象;客户端的Socket对象通过同样的方法获取服务端的InetAddress对象。
程序
服务端MyServer.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
//服务端程序
public class MyServer {
public static void main(String[] args) {
ServerSocket ss;// 服务器端连接对象
Socket sck;// (服务端)Socket对象
DataInputStream dis;// 数据输入流
DataOutputStream dos;// 数据输出流
String[] strs = { "服务端发送的信息1", "服务端发送的信息2", "服务端发送的信息3" };
try {
// 建立服务器端连接对象,使用3838端口
ss = new ServerSocket(3838);
System.out.println("[+]等待客户端申请连接...");
sck = ss.accept();// 阻塞,直到连接成功,返回(服务端)Socket对象
System.out.println("[+]客户端已经连接!");
// 用.getOutputStream()建立输出流对象
dos = new DataOutputStream(sck.getOutputStream());
// 用.getInputStream()建立输入流对象
dis = new DataInputStream(sck.getInputStream());
for (String s : strs) {
// 从输入流中读取信息,即读取了客户端的输出流中的信息
String readstr = dis.readUTF();
System.out.println("[+]" + readstr);
dos.writeUTF(s);// 把要输出给客户端的信息,写入输出流
Thread.sleep(2000);// 线程休眠2秒
}
} catch (IOException e) {
System.out.println("[-]客户端提前断开");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[-]交互结束");
}
}
客户端MyClient.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
//客户端程序
public class MyClient {
public static void main(String[] args) {
Socket sck;// (客户端)Socket对象
String[] strs = { "客户端发送的信息1", "客户端发送的信息2", "客户端发送的信息3" };
DataInputStream dis;// 数据输入流
DataOutputStream dos;// 数据输出流
try {
// 客户端的Socket需要用构造方法来显式构造
// 需要提供域名和端口号,端口号需要和服务端一致
sck = new Socket("127.0.0.1", 3838);
System.out.println("[+]成功连接到服务端");
// 用.getOutputStream()建立输出流对象
dos = new DataOutputStream(sck.getOutputStream());
// 用.getInputStream()建立输入流对象
dis = new DataInputStream(sck.getInputStream());
for (String s : strs) {
dos.writeUTF(s);// 把信息发送给服务端
// 从输入流中读取信息,即读取了服务端的输出流中的信息
String readstr = dis.readUTF();
System.out.println("[+]" + readstr);
Thread.sleep(1000);// 线程休眠1秒
}
} catch (UnknownHostException e) {
System.out.println("[x]无法解析域名");
} catch (IOException e) {
System.out.println("[x]无法连接到服务端");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[-]交互结束");
}
}
输出(服务端)
[+]等待客户端申请连接...
[+]客户端已经连接!
[+]客户端发送的信息1
[+]客户端发送的信息2
[+]客户端发送的信息3
[-]交互结束
输出(客户端)
[+]成功连接到服务端
[+]服务端发送的信息1
[+]服务端发送的信息2
[+]服务端发送的信息3
[-]交互结束