java网络编程系列之JavaIO的“前世”:BIO阻塞模型
- Scoket与ServerSocket
-
- 服务器端: ServerSocket代码实战
- 客户端: socket代码实战
- 运行简单的服务器客户端实例
-
- 补充知识点 > 1 --- 多重流嵌套(比如BufferedWrite)时各个流的关闭问题
- 优化
Scoket与ServerSocket
-
bind
:提供给ServerSocket
端口进行绑定操作,客户端只需要向指定的端口发送数据,服务器就可以收到数据 -
accept
: 阻塞式调用,等待客户端与其建立连接,如果没有客户端与其建立连接,那么accept
函数就会阻塞住当前线程 -
connect
:客户端socket
通过指定的服务器的主机和端口,与指定的服务器进程相连接 - 一旦服务器的
accept
函数,接收了对应的客户端连接,便会返回一个socket
对象,就是服务器进程和指定客户端进行通信的一个端点 - 连接建立成功后,变可以通过I/O流进行数据的传输
- 当数据传输完毕后,客户端调用close函数,关闭对应的端点,服务器端如果此时尝试向客户端发送数据,便会抛出异常,因此服务器端也应该关闭端点
服务器端: ServerSocket代码实战
public class Server
{
public static void main(String[] args) {
final int DEFAULT_PORT=8080;
ServerSocket serverSocket=null;
try {
//监听端口
serverSocket=new ServerSocket(DEFAULT_PORT);
System.out.println("服务器启动,监听端口为: "+DEFAULT_PORT);
//与客户端进行数据交换
while(true)
{
//等待客户端连接--这里会阻塞住,知道有客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端["+socket.getPort()+"]已连接");
//获取客户端的输入数据流
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取向客户端写入数据流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//读取客户端发送的消息
String line = br.readLine();
if(line!=null)
{
System.out.println("客户端["+socket.getPort()+"]发送的消息:"+line);
//向客户端写入数据
bw.write("服务器: "+line+"\n");
//刷新缓冲区的数据,确保全部写入
bw.flush();
}
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
//关闭流---也可以用try with resources
if(serverSocket!=null)
{
try {
serverSocket.close();
System.out.println("关闭服务端的serverSocket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端: socket代码实战
//客户端
public class Client
{
public static void main(String[] args) {
//服务器的主机地址
final String DEFAULT_SERVER_HOST="127.0.0.1";
//服务器的端口
final int DEFAULT_SERVER_PORT=8080;
Socket socket=null;
BufferedWriter bw = null;
try {
//创建sokcet
socket=new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
//创建IO流
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//让用户输入信息
BufferedReader consoleBr=new BufferedReader(new InputStreamReader(System.in));
String input=consoleBr.readLine();
//发送消息给服务器
bw.write(input+"\n");
bw.flush();
//读取服务器返回的消息
String line = br.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭最外层的writer,会自动去flush缓冲区里面的剩余内容
//关闭writer,也就关闭了socket
if(bw!=null)
{
try {
bw.close();
System.out.println("关闭客户端的socket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行简单的服务器客户端实例
补充知识点 > 1 — 多重流嵌套(比如BufferedWrite)时各个流的关闭问题
public
class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
try {
flush();
} catch (IOException ignored) {
}
out.close();
}
}
这是jdk1.6 中FilterOutputStream流的部分实现代码(我是粘贴过来的)。从这段代码可以看出,嵌套流关闭时直接关闭的是被封装流,只是在关闭之前flush。
因此关闭最外层的流即可
也可以考虑使用try with resource 关闭流
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
优化
上面的例子中,客户端发送一次数据给服务器端后,便会自动断开连接,这样显然不太好,所以我们下面进行了优化
//客户端
public class Client
{
public static void main(String[] args) {
//服务器的主机地址
final String DEFAULT_SERVER_HOST="127.0.0.1";
//服务器的端口
final int DEFAULT_SERVER_PORT=8080;
//停止
final String QUIT="quit";
Socket socket=null;
BufferedWriter bw = null;
try {
//创建sokcet
socket=new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
//创建IO流
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//让用户输入信息
BufferedReader consoleBr=new BufferedReader(new InputStreamReader(System.in));
String input=null;
while(!QUIT.equals(input=consoleBr.readLine()))
{
//发送消息给服务器
bw.write(input+"\n");
bw.flush();
//读取服务器返回的消息
String line = br.readLine();
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭最外层的writer,会自动去flush缓冲区里面的剩余内容
//关闭writer,也就关闭了socket
if(bw!=null)
{
try {
bw.close();
System.out.println("关闭客户端的socket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class Server
{
public static void main(String[] args) {
final int DEFAULT_PORT=8080;
ServerSocket serverSocket=null;
try {
//监听端口
serverSocket=new ServerSocket(DEFAULT_PORT);
System.out.println("服务器启动,监听端口为: "+DEFAULT_PORT);
//等待客户端连接--这里会阻塞住,知道有客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端["+socket.getPort()+"]已连接");
//获取客户端的输入数据流
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取向客户端写入数据流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//读取客户端发送的消息
String line=null;
//与客户端进行数据交换
while((line = br.readLine())!=null)
{
System.out.println("客户端["+socket.getPort()+"]发送的消息:"+line);
//向客户端写入数据
bw.write("服务器: "+line+"\n");
//刷新缓冲区的数据,确保全部写入
bw.flush();
}
}
catch (IOException e) {
e.printStackTrace();
}
finally {
//关闭流---也可以用try with resources
if(serverSocket!=null)
{
try {
serverSocket.close();
System.out.println("关闭服务端的serverSocket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}