Java教程 第52页 Java网络教程 – Java网络UDP服务器 以下代码显示了如何编写UDP回显服务器: DatagramSocket socket = new DatagramSocket(12345); DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); while(true) { // Receive the packet socket. receive(packet); //Send back the same packet to the sender socket. send(packet); } Echo 服务器 基于UDP套接字的Echo服务器 import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class Main { public static void main(String[] args) throws Exception { final int LOCAL_PORT = 12345; final String SERVER_NAME = "localhost"; DatagramSocket udpSocket = new DatagramSocket(LOCAL_PORT, InetAddress.getByName(SERVER_NAME)); System.out.println("Created UDP server socket at " + udpSocket.getLocalSocketAddress() + "..."); while (true) { System.out.println("Waiting for a UDP packet..."); DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); udpSocket.receive(packet); displayPacketDetails(packet); udpSocket.send(packet); } } public static void displayPacketDetails(DatagramPacket packet) { byte[] msgBuffer = packet.getData(); int length = packet.getLength(); int offset = packet.getOffset();...
2024-04-01
Java网络教程 – Java网络UDP套接字 TCP套接字是面向连接的,基于流。基于UDP的套接字是无连接的,基于数据报。 使用UDP发送的数据块称为数据报或UDP数据包。每个UDP分组具有数据,目的地IP地址和目的地端口号。 无连接套接字在通信之前不建立连接。 UDP是不可靠的协议,因为它不保证到达分组的传递和顺序。 在无连接协议UDP中,不会有服务器套接字。 在UDP连接中,客户端和服务器发送或接收一组数据,而无需事先知道它们之间的通信。 发送到同一目的地的每个数据块独立于先前发送的数据。 当编码UDP连接时,使用以下两个类。 DatagramPacket类表示UDP数据报。 DatagramSocket类表示用于发送或接收数据报包的UDP套接字。 以下代码显示如何在localhost中创建绑定到端口号12345的UDP套接字。 DatagramSocket udpSocket = new DatagramSocket(12345, "localhost"); DatagramSocket 类提供了一个bind()方法,它允许您将套接字绑定到本地IP地址和本地端口号。 DatagramPacket DatagramPacket包含三个东西: 目的IP地址 目的端口号 数据 DatagramPacket类的构造函数创建一个数据包来接收数据如下: DatagramPacket(byte[] buf, int length) DatagramPacket(byte[] buf, int offset, int length) DatagramPacket类的构造函数创建一个数据包来发送数据如下: DatagramPacket(byte[] buf, int length, InetAddress address, int port) DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) DatagramPacket(byte[] buf, int length, SocketAddress address) DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 以下代码演示如何创建数据报: 以下代码创建一个数据包以接收1024字节的数据。 byte[] data = new byte[1024]; DatagramPacket packet = new DatagramPacket(data, data.length); 以下代码创建一个包的缓冲区大小为1024,并从偏移量8开始接收数据,它将只接收32字节的数据。 byte[] data2 = new byte[1024]; DatagramPacket packet2 = new DatagramPacket(data2, 8, 32); 数据包中的数据总是指定偏移量和长度。我们需要使用offset和length来读取数据包中的数据。
2024-04-01
Java网络教程 – Java URL 绝对URI具有以下通用格式: scheme:scheme-specific-part scheme-specific-part 取决于 scheme 。 例如,httpscheme使用一种格式,并且mailtoscheme使用另一种格式。 URI的另一个通用形式如下,它表示一个URL。 scheme://<authority><path>?<query>#<fragment> scheme访问资源的方法。 它是协议名称,如http,ftp等。 URI中的 scheme 和 path 部分是必需的。所有其他部分均为可选。 path部分可以是空字符串。权限部分指示服务器名称或IP地址。 如果权限部分表示服务器名称,则它可以是 userinfo @ host:port 的形式。 例如,标识本地文件系统中的文件的URL使用文件方案作为file:///c:/documents/java.doc。 URL URI语法在其 path 部分中使用分层语法。 path的多个部分由正斜杠(/)分隔。 query 部分指示通过执行指定的查询获得资源。 它由名称 – 值对组成,用&符号分隔。 名称和值由等号=分隔。 例如, id = 123& num = 5 是一个查询,它有两个部分,id和num。id的值为123,num的值为5。 fragment 部分标识辅助资源,通常是子集由URI的另一部分标识的主资源。 下面是一个URI的例子,它也被分成几部分: URI: //www.w3cschool.cn/java/a.html?id=123#abc Scheme: http Authority: www.w3cschool.cn Path: /java/a.html Query: id=123 Fragment: abc 要在URI中使用空格字符,请使用%20,这是空格的转义形式。 使用%25表示URI中的%字符。 例如,要在查询中使用5.2%的值 //www.w3cschool.cn/details?rate=5.2%25 Java URI类 Java表示一个URI和一个URL作为对象。 它提供以下四个类,您可以使用它们将URI和URL用作Java程序中的对象: java.net.URI java.net.URL java.net.URLEncoder java.net.URLDecoder 以下代码创建一个URI对象。 URI baseURI = new URI("http://www.www.w3cschool.cn"); 要创建具有相对URI字符串的URI,并使用baseURI解析它 URI baseURI = new URI("http://www.www.w3cschool.cn"); URI relativeURI = new URI("welcome.html"); URI resolvedRelativeURI = baseURI.resolve(relativeURI); 完整代码 import java.net.URI; public class Main { public static void main(String[] args) throws Exception { String baseURIStr = "http://www.www.w3cschool.cn/a/b/c/index.html?id=1&rate=5%25#foo"; String relativeURIStr = "../x/y/z/welcome.html"; URI baseURI =...
2024-04-01
Java网络教程 – Java数据报套接字通道 java.nio.channels.DatagramChannel类表示数据报通道。默认情况下,它是阻塞。要使其无阻塞,请使用configureBlocking(false)方法。 要创建 DatagramChannel ,请调用其 open()静态方法之一。 要将其用于IP多播,请将多播组的地址类型指定为其 open()方法的参数。 open()方法创建一个没有连接的DatagramChannel对象。 例子 以下代码显示如何基于数据报通道创建Echo服务器。 import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; public class Main { public static void main(String[] args) throws Exception { DatagramChannel server = null; server = DatagramChannel.open(); InetSocketAddress sAddr = new InetSocketAddress("localhost", 8989); server.bind(sAddr); ByteBuffer buffer = ByteBuffer.allocate(1024); while (true) { System.out.println("Waiting for a message from" + " a remote host at " + sAddr); SocketAddress remoteAddr = server.receive(buffer); buffer.flip(); int limits = buffer.limit(); byte bytes[] = new byte[limits]; buffer.get(bytes, 0, limits); String msg = new String(bytes); System.out.println("Client at " + remoteAddr + " says: " + msg); buffer.rewind(); server.send(buffer, remoteAddr); buffer.clear(); } //server.close(); } } 上面的代码生成以下结果。 例2 以下代码基于数据报通道创建客户端程序。 import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; public class...
2024-04-01
Java网络教程 – Java异步套接字通道 使用以下两个套接字通道类来执行异步套接字操作: java.nio.channels.AsynchronousServerSocketChannel java.nio.channels.AsynchronousSocketChannel 以下代码显示如何创建使用异步服务器套接字通道的服务器应用程序。 例子 import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.nio.charset.Charset; public class Main { public static void main(String[] args) throws Exception { AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel .open(); String host = "localhost"; int port = 8989; InetSocketAddress sAddr = new InetSocketAddress(host, port); server.bind(sAddr); System.out.format("Server is listening at %s%n", sAddr); Attachment attach = new Attachment(); attach.server = server; server.accept(attach, new ConnectionHandler()); Thread.currentThread().join(); } } class Attachment { AsynchronousServerSocketChannel server; AsynchronousSocketChannel client; ByteBuffer buffer; SocketAddress clientAddr; boolean isRead; } class ConnectionHandler implements CompletionHandler<AsynchronousSocketChannel, Attachment> { @Override public void completed(AsynchronousSocketChannel client, Attachment attach) { try { SocketAddress clientAddr = client.getRemoteAddress(); System.out.format("Accepted a connection from %s%n", clientAddr); attach.server.accept(attach, this); ReadWriteHandler rwHandler...
2024-04-01
Java线程教程 – Java线程局部变量 线程局部变量分隔每个线程的变量的值。 java.lang包中的ThreadLocal类提供了一个线程局部变量的实现。 它有四个方法:get(),set(),remove()和initialValue()。 get()和set()方法分别用于获取和设置线程局部变量的值。 您可以使用remove()方法删除该值。 initialValue()方法设置变量的初始值,它具有受保护的访问。要使用它,子类ThreadLocal类并重写此方法。 例子 以下代码显示如何使用ThreadLocal类。 public class Main { public static void main(String[] args) { new Thread(Main::run).start(); new Thread(Main::run).start(); } public static void run() { int counter = 3; System.out.println(Thread.currentThread().getName()+ " generated counter: " + counter); for (int i = 0; i < counter; i++) { CallTracker.call(); } } } class CallTracker { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); public static void call() { int counter = 0; Integer counterObject = threadLocal.get(); if (counterObject == null) { counter = 1; } else { counter = counterObject.intValue(); counter++; } threadLocal.set(counter); String threadName = Thread.currentThread().getName(); System.out.println("Call counter for " + threadName + " = " + counter); } } 上面的代码生成以下结果。
2024-04-01
Java线程教程 – Java Fork/Join框架 fork/join框架通过利用机器上的多个处理器或多个内核来解决问题。 该框架有助于解决涉及并行性的问题。 fork/join框架创建一个线程池来执行子任务。 当线程在子任务上等待完成时,框架使用该线程来执行其他线程的其他未决子任务。 java.util.concurrent包中的以下四个类是学习fork/join框架的核心: ForkJoinPool ForkJoinTask RecursiveAction RecursiveTask ForkJoinPool类的一个实例表示一个线程池。 ForkJoinTask类的一个实例表示一个任务。 ForkJoinTask类是一个抽象类。它有两个具体的子类:RecursiveAction和RecursiveTask。 Java 8添加了一个称为CountedCompleter的ForkJoinTask类的抽象子类。 该框架支持两种类型的任务:不产生结果的任务和产生结果的任务。 RecursiveAction类的实例表示不产生结果的任务。 RecursiveTask类的实例表示产生结果的任务。 CountedCompleter任务可能产生结果,也可能不产生结果。 这两个类,RecursiveAction和RecursiveTask,提供了一个抽象的compute()方法。 我们应该继承这些类之一,并为compute()方法提供一个实现。 例子 ForkJoinTask类的以下两个方法在任务执行期间提供了两个重要的功能: fork()方法从异步执行的任务启动一个新的子任务。join()方法让任务等待另一个任务完成。 import java.util.ArrayList; import java.util.List; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class Main { public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); IntSum task = new IntSum(3); long sum = pool.invoke(task); System.out.println("Sum is " + sum); } } class IntSum extends RecursiveTask<Long> { private int count; public IntSum(int count) { this.count = count; } @Override protected Long compute() { long result = 0; if (this.count <= 0) { return 0L; }else if (this.count == 1) { return (long) this.getRandomInteger(); } List<RecursiveTask<Long>> forks = new ArrayList<>(); for (int i = 0; i <...
2024-04-01
Java线程教程 – Java执行器 框架提供了一种将任务提交与任务执行分离的方法。 java.util.concurrent包中的Executor接口是执行器框架的基础。 它是一个只有一个方法的接口,如图所示: public interface Executor { void execute (Runnable command); } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class RunnableTask implements Runnable { private int taskId; private int loopCounter; public RunnableTask(int taskId, int loopCounter) { this.taskId = taskId; this.loopCounter = loopCounter; } public void run() { for (int i = 1; i <= loopCounter; i++) { try { System.out.println("Task #" + this.taskId + " - Iteration #" + i); Thread.sleep(1000); } catch (Exception e) { System.out.println("Task #" + this.taskId + " has been interrupted."); break; } } } } public class Main { public static void main(String[] args) { final int THREAD_COUNT = 3; final int LOOP_COUNT = 3; final int TASK_COUNT = 5; // Get an executor with...
2024-04-01
Java线程教程 – Java同步器 同步器对象与一组线程一起使用。 它维护一个状态,根据它的状态,它让一个线程通过或强迫它等待。 本节将讨论四种类型的同步器: Semaphores Barriers Latches Exchangers 信号量 信号量用于控制可以访问资源的线程数。 java.util.concurrent包中的Semaphore类表示信号量同步器。 您可以使用其构造函数创建信号量,如下所示: final int MAX_PERMITS = 3; Semaphore s = new Semaphores(MAX_PERMITS); Semaphore类的另一个构造函数使用公平作为第二个参数 final int MAX_PERMITS = 3; Semaphore s = new Semaphores(MAX_PERMITS, true); // A fair semaphore 如果你创建一个公平的信号量,在多线程请求许可的情况下,信号量将保证先进先出(FIFO)。也就是说,首先请求许可的线程将首先获得许可。 要获取许可证,请使用acquire()方法。 如果许可证可用,它立即返回。 它阻止如果许可证不可用。线程在等待许可证可用时可能中断。 Semaphore类的其他方法允许您一次性获取一个或多个许可证。要释放许可证,请使用release()方法。 以下代码显示了一个Restaurant类,它使用信号量来控制对表的访问。 import java.util.Random; import java.util.concurrent.Semaphore; class Restaurant { private Semaphore tables; public Restaurant(int tablesCount) { this.tables = new Semaphore(tablesCount); } public void getTable(int customerID) { try { System.out.println("Customer #" + customerID + " is trying to get a table."); tables.acquire(); System.out.println("Customer #" + customerID + " got a table."); } catch (InterruptedException e) { e.printStackTrace(); } } public void returnTable(int customerID) { System.out.println("Customer #" + customerID + " returned a table."); tables.release(); } } class...
2024-04-01
Java线程教程 – Java显式锁 显式锁定机制可以用于协调对多线程环境中的共享资源的访问。 在java.util.concurrent.locks包中声明的Lock接口定义了显式锁定操作。 ReentrantLock类在同一个包中,是Lock接口的具体实现。 Lock接口声明如下: public interface Lock { void lock(); Condition newCondition(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); } lock()方法获取一个锁的行为与使用synchronized关键字相同。 我们必须在完成锁定后通过调用Lock接口的unlock()方法释放锁定。 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Instantiate the lock object private Lock myLock = new ReentrantLock(); public void updateResource() { try { // Acquire the lock myLock.lock(); } finally { // Release the lock myLock.unlock(); } } } 例子 下面的代码模拟哲学家,假设ReentrantLock类的一个对象表示一个fork。 import java.util.concurrent.locks.Lock; class Philosopher { private Lock leftFork; private Lock rightFork; private String name; // Philosopher"s name public Philosopher(Lock leftFork, Lock rightFork, String name) { this.leftFork = leftFork; this.rightFork = rightFork; this.name = name; } public void think() { System.out.println(name + " is thinking...");...
2024-04-01