Java网络编程概述
网络编程本质上就是进程之间的通信,数据通信的基础就是IO模型
- 输入流:从数据源将数据输入到我们的应用进程的数据流
- 输出流:从我们的应用进程将数据输出到数据源
数据源:字符串、文件、对象(序列化)、Socket等都可以是数据源
JavaIO基础
Socket
Linux中为了区分文件是否被打开,Linux会给每个文件分配一个整数ID,被称为文件描述符(Windows中也有类似的句柄概念,和Linux的区别还是比较大的),Linux程序在执行任何形式的I/O操作时,都是在读取或者写入一个文件描述符
Socket(插座)是网络通信的端点(绑定了IP和端口),在Linux中Socket对应着一个文件,在Java中使用一个Socket类创建一个对象就相当于创建了一个文件描述符,就可以使用普通的文件操作来传输数据
InetAddress
方法名 | 描述 |
---|---|
static InetAddress getByName(String host) |
根据主机名或ip地址获取InetAddress 对象实例 |
String getHostName() |
获取主机名字符串 |
String getHostAddress() |
获取主机地址字符串 |
UDP
由DatagramSocket
类发送或接收DatagramPacket
数据报
DatagramSocket
方法名 | 描述 |
---|---|
void send(DatagramPacket p) |
发送数据报(不检查对方是否存活,不会阻塞) |
void receive(DatagramPacket p) |
接受数据报(等待接收,程序会阻塞) |
void close() |
释放资源 |
DatagramPacket
方法名 | 描述 |
---|---|
DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port) |
包装数据报指定发送地址和端口(发送用) |
DatagramPacket(byte buf[], int offset, int length) |
包装数据报(接收用) |
TCP
由Socket
类担任客户端角色,ServerSocket
类担任服务端角色,当ServerSocket
发现有客户端发出连接请求时,会创建Socket
实例与之进行通信
Socket
方法名 | 描述 |
---|---|
OutputStream getOutputStream() |
获取输出流 |
InputStream getInputStream() |
获取输入流 |
void close() |
关闭连接资源 |
void shutdownInput() |
输出结束标记 |
void shutdownOutput() |
输入结束标记 |
ServerSocket
方法名 | 描述 |
---|---|
Socket accept() |
监听到有客户端连接,就对应生成与之通讯的成客户端对象 |
简单的CS程序实现
Server
public class Server {
private static final int PORT = 8080;
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("服务器监听在" + PORT + "端口");
while (true) {
Socket socket = serverSocket.accept(); //等待客户端连接
String prefix = "客户端[" + socket.getInetAddress() + ":" + socket.getPort() + "]"; //客户端IP和端口信息
System.out.println(prefix + "已连接"); //客户端连接日志
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取字符缓存输入流
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //获取字符缓冲输出流
String message;
while ((message = reader.readLine()) != null) { //若能读取到客户端一行内容
System.out.println(prefix + "说: " + message);
writer.write(message + "\n"); //最后加一个换行符,为了客户端能使用readLine()方法
writer.flush(); //刷新缓冲区
}
System.out.println(prefix + "断开连接");
}
} catch (IOException e) {
e.printStackTrace();
System.out.println(PORT + "端口被占用");
}
}
}
Client
public class Client {
private static final String HOST = "localhost";
private static final int PORT = 8080;
private static final String QUIT = "quit";
public static void main(String[] args) {
try (Socket socket = new Socket(HOST, PORT);
Scanner scanner = new Scanner(System.in);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
System.out.println("请输入请求内容");
while (true) {
/*输入信息的三种方式*/
//String info = scanner.nextLine(); //scanner标准输入流
//String info = JOptionPane.showInputDialog("请输入请求内容"); //swing图形化输入框
String info = consoleReader.readLine(); //字符缓冲流
if (info != null) {
writer.write(info + "\n"); //最后加一个换行符,为了服务端能使用readLine()方法
writer.flush(); //刷新缓冲区
}
String message = reader.readLine();
System.out.println("服务器说: " + message);
if (QUIT.equals(message)) { //检查是否退出连接
break;
}
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("[" + HOST + ":" + PORT + "]服务器未启动");
}
}
}
同步异步阻塞非阻塞
同步与异步
同步和异步指得是:被调用方返回处理结果的机制(通信机制)
- 同步:调用方发出请求后等待返回处理结果,被调用方处理请求,处理结束后才会返回处理结果
- 异步:调用方发出请求后等待返回处理结果,被调用方立即返回,等请求处理结束后,再使用其他方式返回给调用方处理结果
阻塞与非阻塞
阻塞与非阻塞指的是:调用方在等待调用结果时的状态(调用状态)
- 阻塞:调用方发出请求后等待返回结果的过程中陷入阻塞状态
- 非阻塞:调用方发出请求后等待返回结果的过程中继续执行其他的任务
不同的组合
这两组概念可以有以下四种不同的搭配
- 同步阻塞:调用方发出请求后阻塞,被调用方处理请求,请求处理结束返回处理结果,被调用方被唤醒(类比单个线程内普通方法的调用)
- 同步非阻塞:调用方发出请求后,被调用方直接返回,在此期间调用方抽空不断询问是否处理完成,直到被调用方将请求处理结束后,才会返回真正的处理结果,否则返回未处理完毕的状态(类比NodeJS的事件轮询机制)
- 异步阻塞:调用方发出请求后,被调用方立刻返回,但调用方仍然是阻塞状态,直到被调用方将请求处理结束后,通知调用方获取处理结果时,调用方这才被唤醒(类比JavaScript中的
forEach()
方法) - 异步非阻塞:调用方发出请求后,被调用方立刻返回,调用方继续执行其他的任务,被调用方将请求处理结束后,再通知调用方获取处理结果(类比AJAX请求)
Comments NOTHING