——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
Java自1.0就已集成网络操作的工具,提供了从基于流的Java.net包以实现网络访问和操作,而Java1.4又假如了nio包,利用异步和面向缓冲的新方式实现了更高效的网络操作工具类,并以此重写了部分io包中的内容。
java.net 包可以大致分为两个部分:
InetAddress 类是表示 IP(Internet 协议)地址的抽象。它拥有两个子类:
- 用于 IPv4 地址的 Inet4Address。
- 用于 IPv6 地址的 Inet6Address。
IP 地址是 IP 使用的4个byte(IPv4)或者16个byte(IPv6);对于Hostname,将通过主机名解析服务(通常是DNS)将他解析为IP地址。
并非所有系统和网络环境都支持 IPv6 协议,在 IPv6 不可用或被显式禁用的情况下,在此情况下,大多数方法在使用 Inet6Address 调用时都将抛出异常。
try {
InetAddress ip = null;
//localhost
ip = Inet4Address.getLocalHost();
sp(ip);//xxxx-xxxx/192.168.1.100 for example
//loopback
ip= Inet4Address.getLoopbackAddress();
sp(ip);//localhost/127.0.0.1
//Indicate a ip & hostname without NS
ip=Inet4Address.getByAddress("Its.A.Fake.Domian",
new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});
sp(ip);//Its.A.Fake.Domian/254.254.254.254
//Indicate a ip with 4bytes for ipv4
ip=Inet4Address.getByAddress(
new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});
sp(ip);///254.254.254.254
//Indicate a ip for hostname using NS
ip=Inet4Address.getByName("www.oracle.com");
sp(ip);//www.oracle.com/184.50.90.127
} catch (UnknownHostException e) {
e.printStackTrace();
}
套接字是实际操作传输层的Java工具对象,基于UDP和TCP的传输都可以通过构建套接字来完成。
端口是TCP和UDP协议中对同一IP地址不同应用对象的区分手段,TCP与UDP的端口是互不影响的。
UDP:User Datagram Protocol是一种面向无连接,基于数据包的传输层协议,一个UDP数据包最大为65535bytes。但是UDP不含任何传输保证,数据的有效性必须由应用自行处理,但相对于TCP,它的连接开销小,相对速度快,很多协议也是基于UDP的,比如很重要的DNS(UDP53)协议,DHCP(UDP67/68)协议;另外很多协议同时支持TCP以及UDP以实现更好的数据交换。
DatagramSocket是Java中处理UDP点对点传输的网络端点对象。通过传输DatagramPacket网络包实现UDP协议的传输。将DatagramPacket目标指定为广播地址可以实现域内网络多播。
DatagramSocket的构造方法允许指定端口,当绑定端口失败时会抛出SocketException;若不指定,将由JVM随机安排一个可用的端口。
由于端口属于系统资源,必须保证端口在使用完后通过close()方法释放。
UDP的组播,多播组通过 D 类 IP 地址和标准 UDP 端口号指定,通过joinGroup(group)加入组,将消息发送到多播组时,该主机和端口的所有预定接收者都将接收到消息,多个 MulticastSocket 可以同时预定多播组和端口,并且都会接收到组数据报。
一个简单的控制台通过本地环回的UDP信息收发
final int port = 36314;
try {
UdpReciver serv = new UdpReciver(port);
StringUdpSender cli=new StringUdpSender(port);
new Thread(serv).start();
new Thread(cli).start();
ts(10000);
serv.stop();
} catch (IOException e) {
e.printStackTrace();
}
class StringUdpSender implements Runnable {
private DatagramSocket soc;
private final String exitFlag;
private final int port;
public StringUdpSender(int port,String exitFlag) throws SocketException {
super();
this.exitFlag = exitFlag;
soc=new DatagramSocket();
this.port=port;
}
public StringUdpSender(int port) throws SocketException {
this(port,"exit");
}
@Override
public void run() {
String msg=null;
BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
DatagramPacket dp=null;
while(true){
try {
msg=br.readLine();
if(!exitFlag.equals(msg)){
dp=new DatagramPacket(msg.getBytes(),
msg.getBytes().length, InetAddress.getByName("localhost"), port);
soc.send(dp);
}else{
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (soc != null && !soc.isClosed()) {
soc.close();
}
}
}
class UdpReciver implements Runnable {
private DatagramSocket soc;
private volatile boolean runFlag = false;
public UdpReciver(int port) throws SocketException {
super();
this.soc = new DatagramSocket(port);
}
public void stop() {
this.setRunFlag(false);
}
public boolean isRunning() {
return runFlag;
}
private synchronized void setRunFlag(boolean runFlag) {
this.runFlag = runFlag;
}
@Override
public void run() {
this.setRunFlag(true);
DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
while (runFlag) {
try {
this.soc.receive(dp);
} catch (IOException e) {
e.printStackTrace();
}
sp(dp.getAddress());
sp(new String(dp.getData(),0,dp.getLength()));
}
if (soc != null && !soc.isClosed()) {
soc.close();
sp("Server stop");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (soc != null && !soc.isClosed()) {
soc.close();
}
}
}
TCP(Transfer Control Protocol)是面向连接的,相对可靠的数据传输协议,TCP的双方必须经过3次握手来达成一个连接,有了连接之后才能开始传输数据,TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和,一旦有问题将请求重发,包丢失也将请求重发,而重复的包将丢弃。这样TCP就达成了一个可靠的数据传送。
TCP的连接时有状态的,双方通过协议信号更新彼此的状态,常见的服务端LISTEN表示监听中,任一方ESTABLISHED 表示连接已建立等。
Socket是Java中TCP端点的抽象,套接字的实际工作由 SocketImpl 类的实例执行。
客户端可以通过构造一个包含服务器IP和端口的Socket尝试打开TCP连接。
打开连接之后,可以通过getInputStream和getOutputStream与对端通信。
ServerSocket是Java中TCP服务端的抽象,绑定一个端口即可开始服务。服务器套接字的实际工作由 SocketImpl 类的实例执行。
ServerSocket通过accept() 阻塞方法获得客户端的连接,然后可以操作这个连接来处理交互。
这个例子实现了简单的TCP服务器和客户端,服务器对客户端送来的String简单的+上”response”返回客户端,服务端实现了简单的异步消息和连接统计。
public class TcpDemo {
public static void main(String[] args) {
final int serverPort = 23333;
InetAddress serverAddr = null;
try {
serverAddr = InetAddress.getLocalHost();
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
//start Server
try {
new Thread(new TcpServer(serverPort)).start();
} catch (IOException e1) {
e1.printStackTrace();
}
//start Client
Socket s = null;
//5 thread client
// for (int i = 0; i < 5; i++) {
// try {
// s = new Socket(serverAddr, serverPort);
// } catch (IOException e) {
// e.printStackTrace();
// }
// new Thread(new TcpClient(s)).start();
// }
//about 400 thread client
while(true){
ts(10);
try {
s = new Socket(serverAddr, serverPort);
} catch (IOException e) {
e.printStackTrace();
}
new Thread(new TcpClient(s)).start();
}
}
}
class TcpClient implements Runnable {
//end connection key word
public static final String ENDCONN="##$$$$##";
Socket socket;
public TcpClient(Socket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
// sp(socket+" Client");
//client request
//emulate chat
Random r=new Random();
int times=r.nextInt(20)+5;
PrintWriter pw=null;
BufferedReader bfr=null;
try {
pw = new PrintWriter(socket.getOutputStream());
bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e1) {
e1.printStackTrace();
}
for (int i = 0; i < times; i++) {
String msg = "hello from"+socket.getLocalPort()+"##"+i;
pw.println(msg);
pw.flush();
try {
msg=bfr.readLine();
} catch (IOException e) {
e.printStackTrace();
}
// sp(msg);
ts(300);
}
pw.println(TcpClient.ENDCONN);
pw.flush();
// if(socket!=null && !socket.isClosed()){
// try {
// socket.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
}
class TcpServer implements Runnable {
private ServerSocket s;
private ExecutorService exes;
private final BlockingQueue connects;
private volatile ConcurrentHashMap> results;
public TcpServer(int port) throws IOException {
super();
this.s = new ServerSocket(port);
this.exes = Executors.newCachedThreadPool();
this.connects = new SynchronousQueue();
this.results = new ConcurrentHashMap>();
}
@Override
public void run() {
// Dispatcher thread
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Socket s = connects.take();
String stamp = s.toString() + Instant.now().toString();
Future result = exes.submit(new RequestHandler(
s));
results.put(stamp, result);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// Result collector thread
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
ts(1000);
List remove=new ArrayList();
results.forEach((String stamp,Future result) -> {
if(result.isDone()){
try {
// sp(result.get());
remove.add(stamp);
} catch (Exception e) {
e.printStackTrace();
}
}else if(result.isCancelled()){
// sp(stamp+" is Cancelled");
remove.add(stamp);
}
});
for (String string : remove) {
results.remove(string);
}
sp("Missions : "+results.size());
}
}
}).start();
//Serv to Client
while (true) {
try {
final Socket socket = s.accept();
try {
this.connects.put(socket);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class RequestHandler implements Callable<String> {
private Socket socket;
public RequestHandler(Socket conn) {
super();
this.socket = conn;
}
@Override
public String call() throws Exception {
//server Business
PrintWriter pw=null;
BufferedReader bfr=null;
String msg=null;
try {
pw = new PrintWriter(socket.getOutputStream());
bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e1) {
e1.printStackTrace();
}
while(true){
msg=bfr.readLine();
if(TcpClient.ENDCONN.equals(msg)){
// sp(this+" recieved ENDCONN");
break;
}
// sp(msg);
pw.println(msg+" : responsed");
pw.flush();
}
socket.close();
return "done :"+socket;
}
}
NetworkInterface 实际指的是网络设备,包括硬件和软件模拟的,比如本地网络环回(lo)就是一个操作系统虚拟的网络设备。
NetworkInterface类提供Java访问本地NetworkInterface的工具,通过它,可以查询比如MAC地址,接口是否物理/虚拟,当前传输MTU值等等有价值的硬件信息,它还有一个很便捷的方法NetworkInterface getByInetAddress(InetAddress addr):可以查询绑定了指定IP地址的网络接口。
Uniform Resource Identifier 统一资源标识符,是表示一个网络中资源的标识字符串,这种标识指定了某种协议以便使用者通过合适的方式使用。
URI由URL(地址)和URN(名称)组成,常见的URI有不透明URI和分层URI
比如mailto:[email protected] urn:isbn:096139210x
就是不透明URI,其特点是方案定义部分不以’/’开始,其结构可以表示为:[scheme:]scheme-specific-part[#fragment]
而更常见的是分层URI,http://java.sun.com/j2se/1.3/ docs/guide/collections/designfaq.html#28 ../../../demo/jfc/SwingSet2/src/SwingSet2.java
等都是分层URI,分层 URI 还要按照下面的语法进行进一步的解析:[scheme:][//authority][path][?query][#fragment]
Java.net中的URI类提供了构造和解析URI的方便工具,可以通过字符串解析URI的各个部分,也可以通过已有URI构建新的URI。最后,可以把从URI提取URL来使用,这也是最常用的。
Uniform Resource Location 统一资源定位符 ,它是指向互联网“资源”的指针。URL是URI的定位部分,他们之间可以用toURI() 和toURL() 方法相互转换 。
URL 可选择指定一个“端口”,它是用于建立到远程主机 TCP 连接的端口号。如果未指定该端口号,则使用协议默认的端口。例如,http 协议的默认端口为 80。还可以指定一个备用端口,用.au:端口号
Java.net中的URL类除了像URI一样可以解析和构造(解析和构造也可以使用URLDecoder和URLEncoder)以外,特别的,它提供了一个极其方便的方法 openConnection()返回一个URLConnection对象,这个方法通过反射查询当前jre支持的协议,如果支持就打开一个协议连接,然后就可以直接操作了。
这里是一个打开HttpURLConnection的例子:
URL url=new URL("http://www.java.com");
URLConnection conn=url.openConnection();
BufferedReader bfr=new BufferedReader(new InputStreamReader(conn.getInputStream()));
String str;
while((str=bfr.readLine())!=null){
sp(str);
}
Java中不仅可以直接使用系统的协议处理程序打开连接,也可以自定义协议处理程序,只需要继承URLStreamHandler抽象类即可。
@Since Java 1.4
Java1.4中加入了新的IO体系(JSR 51),包括面向缓冲区的各种Buffer对象,双向连接的Channel,Selector模式的多路复用。
在Java1.7中,nio又得到了补充(JSR 203),包括更多的文件系统操作API(包括可插拔的自定义的文件系统), 还提供了对socket和文件的异步(Async) I/O操作。
下面是一个基于异步非阻塞模式的服务器简单例子,双方仅仅是简单的进行几十次字符串传递即关闭连接。
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.*;
import static cc.sisel.util.Quic.*;
/**
* @author lz
*
*/
public class AsyncTcpDemo {
public static void main(String[] args) throws IOException,
InterruptedException, ExecutionException {
timeMark();
InetSocketAddress servAddr = new InetSocketAddress("localhost", 22333);
// start server
new Thread(new Runnable() {
@Override
public void run() {
new AsyncServer(servAddr.getPort(), 100).start();
// blocking server main thread to keep async alive
while (true) {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// wait server init
ts(300);
Random r = new Random();
// 5000 clients
for (int i = 0; i < 5000; i++) {
ts(10);
new Thread(new AsyncClient(servAddr, r.nextInt(50) + 10)).start();
}
sp("ALL DONE");
}
}
class AsyncServer {
private AsynchronousServerSocketChannel servsc;
private final int port, backlog;
private boolean started;
public AsyncServer(int port, int backlog) {
super();
this.port = port;
this.backlog = backlog;
init();
}
public void start() {
// start by accept a connection channel
servsc.accept(null, new AcceptCompletionHandler());
this.started = true;
}
private void init() {
// build async group
AsynchronousChannelGroup asyncGroup = null;
try {
// async thread pool
asyncGroup = AsynchronousChannelGroup.withCachedThreadPool(
Executors.newCachedThreadPool(), 10);
} catch (IOException e) {
e.printStackTrace();
}
// open serversocket
try {
this.servsc = AsynchronousServerSocketChannel.open(asyncGroup);
} catch (IOException e) {
e.printStackTrace();
}
// config addr resuse & tcp recieve buffer
try {
this.servsc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
this.servsc.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
} catch (IOException e) {
e.printStackTrace();
}
// bind to port ;ip= 0.0.0.0 for everyone
try {
this.servsc.bind(new InetSocketAddress("0.0.0.0", this.port),
this.backlog);
} catch (IOException e) {
e.printStackTrace();
}
}
public void pendingAccept() {
// after accepted channel reaccept cyclicly
if (this.started && this.servsc.isOpen()) {
servsc.accept(null, new AcceptCompletionHandler());
} else {
throw new IllegalStateException("Controller has been closed");
}
}
// accept complete event handler
class AcceptCompletionHandler implements
CompletionHandler {
final ByteBuffer buffer = ByteBuffer.allocate(1024);
@Override
public void completed(AsynchronousSocketChannel channel,
Object attachment) {
new ASession(channel, AsyncServer.this).start();
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("failed: " + exc);
pendingAccept();
}
}
}
/**
* server session handle it's read & write
*
* @author lz
*
*/
class ASession {
private AsynchronousSocketChannel channel;
private AsyncServer server;
private final ByteBuffer buffer = ByteBuffer.allocate(1024);
private WriteCompletionHandler writeh;
private ReadCompletionHandler readh;
public ASession(AsynchronousSocketChannel channel, AsyncServer server) {
this.channel = channel;
this.writeh = new WriteCompletionHandler();
this.readh = new ReadCompletionHandler();
this.server = server;
}
public void start() {
channel.read(buffer, null, readh);
}
class WriteCompletionHandler implements CompletionHandler {
@Override
public void completed(Integer result, Object attachment) {
if (channel.isOpen()) {
buffer.clear();
channel.read(buffer, 20, TimeUnit.SECONDS, null, readh);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
sp("########################## WRITE FAIL IN SERVER");
exc.printStackTrace();
}
}
class ReadCompletionHandler implements CompletionHandler {
@Override
public void completed(Integer result, Object attachment) {
buffer.flip();
String msg = readBuffer(buffer);
// sp("server recieved:" + msg);
buffer.clear();
// if ENDCODE then close session and pending server next
if (AsyncClient.ENDCODE.equals(msg)) {
if (channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
server.pendingAccept();
} else {
// write response
if (channel != null && channel.isOpen()) {
channel.write(
ByteBuffer.wrap((msg + " responsed.").getBytes()),
10, TimeUnit.SECONDS, null, writeh);
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
sp("########################## READ FAIL IN SERVER");
exc.printStackTrace();
}
}
}
/**
* client emualtor send simple message & recieve
*
* @author lz
*
*/
class AsyncClient implements Runnable {
public static final String ENDCODE = "$$#&$$";
private final InetSocketAddress serverAddr;
private int chatCount;
private AsynchronousSocketChannel client = null;
private final ByteBuffer buffer = ByteBuffer.allocate(1024);
// event handles
private ConnectCompletionHandler connecth;
private WriteCompletionHandler writeh;
private ReadCompletionHandler readh;
private NullCompletionHandler endh;
public AsyncClient(InetSocketAddress serverAddr, int chatCount) {
super();
this.serverAddr = serverAddr;
this.chatCount = chatCount < 1 ? 1 : chatCount;
this.connecth = new ConnectCompletionHandler();
this.writeh = new WriteCompletionHandler();
this.readh = new ReadCompletionHandler();
this.endh = new NullCompletionHandler();
}
@Override
public void run() {
try {
client = AsynchronousSocketChannel.open();
} catch (IOException e) {
e.printStackTrace();
}
// launch conn
if (client != null && client.isOpen()) {
client.connect(serverAddr, null, connecth);
}
}
class ConnectCompletionHandler implements CompletionHandler {
@Override
public void completed(Void result, Object attachment) {
buffer.clear();
if (chatCount > 0) {
ts(300);
if (client.isOpen()) {
client.write(ByteBuffer.wrap(("message:" + chatCount)
.getBytes()), 10, TimeUnit.SECONDS, null, writeh);
chatCount--;
}
} else {
if (client.isOpen()) {
client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,
TimeUnit.SECONDS, null, endh);
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
sp("########################## CONNECT FAIL IN CLIENT");
ts(300);
client.connect(serverAddr, null, connecth);
exc.printStackTrace();
}
}
class WriteCompletionHandler implements CompletionHandler {
@Override
public void completed(Integer result, Object attachment) {
if (client.isOpen()) {
buffer.clear();
client.read(buffer, 20, TimeUnit.SECONDS, null, readh);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
sp("########################## WRITE FAIL IN CLIENT");
exc.printStackTrace();
}
}
class ReadCompletionHandler implements CompletionHandler {
@Override
public void completed(Integer result, Object attachment) {
if (client.isOpen()) {
buffer.flip();
String msg = readBuffer(buffer);
// sp("client recieved :" + msg);
buffer.clear();
if (chatCount > 0) {
// ts(300);
client.write(ByteBuffer.wrap(("message:" + chatCount)
.getBytes()), 10, TimeUnit.SECONDS, null, writeh);
chatCount--;
} else {
client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,
TimeUnit.SECONDS, null, endh);
}
}
}
@Override
public void failed(Throwable exc, Object attachment) {
sp("########################## READ FAIL IN CLIENT");
exc.printStackTrace();
}
}
}
class NullCompletionHandler implements CompletionHandler {
@Override
public void completed(Integer result, Object attachment) {
//just do nothing
}
@Override
public void failed(Throwable exc, Object attachment) {
//just do nothing
}
}
附:
- sp和ts方法,偷懒用:
/**
* 简单的在控制台输出 对于非数组类型,输出.toString() 对于数组类型,输出:类型[内容] 不递归
*
* @param o
* 被打印的对象
*/
public static void sp(Object o) {
if (o == null) {
System.out.println("null");
return;
}
if (o.getClass().isArray()) {
StringBuilder sb = new StringBuilder();
int len = Array.getLength(o);
sb.append(o.getClass().getComponentType().getSimpleName());
if (len == 0) {
sb.append("[]");
System.out.println(sb.toString());
return;
}
sb.append('[');
for (int i = 0; i < len; i++) {
if (i != 0) {
sb.append(':');
}
sb.append(Array.get(o, i));
}
sb.append(']');
System.out.println(sb.toString());
} else {
System.out.println(o);
}
}
/**
* 简单的换行 调用sp("");
*/
public static void sp() {
sp("");
}
/**
* !!仅用于test 或者 demo 简单的包装了Thread.sleep(millis); 并catch
* (InterruptedException e)不作任何处理
*
* @param millis 毫秒数
* @see java.lang.Thread#sleep(long millis)
*/
public static void ts(long millis) {
if(millis<0){
return;
}
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}