网络编程的基础类
1.IP地址:
目前Java对IPv4和IPv6这两种IP地址进行了封装,实现类分别为Inet4Address和Inet6Address,它们都继承了类InetAddress。
InetAddress的实例对象包含以数字形式保存的IP地址,同时还可能包含主机名,提供了将主机名解析为IP地址和反向解析的方法。InetAddress对域名进行解析时使用本地机器配置或者DNS和网络信息服务来实现。对于DNS,本地需要向DNS服务器发送查询请求,然后服务器返回对应的IP地址,当然本地也会缓存一些主机名与IP地址的映射,提高性能。
InetAddress的构造方法不是公有的,只能通过提供的静态方法来获取InetAddress对象,最常用的是getByName(String host)方法,传入目标主机的名字,尝试连接DNS,获取对应的IP地址。还有一个getLocalHost()返回本地的IP地址。
2.输入输出流:
输出流是指向目的地写入的二进制序列,输入流是从数据源读取的二进制序列,都是以字节为单位的。
输出流的基类OutputStream:
-
write(byte[] b) :将字节数组中的所有字节写入输出流,等于write(b,0,b,length)。
-
flush() :将输出流缓存的所有字节写向它们的目标,只能保证正确进行输出,不能保证正确到达目标。
-
close() :关闭释放。
**输入流的基类InputStream:**
-
read() :从输入流中读取数据的下一个字节,没有返回-1。
-
read(byte[] b) :从输入流中读取一定数量的字节,并存到缓冲区数组b中。
-
close() :关闭释放。
**流的处理工具:** 输出流:Java定义了一个抽象的基类java.io.Writer,对write方法进行了5次重载。最常用的子类是PrintWriter,因为具有强大的格式化输出能力,还有print()和println()方法可以高效的向输出流写入指定编码的对象。 输入流:Java定义了一个抽象的基类java.io.Reader,最常用的子类InputStreamReader,是字节流通向字符流的桥梁,使用指定的字符集读取输入流中的数据并将其解码为字符,如new InputStreamReader(System.in)。 通常还使用BufferedReader从字符输入流中读取文本,缓冲各个字符,实现高效读取,如: BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
3.套接字:
套接字用于实现网络上的两个程序之间的连接与通信,连接的两端都分别有一个套接字。
流套接字: 用于面向连接,可靠的数据传输服务,实现无差错,无重复发送,并按顺序接收,用于TCP连接。如Socket,ServerSocket。
数据报套接字: 提供无连接服务,不保证数据传输的可靠性,有可能丢失或重复,用于UDP连接。如DatagramSocket。
原始套接字: 前两种为标准套接字,而原始套接字是可以读写内核没有处理的IP数据包,而标准套接字只能读取TCP或UDP的数据。如果要访问其他协议发送的数据,就必须使用原始套接字。
TCP编程
一对一通信:
package javaS.net.TCPsocket.one2one;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
//一对一通信:服务端
public class Server {
public static void main(String[] args) throws IOException {
//创建服务端套接字,提供监听端口,监听客户端是否发来请求
ServerSocket server = new ServerSocket(888);
//accep方法,使server服务端套接字一直处于阻塞状态,直到有客户端发来请求连接
//创建一个socket实例接收客户端发来的请求,操作这个实例完成所需要的会话
Socket socket = server.accept();
//创建一个输入流来读取客户端发来的数据,这里使用缓冲输入流,实现高效读取
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader buffer_reader = new BufferedReader(reader);
//创建一个输出流,来放服务端的响应数据
PrintWriter writer = new PrintWriter(socket.getOutputStream());
//读出客户端的请求数据
String request = buffer_reader.readLine();
System.out.println("客服端的请求是:"+request);
//把服务端的响应数据输入到输出流中
String line = "你好,我是服务端!";
writer.println(line);
//因为输出流的内容可能并不是立即写回给客户端,所以这里显示刷新输出流
writer.flush();
//关闭IO流,Socket套接字
writer.close();
buffer_reader.close();
socket.close();
server.close();
}
}
package javaS.net.TCPsocket.one2one;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
//客户端
public class Client {
public static void main(String[] args) throws IOException {
//客户端只需要创建一个socket实例来实现会话需求即可,指定目标的IP地址和端口
Socket socket = new Socket("127.0.0.1", 888);
// 创建一个输入流来读取服务端发来的数据,这里使用缓冲输入流,实现高效读取
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader buffer_reader = new BufferedReader(reader);
// 创建一个输出流,来放客户端的请求数据
PrintWriter writer = new PrintWriter(socket.getOutputStream());
//把请求数据写入输出流,发送给服务端
String readline = "你好,我是客户端!";
writer.println(readline);
writer.flush(); //显示刷新
//从输入流中读取服务端返回来的数据
String response = buffer_reader.readLine();
System.out.println("服务端的响应是:"+response);
//关闭IO流,Socket套接字
writer.close();
buffer_reader.close();
socket.close();
}
}
一对多通信:
服务端有两个类,一个实现runnable接口负责套接字以及流的处理,另一个负责监听连接和线程的分配。
package javaS.net.TCPsocket.one2more;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/*
* 这个类实现了runnable接口,是一个线程类,负责套接字socket和IO流的处理。
*/
public class ServerSocketHandler implements Runnable{
private Socket socket;
//需要传入一个socket来处理
public ServerSocketHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//创建一个输入流来读取客户端发来的数据,这里使用缓冲输入流,实现高效读取
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader buffer_reader = new BufferedReader(reader);
//创建一个输出流,来放服务端的响应数据
PrintWriter writer = new PrintWriter(socket.getOutputStream());
//从socket中读取出客服端的ip地址和端口,以及请求数据
String client = "<"+socket.getInetAddress().toString()+" : "+socket.getPort()+">";
String request = buffer_reader.readLine();
System.out.println(client+" 说:"+request);
//把服务端的响应数据写入输出流
String line = client + "你好,我是服务端!";
writer.println(line);
writer.flush();
//关闭io流和socket套接字
writer.close();
buffer_reader.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package javaS.net.TCPsocket.one2more;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/*
* 这个服务端类实现线程池来进行线程分配,避忌频繁创建线程的开销。
* 监听连接服务不变。
*/
public class ServerThreadPool {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(888);
/*
* 使用高级线程池:
* 线程池大小根据负载情况自动调整
* 如果一个线程空闲60秒,移出线程池。
*/
Executor service = Executors.newCachedThreadPool();
Socket socket = null;
while(true) {
socket = server.accept();
service.execute(new ServerSocketHandler(socket));
}
}
}
客户端使用多个重复类,发起连接:
package javaS.net.TCPsocket.one2more;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//客户端只需要创建一个socket实例来实现会话需求即可,指定目标的IP地址和端口
Socket socket = new Socket("127.0.0.1", 888);
// 创建一个输入流来读取服务端发来的数据,这里使用缓冲输入流,实现高效读取
InputStreamReader reader = new InputStreamReader(socket.getInputStream());
BufferedReader buffer_reader = new BufferedReader(reader);
// 创建一个输出流,来放客户端的请求数据
PrintWriter writer = new PrintWriter(socket.getOutputStream());
//把请求数据写入输出流,发送给服务端
String readline = "你好,我是客户端!";
writer.println(readline);
writer.flush(); //显示刷新
//从输入流中读取服务端返回来的数据
String response = buffer_reader.readLine();
System.out.println("服务端的响应是:"+response);
//关闭IO流,Socket套接字
writer.close();
buffer_reader.close();
socket.close();
}
}
UDP编程
发送方:
package javaS.net.UDPsocket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 这是UDP网络编程的发送方
*/
public class Client {
public static void main(String[] args) {
try {
//UDP使用DatagramSocket来发送和接收消息,没指定ip,默认本地,端口号随机
//UDP是广播形式
DatagramSocket sendSocket = new DatagramSocket();
//编写发送消息,必须转为字节数组
String msg = "你好,我是发送方!";
byte[] buf = msg.getBytes();
//发送方的IP地址和端口号
int port = 8888;
InetAddress ip = InetAddress.getLocalHost();
//把数据,ip,端口封装在一个数据报DatagramPacket
DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, ip, port);
//使用DatagramSocket发送数据报DatagramPacket
sendSocket.send(sendPacket);
//创建一个数据报和字节数组来接收返回的消息
byte[] getBuf = new byte[1024];
DatagramPacket getPacket = new DatagramPacket(getBuf,getBuf.length);
sendSocket.receive(getPacket);
//将字节数组转为字符串
String backMsg = new String(getBuf,0,getPacket.getLength());
System.out.println("接收方返回的消息是:"+backMsg);
//关闭DatagramSocket
sendSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收方:
package javaS.net.UDPsocket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
/*
* 这是UDP网络编程的接收方
*/
public class Server {
public static void main(String[] args) {
try {
//创建接收方的套接字,并指定可接收的ip地址和端口号,也可以使用SocketAddress来绑定监听的ip和端口
InetAddress ip = InetAddress.getLocalHost();
int port = 8888;
DatagramSocket getSocket = new DatagramSocket(port, ip);
//创建数据报和字节数组来接收发送方的消息
byte[] buf = new byte[1024];
DatagramPacket getPacket = new DatagramPacket(buf, buf.length);
getSocket.receive(getPacket);
//转为字符串输出
String getMsg = new String(buf,0,getPacket.getLength());
System.out.println("发送方发送的消息是:"+getMsg);
//从数据报得到发送方的ip和端口
InetAddress sendIP = getPacket.getAddress();
int sendPort = getPacket.getPort();
System.out.println("发送方的地址是:"+sendIP.getHostAddress()+" : "+sendPort);
//从数据报得到发送方的套接字地址,里面就绑定了发送方的ip和端口
SocketAddress sendAddress = getPacket.getSocketAddress();
String feedback = "你好,我是接收方!";
byte[] backBuf = feedback.getBytes();
//把返回的消息封装到数据报中
DatagramPacket sendPacket = new DatagramPacket(backBuf, backBuf.length,sendAddress);
//返回给发送方
getSocket.send(sendPacket);
//关闭接收方的套接字
getSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后,参考学习链接