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加鎖.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM