前言
好久没用java来读写文件了,来复习一下,上次写的那个修改文件名都没有涉及到文件读写
目标:
- 文件读写
- 写个TCP通信的客户端和服务器端,服务器先发送str1, 客户端接收后发送str2,服务器接收后就会修改str1,这样两个客户端,就可以实现简单的通信功能。
开始
IO流分类
输入输出流可以按3种方式来分类:
- 分为输入流和输出流。其中输入流以InputSream和Reader作为基类,输出流以OutputStream和Writer作为基类,不过这四个基类都是抽象类,不可以直接实例化
- 分为字节流和字符流。其中字节流以8位字节作为操作单位,典型的有FileInputStream等等,字符流以16位字符作为操作单位,典型的有BufferedReader PrintWriter FileWriter等等。
- 分为节点流和处理流,其中节点流是直接访问数据源的底层操作,处理流是对底层的数据流进行封装,而且封装好的流非常好用,如PrintStream.
流的概念详解
因为所有类都是从InputStream OutputStream和 Reader Writer四个基类中派生,只要掌握牢固这四个类的常用方法,那么其他的类都可以融会贯通
- InputStream
int read() 读取单个字节,返回实际读取的字节,访问的字节数据直接转为int
int read(byte[] b) 最多读取b.length()个字节,存储在b中。
int read(byte[] b,int off,int len) 从输入流中最多读取len个字节,存储在b数组中,从off位置开始,返回实际读取的字符数量
- OutputStream
void write(int c) 将单个字符写入输出流
void write(byte[] buf) 将buf写入输出流
void write(byte[] buf,int off,int len) 将buf从off开始写入len个字符
- Reader
int read() 读取单个字节,返回实际读取的字节,访问的字节数据直接转为int
int read(char[] b) 最多读取b.length()个字节,存储在b中。
int read(char[] b,int off,int len) 从输入流中最多读取len个字节,存储在b数组中
- Writer
void write(int c) 将单个字符写入输出流
void write(char[] buf) 将buf写入输出流
void write(char[] buf,int off,int len) 将buf从off开始写入len个字符
void write(String str) 写入字符串
void write(String str, int off, int len) 将str从off开始写入len个字符
常用流举例
- FileWriter
这个类对于文本的写入很方便
import java.io.*;
public class FileWriterTest {
public static void main(String[] args)
{
try{
FileWriter fw = new FileWriter("poem.txt");
fw.write("小草\r\n");
fw.write("离离原上草\r\n");
fw.write("一岁一枯荣\r\n");
fw.write("野火烧不尽\r\n");
fw.write("春风吹又生\r\n");
fw.close();
}catch(IOException e)
{
e.printStackTrace();
}
- BufferedReader
这个类是个缓冲类,有个readline()按行读取的方法很好用,但可惜FileWriter没有readline()方法
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
String str = br.readline()
- PrintWriter
这个类有个非常有用的特性就是行刷新,每输出一行就会刷新强制这行输出去,对比之下,BufferedWriter就得加上\r\n还得调用flush()方法。
PrintWriter pw = new PrintWriter(socket.getOutputStream(),true)
pw.print(str)
pw.println(str)
实战
先写个客户端,
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Socket client = new Socket("0.0.0.0", 5555);
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter pw = new PrintWriter(client.getOutputStream(),true);
String str = br.readLine();
System.out.println(str);
pw.println("hello");
pw.flush();
pw.println("exit");
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里有一些需要注意的地方
- 这里我在发送之前就有个br.readline()而且我没有设置等待时间限制,如果服务器没有首先发数据的话就会卡在那
- 按理PrintWriter会换行刷新,但发现好像没有,也有可能因为我的服务器端是用python写的,导致一度卡住
- 自己显示调用client.close()为宜
- 如果接收出问题就会影响发送,发送出问题就会影响接收,要么设定延时最大时间,要么发送接收分成两个子线程。
再写个服务器端
import socket
import threading
import time
str = 'test'
def tcplink(sock, addr):
global str
print 'Accept new connection from %s:%s...' % addr
#sock.settimeout(5)
sock.send(str+'\r\n')
try:
while True:
data = sock.recv(1024)
print data
if data:
str = data
print "receive"
if data == 'exit' or not data:
break
except Exception,e:
print e
print u"出现异常"
finally:
sock.close()
print "client is down"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 5555))
s.listen(5)
print u'测试客户端已就绪'
while 1:
sock, addr = s.accept()
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()