转自:https://www.xuebuyuan.com/3215262.html
在Java中可以基于Socket,ServerSocket来实现TCP/IP+BIO的系统间通信。Socket主要用于实现建立连接及网络IO的操作,ServerSocket主要用于实现服务器端端口的监听及Socket对象的获取。
Socket实现客户端的关键代码如下:
/*创建连接,如果域名解析不了会抛出UnkownHostException,当连接不上时会抛出IOException。
*如果希望控制建立连接的超时,可先调用socket=newSocket(),然后调用socket.connect(SocketAddress endpoint, int timeout) 方法
*/
Socket socket = new Socket("www.baidu.com", 80);
/* 将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。
* 如果超过超时值,将引发java.net.SocketTimeoutException,虽然 Socket 仍旧有效。
* 选项必须在进入阻塞操作前被启用才能生效。
* 超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。
*/
socket.setSoTimeout(WAIT_TIME);//read操作时,5s后抛出SocketTimeoutException
System.out.println("socketcreated!");
try {
//获取服务器端返回流
InputStreamips = socket.getInputStream();
BufferedReader bin=new BufferedReader(new InputStreamReader(ips));
//创建向服务器写入流的PrintWriter
System.out.println("inputstream getted:"+bin);
PrintWriter out=new PrintWriter(socket.getOutputStream());
//向服务器发送字符串信息,要注意的是,此次即使写入失败也不会抛出异常信息,
//并且会一直阻塞到写入操作系统或网络IO出现异常为止。
out.print("hello");
//阻塞读取服务器端的返回信息,程序阻塞直到服务器端返回信息或网络IO出现
//异常为止,如果希望在超过一段时间后就不再阻塞了,那么要在创建Socket对象
//后调用socket.setSoTimeout(int timeout)
try{
String inputline=bin.readLine();
}catch (SocketTimeoutException e) {
System.out.println(e.getMessage()+";waitted time is:"+WAIT_TIME);
}
} finally {
socket.close();
}
System.out.println("finished");
服务器端关键代码如下:
// 创建server 对本地指定端口的监听,如果端口冲突抛出SocketException,
//其他网络IO异常抛出IOException
ServerSocket s = new ServerSocket(LISTENING_PORT);
//接受客户端建立连接的请求,并返回一个Socket对象,以便和客户端进行交互
//交互的方式和客户端相同,通过getInputStream()和getOutputStream()
//来进行读写。
//此方法会一种阻塞直到有客户端发送建立连接的请求,如果希望限制阻塞时间,可以
//通过调用setSoTimeout(int timeout)设置超时时间。超过指定时间后会抛出
//s.setSoTimeout(WAIT_TIME);
Socket incoming = s.accept();// 等待客户端连接
if (incoming.isConnected()) {
log.info("creat aconnect");
}
上述代码中,基于Socket和Server能够建立简单的系统间通信。在实际的系统中,通常客户端同时要发送多个请求到服务器端,而服务器端同时要接受多个连接发送的请求。
为满足客户端能同时发送多个请求到服务器端,最简单的办法就是生成多个Socket。但这时就产生两个问题:
1. 生成太多的Socket会消耗过多的本地资源,在客户端机器多,服务器端机器少得情况下,客户端生成太多Socket会导致服务器要支撑非常大的连接数;
2. 生成Socket(建立连接)通常比较慢,因此频繁的创建会导致系统性能不足。
鉴于此,通常采用连接池的方式来维护Socket是比较好的,这样做,第一可以限制能创建的Socket的数目。第二由于将Socket放入连接池中,可以避免重复创建Socket带来的性能下降问题。比如数据库连接池就是典型例子。但是使用连接池同样有缺点,连接池中Socket数目有限,如果同时要用的Socket请求很多,就会造成激烈的竞争和等待。
此外,需要注意的一个问题是如何合理控制等待响应的超时时间,如果不设定超时会导致当服务器端出处理变慢时,客户端得相关请求都在做无限的等待,而客户端资源可能是有限的,才这种情况下很容易造成服务器端出现问题时,客户端挂掉。超时时间的具体设定,一般取决于客户端能承受的请求量及服务器端得处理时间。Java TCP/IP+BIO容易通过setSoTimeout方法设置阻塞等待时间。
为了满足服务器端能同时接受多个连接发送的请求,通常采用的方法是在accept获取Socket之后,将Socket放入一个线程中处理,通常将此方式描述为一个连接一线程。这样服务器可接受多个连接发送请求。这种方式存在的缺点是无论连接上是否有真实的请求,都要耗费一个线程。为避免创建过多的线程导致服务器端资源耗尽,必须限制创建线程的数量。因此采用BIO的情况下服务器端所能支持的并发连接数是有限的。