Java为了可移植性,不允许直接调用操作系统,而是由java.net包来提供网络功能能,JVM负责提供与操作系统的实际连接。
作用
InetAddress用于封装计算机的IP地址和DNS(无端口信息,DNS-Domain Name System是系统域名)。
特点
InetAddress类没有构造器,如果要得到对象,只能通过静态方法getLocalHost()、getByName()、getAllByName()、getAddress()、getAddress()和getHostName()实现。
使用如下:
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Study{
public static void main(String[] args) throws UnknownHostException {
//使用getLocalHost方法创建InetAddress对象
InetAddress address = InetAddress.getLocalHost();
/*getAddress()方法返回的byte数组是有符号的,在Java中byte类型的取值范围是-128〜127,如果
返回的IP地址的某个字节是大于127的整数,在byte数组中就是负数。由于Java中没有无符号byte类型因
此,要想显示正常的IP地址,必须使用int或long类型。下面代码演示了如何利用getAddress返回IP地
址,以及如何将IP地址转换成正整数形式*/
byte[] ip = address.getAddress();
for(byte temp : ip){
int Ip = (temp < 0) ? temp + 256 : temp;
System.out.print(Ip + " ");
}
System.out.println();
System.out.println(address.getHostAddress());//输出IP地址
System.out.println(address.getHostName());//输出计算机的名字
//根据域名得到InetAddress对象
InetAddress address1 = InetAddress.getByName("blog.csdn.net");
System.out.println(address1.getHostAddress());//输出csdn服务器IP
System.out.println(address1.getHostName());//输出csdn域名
//根据IP得到InetAddress对象
InetAddress address2 = InetAddress.getByName("47.95.47.253");//csdnIP
System.out.println(address2.getHostAddress());//输出csdn服务器IP
//输出IP地址而不是域名。如果这个IP地址不存在或者DNS服务器不允许进行IP地址和域名的映射,
//getHostName方法就直接返回这个IP地址
System.out.println(address2.getHostName());
}
}
InetSocketAddress用于包含IP地址和端口信息,常用于Socket通信。该类实现IP套接字地址(IP地址+端口号),不依赖任何协议。使用如下:
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
public class Study{
public static void main(String[] args) throws UnknownHostException {
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8888);
InetSocketAddress socketAddress1 = new InetSocketAddress("localhost", 9999);
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress1.getAddress());
}
}//输出如下
127.0.0.1
localhost/127.0.0.1
IP地址唯一表示了Internet上的计算机,而URL则标识了这些计算机上的资源。URL类代表一个统一资源定位符,它是指向物联网资源的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用。
JDK提供URL类,全名是java.net.URL,有了它就可以使用它的各种方法来对URL对象进行分割、合并等处理。
使用如下:
import java.net.MalformedURLException;
import java.net.URL;
public class Study{
public static void main(String[] args) throws MalformedURLException {
//正在编写的网络编程url
URL url = new URL("https://editor.csdn.net/md?articleId=107335888");
System.out.println("获取与此URL关联的协议的默认端口:" + url.getDefaultPort());
System.out.println("端口号后面的内容:" + url.getFile());
System.out.println("主机名:" + url.getHost());
System.out.println("路径:" + url.getPath());//端口号后、参数前的内容
//若blog.csdn.net:443则返回433(加了端口号),否则返回-1
System.out.println("端口:" + url.getPort());
System.out.println("协议:" + url.getProtocol());
System.out.println("参数部分:" + url.getQuery());
URL url1 = new URL("https://blog.csdn.net/");//相对路径构建URL对象
URL url2 = new URL(url1 , "m0_45240384");
System.out.println(url2.toString());
}
}//输出如下
获取与此URL关联的协议的默认端口:443
端口号后面的内容:/md?articleId=107335888
主机名:editor.csdn.net
路径:/md
端口:-1
协议:https
参数部分:articleId=107335888
https://blog.csdn.net/m0_45240384
接下来实现一个简单的爬虫:
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
public class Study{
public static void main(String[] args) throws MalformedURLException {
BasicSpider();
}
static void BasicSpider(){
URL url = null;
InputStream is = null;
OutputStream os = null;
BufferedReader br = null;
BufferedWriter bw = null;
//StringBuilder sb = new StringBuilder();
String temp = "";
try {
url = new URL("https://blog.csdn.net/m0_45240384/article/details/107077506");
is = url.openStream();
br = new BufferedReader(new InputStreamReader(is));
os = new FileOutputStream("study.txt");
bw = new BufferedWriter(new OutputStreamWriter(os));
while ((temp = br.readLine()) != null){
bw.write(temp);
br.readLine();
}
//System.out.println(sb);
}catch (MalformedURLException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
bw.close();
}catch (IOException e){
e.printStackTrace();
}try {
os.close();
}catch (IOException e){
e.printStackTrace();
}try{
br.close();
}catch (IOException e){
e.printStackTrace();
}try {
is.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
TCP协议是面向连接的,在通信是客户端与服务器必须建立连接。在网络通信中,第一次主动发起通信的程序被称作客户端(Client)程序,简称客户端;而在第一次通信中等待连接的程序被称作为服务器端(Server)程序,简称服务器。一旦通信建立,则客户端和服务器端完全一样,没有本质区别。
“请求-响应”模式
在“请求-响应”模式中,Socket类用于发送TCP消息; ServerSocket类用于创建服务器。
套接字Socket是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到了通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。
在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性将两个Socket进行连接,这样,客户端和服务器通过套接字所建立的连接即可使用输入/输出流进行通信。
TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据。
实际上,套接字只是计算机上已编号的端口。如果发送方和接收方计算机确定好端口,它们之间就可以进行通信了。
TCP/IP通信连接的简单过程
TCP/IP通信连接过程是:位于A计算机上的TCP/IP软件向B计算机发送包含端口号的消息; B计算机的TCP/IP软件接收该消息并进行检查,查看是否有它知道的程序正在该端口上接收消息。如果有,它将该消息交给这个程序。
要使程序有效地运行,就必须有- -个客户端和一个服务器。
通过Socket的编程顺序
顺序如下:
1)创建服务器ServerSocket。在创建时,定义ServerSocket监听端口(用来接收客户端发来的消息)
2)ServerSocket调用accept()方法,使之处于阻塞状态
3)创建客户端Socket,并设服务器的IP地址以及端口
4)客户端发出连接请求,建立连接
5)分别取得服务器和客户端Socket的InputStream和OutputSteam
6)利用Socket和ServerSocket进行数据传输
7)关闭流以及Socket
TCP–单向通信Socket服务器端
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class BasicSocketServer {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter bw = null;
try{
//创建服务器端套接字->指定监听端口
ServerSocket serverSocket = new ServerSocket(888);
System.out.println("服务器建立监听");
socket = serverSocket.accept();//监听、等待客户端请求,并愿意接受连接
//获取socket的输出流,并使用缓冲流进行包装
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//向客户端发送反馈信息
bw.write("芜湖起飞!");
}catch (IOException e){
e.printStackTrace();
}finally {
if(bw != null){
try {
bw.close();
}catch (IOException e){
e.printStackTrace();
}
}if (socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
TCP–单向通信Socket客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
public class BasicSockedClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader br = null;
try {
//创建Socket对象,指定要连接的服务器的IP地址和端口而不是自己机器的端口,发送端口是随机的
socket = new Socket(InetAddress.getLocalHost(), 888);
//获取socket的输入流,使用缓冲流包装
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//接受服务器端发送的信息
System.out.println(br.readLine());
}catch (IOException e){
e.printStackTrace();
}finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}if (socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
运行第一个程序后运行第二个程序,输出如下
TCP–双向通信Socket服务器
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) {
Socket socket = null;
BufferedReader read = null;
BufferedWriter write = null;
BufferedReader to = null;
try {
ServerSocket serverSocket = new ServerSocket(888);
socket = serverSocket.accept();
read = new BufferedReader(new InputStreamReader(socket.getInputStream()));
write = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
to = new BufferedReader(new InputStreamReader(System.in));
while (true){
String message = read.readLine();
System.out.println("客户:" + message);
String tomessage = "";
if(message.equals("再见"))
break;
tomessage = to.readLine();
write.write(tomessage + "\n");
write.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(read != null){
try {
read.close();
}catch (IOException e){
e.printStackTrace();
}
}if(write != null){
try {
write.close();
}catch (IOException e){
e.printStackTrace();
}
}if(to != null){
try {
to.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
TCP–双向通信Socket客户端
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class SockedClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader read = null;
BufferedWriter write = null;
BufferedReader to = null;
try {
socket = new Socket(InetAddress.getLocalHost(), 888);
read = new BufferedReader(new InputStreamReader(socket.getInputStream()));
write = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
to = new BufferedReader(new InputStreamReader(System.in));
while (true){
String tomessage = to.readLine();
write.write(tomessage + "\n");
write.flush();
if(tomessage.equals("再见"))
break;
System.out.println("客服:" + read.readLine());
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(read != null){
try {
read.close();
}catch (IOException e){
e.printStackTrace();
}
}if(write != null){
try {
write.close();
}catch (IOException e){
e.printStackTrace();
}
}if(to != null){
try {
to.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
示例如下:
上述代码只能一问一答,不够灵活,使用多线程可以实现更加灵活的双向通信。使用多线程技术,可以让一个线程发送消息,一个线程接收消息。实现如下:
服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) {
Socket socket = null;
BufferedReader reader = null;
try {
ServerSocket serverSocket = new ServerSocket(999);
socket = serverSocket.accept();
new Thread(new ServerThread(socket)).start();
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
String message = reader.readLine();
System.out.println("旭旭宝宝:" + message);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
class ServerThread implements Runnable{
Socket socket = null;
BufferedReader reader = null;
BufferedWriter writer = null;
public ServerThread(Socket socket){
this.socket = socket;
try {
reader = new BufferedReader(new InputStreamReader(System.in));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void run() {
try{
while(true){
String message = reader.readLine();
writer.write(message + "\n");
writer.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(writer != null){
try {
writer.close();
}catch (IOException e){
e.printStackTrace();
}
}if(reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
客户端
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class SockedClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader reader = null;
try {
socket = new Socket(InetAddress.getByName("127.0.0.1"), 999);
new Thread(new ClientThread(socket)).start();
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
System.out.println("大斌子:" + reader.readLine());
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
class ClientThread implements Runnable{
Socket socket = null;
BufferedReader reader = null;
BufferedWriter writer = null;
public ClientThread(Socket socket){
this.socket = socket;
try{
reader = new BufferedReader(new InputStreamReader(System.in));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true){
String message = reader.readLine();
writer.write(message + "\n");
writer.flush();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(writer != null){
try {
writer.close();
}catch (IOException e){
e.printStackTrace();
}
}if(reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}if(socket != null){
try {
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
UDP协议是面向无连接的,双方不需要建立连接就可通信。UDP通信所发送的数据通常需要进行封包操作(使用DatagramPacket类),然后才能接受或者发送(使用DatagramSocket类)。
DatagramPacket:数据容器(封包)
DatagramPacket类表示数据报包。数据报包用来实现封包功能,其常用方法如下:
DatagramSocket:用于发送或接受数据报包
当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。服务器的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接受。
DatagramSocket有两种常用的构造器,一种无参,常用于客户端,另一种需要指定端口,常用于服务器:
常用方法如下:
UDP通信编程基本步骤
1)创建客户端的DatagramSocket。创建的时候,定义客户端的监听端口
2)创建服务器端的DatagramSocket。创建时,定义服务器端的监听接口
3)在服务器端定义DatagramPacket对象,封装待发送的数据包
4)客户端将数据报包发送出去
5)服务器端接受数据报包
UDP单向通信服务器端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(888);
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
ds.receive(dp);
String message = new String(dp.getData(), 0 , dp.getLength());
System.out.println(message);
ds.close();
}
}
UDP单向通信客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Client {
public static void main(String[] args) throws Exception{
byte[] b = "UDP单向通信".getBytes();
DatagramPacket dp = new DatagramPacket(b, b.length, new InetSocketAddress("127.0.0.1", 888));
DatagramSocket ds = new DatagramSocket(889);
ds.send(dp);
ds.close();
}
}
通过字节数组流ByteArrayInputStream、ByteArrayOutputStream与数据流DataInputStream、DataOutputStream联合使用可以传递基本数据类型。
服务器端
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(857);
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
ds.receive(dp);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dp.getData()));
System.out.println(dis.readLong());
dis.close();
ds.close();
}
}
客户端
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Client {
public static void main(String[] args) throws Exception{
long n = 20000000000000L;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeLong(n);
byte[] b = bos.toByteArray();
DatagramPacket dp = new DatagramPacket(b, b.length, new InetSocketAddress("127.0.0.1", 857));
DatagramSocket ds = new DatagramSocket(858);
ds.send(dp);
dos.close();
bos.close();
ds.close();
}
}
输出为20000000000000
通过字节数组流ByteArrayInputStream、ByteArrayOutputStream与数据流ObjectInputStream、ObjectOutputStream联合使用可以传递对象类型。
服务器端
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(857);
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
ds.receive(dp);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dp.getData()));
System.out.println(ois.readObject());
ois.close();
ds.close();
}
}
客户端
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Client {
public static void main(String[] args) throws Exception{
Person p = new Person("aabb", 18);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(p);
byte[] b = bos.toByteArray();
DatagramPacket dp = new DatagramPacket(b, b.length, new InetSocketAddress("127.0.0.1", 857));
DatagramSocket ds = new DatagramSocket(858);
ds.send(dp);
oos.close();
bos.close();
ds.close();
}
}
class Person implements Serializable{
private static final long serialVersionUID = 1L;
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
输出为Person{name=‘aabb’, age=18}