- java网络编程
- 阻塞IO
- NIO
1. java网络编程
- 基础知识
1.ip地址和端口号
2.tcp/udp协议
3.URL
4.InetAddress
public void test() throws IOException{
//使用URL读取网页
//创建一个URL实例
URL url=new URL("http://www.baidu.com");
//通过openstream方法虎丘资源字节输入流
InputStream is=url.openStream();
//字节输入转为字符,不指定编码,中文可能乱码
InputStreamReader isr=new InputStreamReader(is,"UTF-8");
//字符输入假如缓存,提高读写效率
BufferedReader br=new BufferedReader(isr);
String data = br.readLine();
while (data != null) {
System.out.println(data);//输出
data=br.readLine();
}
br.close();
isr.close();
is.close();
}
- socket
1.创建socket实例
2.客户端链接
3.服务端链接
4.总结
客户端代码
public void socket() throws IOException {
//1.创建客户端socket,指定服务器地址和端口
Socket socket = new Socket("localhost", 10086);
//2.获取输出流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os); //输出包装成打印流
pw.flush();
socket.shutdownOutput();
//3.获取输入流,读取服务器响应数据
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
if ((info = br.readLine()) != null) {
System.out.println("我是客户端,服务器端说" + info);
}
//4.关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
}
服务端代码
/**
* 基于TCP协议的socket通信,实现用户登录,服务端
*/
public void serverSocket() throws IOException{
//1.创建一个服务器端的socket,即ServerSocket,指定绑定端口,监听端口
ServerSocket serverSocket=new ServerSocket(10086);
//2.调用accept 开始监听,等待客户端连接
Socket socket=serverSocket.accept();
//3.获取输入流,读取客户端
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
if ((info = br.readLine()) != null) {
System.out.println("我是服务器,客户端说" + info);
}
socket.shutdownOutput();//关闭输入流
//4.获取输出流,响应客户端信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os); //输出包装成打印流
pw.write("欢迎您......");
pw.flush();
//5.关闭资源
pw.close();
os.close();
br.close();
is.close();
socket.close();
serverSocket.close();
}
总结
- 创建ServerSocket和Socket
serverSocket.accept() - 打开连接到Socket的输入输出流
socket.openInputStream() - 按照协议对Socket进行读写
包装流输入输出 - 关闭输入,输出流,关闭socket
serverSocket还要关闭socket
2. 阻塞IO
java的I/O接口
- 基于字节
InputStream或OutputStream - 基于字符
Writer和Reader - 基于磁盘
File - 基于网络
Socket
一直是阻塞的,会等到数据到来时才返回
eg:serverSocket.accept()一直等到有客户端socket连接时启用一个线程
总结
1.BIO数据在写入OutputStream或者从InputStream读取时都有阻塞
2.当前一些需要大量HTTP长连接
NIO
基本原理
- 是事件到来时,才执行,不是像BIO那样始终监视
- 有线程间通信方式,通过wait/notify方法,保证每次上下文切换都是有意义的,提高CPU的效率
- 有一个线程处理所有的IO事件
通信模型
双向通道
代码
客户端
public Selector mSelector;
public void initClient(String ip, int port) throws IOException {
//1.获取一个Socket通道
SocketChannel channel = SocketChannel.open();
//2.设置通道为非阻塞
channel.configureBlocking(false);
//3.获得一个通道管理器
mSelector = Selector.open();
//客户端连接服务器,方法执行并没有实现连接,需要在listen方法中调用
//用channel.finisConnection 才能完成连接
channel.connect(new InetSocketAddress(ip, port));
//将通道管理器和通道绑定,并为该通道注册selecionKey.OP_CONNECT事件
channel.register(mSelector, SelectionKey.OP_CONNECT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,有则进行处理
*/
public void listen() throws IOException {
//轮询方式
while (true) {
mSelector.select();
//获得selector中选中的迭代器
Iterator iterator = mSelector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
//删除已选的key 防止重复
iterator.remove();
//连接事件发生
if (key.isConnectable()) {
SocketChannel channel= (SocketChannel) key.channel();
//如果正在连接,则完成连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
//设置成非阻塞
channel.configureBlocking(false);
//在这里可以给服务器发送消息
channel.write(ByteBuffer.wrap(
new String("向服务器发送数据").getBytes()));
//和服务端连接成功后,为了接收服务器消息,需要设置读的权限
channel.register(mSelector,SelectionKey.OP_READ);
//
//获得可读事件
}else if(key.isReadable()){
read(key);
}
}
}
}
private void read(SelectionKey key) {
}
服务器端
//通道管理器
private Selector mSelector;
/**
* 获得一个ServerSocket通道,并对改通道做一些初始化操作
*/
public void initServer(int port) throws IOException{
//获得一个ServerSocket通道
ServerSocketChannel channel=ServerSocketChannel.open();
//2.设置通道为非阻塞
channel.configureBlocking(false);
//将通道对应的serverSocket绑定到port端口
channel.socket().bind(new InetSocketAddress(port));
//3.获得一个通道管理器
mSelector = Selector.open();
//将通道管理器和通道绑定,并为该通道注册selecionKey.OP_ACCEPT事件
//注册该时间后,当事件到达时,selector.select会反悔,
//如果该事件没到达selector.select会一直阻塞
channel.register(mSelector, SelectionKey.OP_ACCEPT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件
*/
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,有则进行处理
*/
public void listen() throws IOException {
System.out.println("服务器端启动成功");
//轮询方式
while (true) {
mSelector.select();
//获得selector中选中的迭代器
Iterator iterator = mSelector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
//删除已选的key 防止重复
iterator.remove();
//连接事件发生
if (key.isConnectable()) {
SocketChannel channel= (SocketChannel) key.channel();
//如果正在连接,则完成连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
//设置成非阻塞
channel.configureBlocking(false);
//在这里可以给服务器发送消息
channel.write(ByteBuffer.wrap(
new String("向服务器发送数据").getBytes()));
//和服务端连接成功后,为了接收服务器消息,需要设置读的权限
channel.register(mSelector,SelectionKey.OP_READ);
//
//获得可读事件
}else if(key.isReadable()){
read(key);
}
}
}
}
private void read(SelectionKey key) {
}