• java.io与网络通信


    文件IO

    java.io.File是用于操作文件或目录的类:

    File file = new File("hello.txt");
    

    实例化File时不关心路径的目标并不会去读取文件或目录. File类提供了一些有用的方法:

    • isFile(): 判断路径指向的是否为文件

    • createNewFile(): 当路径指向的文件不存在时创建一个空文件

    • exists(): 判断路径指向的文件是否存在

    • delete(): 删除路径指向的文件或目录

    • length(): 返回文件的长度

    • isDirectory(): 判断路径指向的是否为目录

    • mkdir(): 根据路径创建空目录, 父目录必须存在

    • mkdirs(): 根据路径创建空目录, 会创建必要的父目录

    • list(): 以String[]类型返回目录中所有文件名

    • listFiles(): 以File[]类型返回目录中所有文件

    字符流

    Java使用流来读写文件, 字符流用来读写文本文件. 所有的字符流类都在java.io包中.

    读取文本文件:

    File file = new File("a.txt");
    FileReader fin  = new FileReader(file);
    BufferedReader reader = new BufferedReader(fin);
    try {
    	String str = reader.readLine();
    	System.out.println(str);
    }
    catch (IOException e) {
    	e.printStackTrace();
    }
    finally {
    	reader.close();
    	fin.close();
    }
    
    

    FileReader也可以直接用文件名创建. new FileReader("a.txt").

    写入文本文件:

    FileWriter fout = new FileWriter("a.txt", true);
    BufferedWriter writer = new BufferedWriter(fout);
    try {
    	String str = "Hello World";
    	writer.write(str);
    }
    catch (IOException e) {
    	e.printStackTrace();
    }
    finally {
    	writer.close();
    	fout.close();
    }
    

    FileWriter的第二个参数为append, true代表在文件尾追加, 默认false代表清空文件重写.

    字节流

    字节流用于读写二进制文件, 其读写的数据以byte[]类型存储.

    所有字节流类都在java.io包中.

    读取文件:

    File file = new File("a.in");
    FileInputStream fin = new FileInputStream(file);
    Byte[] buf = new Byte[512];
    
    try {
    	fin.read(buf);
    }
    catch {IOException e} {
    	e.printStackTrace();
    }
    finally {
    	fin.close();
    }
    

    写文件:

    File file = new File("a.in");
    FileOutputStream fout = new FileOutputStream(file);
    Byte[] buf = new Byte[512];
    try {
    	// write sth in buf 
    	fin.write(buf);
    }
    catch {IOException e} {
    	e.printStackTrace();
    }
    finally {
    	fout.close();
    }
    

    标准输入输出

    java.lang.System对象中维护了3个标准流, 用于终端输入输出:

    • System.out, 标准输出流, PrintStream对象
    • System.err: 标准错误流, PrintStream对象
    • System.in: 标准输入流, FileInputStream对象

    在需要的时候我们可以将它们重定向到文件.

    重定向标准输出:

    File file = new File("a.out");
    FileOutputStream fout  = new FileOutputStream(file);
    PrintStream pout = new PrintStream(fout);
    PrintStream stdout = System.out;  // save
    
    System.setOut(pout);
    System.setOut(stdout); // recover
    

    因为标准输入是字节流, 我们需要把它们转换成需要的类型才能使用:

    int n;
    byte[] buf = new byte[1024];
    n = System.in.read(buf);
    String s = new String(buf, 0, n);
    System.out.println(s);
    

    java.util.Scanner允许用迭代器的方式读取输入:

    Scanner scanner = new Scanner(System.in);
    while (scanner.hasNext()) {
    	// scanner.next();  // return Object
    	scanner.nextInt(); // return int
    }
    

    scanner可以直接将输入转换为内置类型使用很方便.

    网络IO

    TCP客户端

    java.net.Socket是一个用作Tcp客户端的Socket. 从Socket中获得InputStreamOutputStream对象就可以与服务器通过Tcp连接通信了.

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.net.Socket;
    
    public class TcpClient {
    	public static void main(String[] args) throws IOException {
    
    		Socket client = new Socket("127.0.0.1", 5000);
    		client.setSoTimeout(10000);
    
    		PrintStream out = new PrintStream(client.getOutputStream());
    
    		BufferedReader buf =  new BufferedReader(new InputStreamReader(client.getInputStream()));
    
    		String[] msgs = {"你好", "世界"};
    
    		for (String msg : msgs) {
    			out.println(msg);
    
    			while (true) {
    				String echo = buf.readLine();
    				if (echo != null) {
    					System.out.println(echo);
    					break;
    				}
    			}
    		}
    	}
    }
    

    上述示例中发送完一条消息则进入轮询, 查看是否有服务端消息.

    java.net.ServerSocket则是一个用作Tcp服务端的socket.

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    
    
    public class TcpServer implements Runnable {
    
    	private Socket client = null;
    	private String address;
    
    	public TcpServer(Socket client) {
    		this.client = client;
    	}
    
    	@Override
    	public void run() {
    		try {
    			String host = client.getInetAddress().toString();
    			String port = Integer.toString(client.getPort());
    
    			PrintStream out = new PrintStream(client.getOutputStream());
    			System.out.println("Get Connection");
    			BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
    
    			address = host + ":" + port;
    			System.out.println("get connection from" + address);
    
    			while (true) {
    
    				String str = buf.readLine();
    				if (str != null) {
    					out.println(str);
    				}
    			}
    
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) {
    		ArrayList<Thread> list = new ArrayList<Thread>();
    
    		try {
    			ServerSocket socket = new ServerSocket(5000);
    
    			while (true) {
    				// accept a new connection
    				Socket client = socket.accept();
    				Thread thread = new Thread(new TcpServer(client));
    				list.add(thread);
    				thread.start();
    			}
    		}
    		catch (IOException ioe) {
    			ioe.printStackTrace();
    		}
    	}
    
    }
    

    上述示例是一个多线程服务器, ServerSocket监听5000端口. 当有客户端连接该端口时, socket.accept()将返回一个java.net.Socket对象.

    创建一个线程持有Socket对象, 并与客户端进行通信.

    UDP客户端

    java.net.Datagram可以用作UDP客户端, 其接收到的数据报被封装为java.net.DatagramPacket.

    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    
    
    public class UdpClient {
    	public static void main(String[] args) throws IOException {
    		DatagramSocket socket = new DatagramSocket();
    		String[] msgs = {"1", "2.3", "520"};
    		for (String msg : msgs) {
    			byte[] buf = msg.getBytes();
    			InetAddress addr = InetAddress.getByName("127.0.0.1");
    			DatagramPacket packet = new DatagramPacket(buf, buf.length, addr, 5000);
    			socket.send(packet);
    
    			while (true) {
    				byte[] recvBuf = new byte[256];
    				DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
    				socket.receive(recvPacket);
    				String echo = new String(recvPacket.getData(), 0, recvPacket.getLength());
    				if (echo != null) {
    					System.out.println(echo);
    					break;
    				}
    			}
    		}
    	}
    
    }
    

    DatagramSocket的send方法用于发送数据报, receive用于接收数据报.

    UDP服务器

    import java.io.IOException;
    import java.net.DatagramSocket;
    import java.net.DatagramPacket;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class UdpServer implements Runnable {
    
    	private DatagramSocket socket = null;
    	private DatagramPacket packet = null;
    	private String address;
    
    	public UdpServer(DatagramSocket socket, DatagramPacket packet) {
    		this.socket = socket;
    		this.packet = packet;
    	}
    
    	@Override
    	public void run() {
    		try {
    			String msg = new String(packet.getData());
    			UdpServer.sum += Double.parseDouble(msg);
    			UdpServer.count += 1;
    
    			int port = packet.getPort();
    			InetAddress address = packet.getAddress();
    
    			System.out.println("get msg from" + address);
    
    			String response = new String(recvPacket.getData(), 0, recvPacket.getLength());
    			DatagramPacket sendPacket = new DatagramPacket(response.getBytes(), response.getBytes().length, address, port);
    			socket.send(sendPacket);
    		}
    		catch (IOException ioe) {
    			ioe.printStackTrace();
    		}
    	}
    
    	public static void main(String[] args) {
    		AtomicInteger numThreads = new AtomicInteger(0);
    		ArrayList<Thread> list = new ArrayList<Thread>();
    
    		try {
    			DatagramSocket socket = new DatagramSocket(5000);
    			while (true) {
    				byte[] buf = new byte[100];
    				DatagramPacket packet = new DatagramPacket(buf, buf.length);
    				socket.receive(packet);
    
    				Thread thread = new Thread(new UdpServer(socket, packet));
    				list.add(thread);
    				thread.start();
    				numThreads.incrementAndGet();
    			}
    		}
    		catch (IOException ioe) {
    			ioe.printStackTrace();
    		}
    	}
    
    }
    

    因为UDP不需要维护连接, 服务端和客户端的socket是同样的.

    上文是一个多线程UDP服务器, 不过所有线程持有同一个socket对象和要处理的数据报. 为了保证线程安全最好给socket加锁.

  • 相关阅读:
    调试脚本
    if [ $? -eq 0 ]的含义
    主键和索引的区别
    Docker守护式容器
    Docker容器的基本操作
    Linux下Docker的安装
    Linux下查看占用CPU资源最多的几个进程
    报错:pymysql.err.InternalError: (1054, "Unknown column 'AType' in 'field list'")
    在webpack中使用postcss-px2rem的
    vue环境配置脚手架环境搭建vue工程目录
  • 原文地址:https://www.cnblogs.com/Finley/p/6916762.html
Copyright © 2020-2023  润新知