输入输出流和网络通信

前言

好久没用java来读写文件了,来复习一下,上次写的那个修改文件名都没有涉及到文件读写
目标:

  • 文件读写
  • 写个TCP通信的客户端和服务器端,服务器先发送str1, 客户端接收后发送str2,服务器接收后就会修改str1,这样两个客户端,就可以实现简单的通信功能。

开始

IO流分类

输入输出流可以按3种方式来分类:

  1. 分为输入流和输出流。其中输入流以InputSream和Reader作为基类,输出流以OutputStream和Writer作为基类,不过这四个基类都是抽象类,不可以直接实例化
  2. 分为字节流和字符流。其中字节流以8位字节作为操作单位,典型的有FileInputStream等等,字符流以16位字符作为操作单位,典型的有BufferedReader PrintWriter FileWriter等等。
  3. 分为节点流和处理流,其中节点流是直接访问数据源的底层操作,处理流是对底层的数据流进行封装,而且封装好的流非常好用,如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个字符

常用流举例

  1. 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();
     }
  1. BufferedReader
    这个类是个缓冲类,有个readline()按行读取的方法很好用,但可惜FileWriter没有readline()方法
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
String str = br.readline()
  1. 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()

你可能感兴趣的:(输入输出流和网络通信)