- 浏览: 194186 次
- 性别:
- 来自: 厦门
文章分类
最新评论
-
行者买刀:
qxpidt 写道楼主,我想问下,嵌套这些WEB应用后,如何操 ...
JDIC一个能够用java调用ie内核的包 -
qxpidt:
楼主,我想问下,嵌套这些WEB应用后,如何操作你登录的COOK ...
JDIC一个能够用java调用ie内核的包 -
lookforwarding:
...
java重写JSplitPane的UI,设置分隔条的颜色 -
v韧竹v:
最后那个抽象工厂模式,CarType是不是定义错了,应该abs ...
设计模式之略见一斑(工厂模式Factory) -
l7810:
这与模板模式有什么区别啊?
设计模式之略见一斑(建造模式builder)
1.ServerSocket;
ServerSocket有以下3 个选项。
l SO_TIMEOUT:表示等待客户连接的超时时间。
l SO_REUSEADDR:表示是否允许重用服务器所绑定的地址。
l SO_RCVBUF:表示接收数据的缓冲区的大小。
SO_TIMEOUT选项
l 设置该选项:public void setSoTimeout(int timeout) throws SocketException
l 读取该选项:public int getSoTimeout () throws IOException
SO_TIMEOUT 表示ServerSocket 的accept()方法等待客户连接的超时时间,以毫
秒为单位。如果SO_TIMEOUT的值为0,表示永远不会超时,这是SO_TIMEOUT的
默认值。
当服务器执行ServerSocket的accept()方法时,如果连接请求队列为空,服务器就
会一直等待,直到接收到了客户连接才从accept()方法返回。如果设定了超时时间,那
么当服务器等待的时间超过了超时时间,就会抛出SocketTimeoutException,它是
InterruptedException的子类。
example:
import java.io.*; import java.net.*; public class TimeoutTester{ public static void main(String args[])throws IOException{ ServerSocket serverSocket=new ServerSocket(8000); serverSocket.setSoTimeout(6000); //等待客户连接的时间不超过6 秒 Socket socket=serverSocket.accept(); socket.close(); System.out.println("服务器关闭"); } }
运行6秒后会抛出异常:
C:\chapter03\classes>java TimeoutTester
Exception in thread "main" java.net.SocketTimeoutException: Accept timed out
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(Unknown Source)
at java.net.ServerSocket.implAccept(Unknown Source)
at java.net.ServerSocket.accept(Unknown Source)
at TimeoutTester.main(TimeoutTester.java:8)
如果把程序中的“serverSocket.setSoTimeout(6000)”注释掉,那么serverSocket.
accept()方法永远不会超时,它会一直等待下去,直到接收到了客户的连接,才会从
accept()方法返回。
2.ServerSocket 的构造方法
l ServerSocket()throws IOException
l ServerSocket(int port) throws IOException
l ServerSocket(int port, int backlog) throws IOException
l ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
在以上构造方法中,参数port 指定服务器要绑定的端口(服务器要监听的端口),
参数backlog指定客户连接请求队列的长度,参数bindAddr指定服务器要绑定的IP地址。
import java.net.*; public class Client { public static void main(String args[])throws Exception{ final int length=100; String host="localhost"; int port=8000; Socket[] sockets=new Socket[length]; for(int i=0;i<length;i++){ //试图建立100 次连接 sockets[i]=new Socket(host, port); System.out.println("第"+(i+1)+"次连接成功"); } Thread.sleep(3000); for(int i=0;i<length;i++){ sockets[i].close(); //断开连接 } } } import java.io.*; import java.net.*; public class Server { private int port=8000; private ServerSocket serverSocket; public Server() throws IOException { serverSocket = new ServerSocket(port,3); //连接请求队列的长度为3 System.out.println("服务器启动"); } public void service() { while (true) { Socket socket=null; try { socket = serverSocket.accept(); //从连接请求队列中取出一个连接 System.out.println("New connection accepted " + socket.getInetAddress() + ":" +socket.getPort()); }catch (IOException e) { e.printStackTrace(); }finally { try{ if(socket!=null)socket.close(); }catch (IOException e) {e.printStackTrace();} } } } public static void main(String args[])throws Exception { Server server=new Server(); Thread.sleep(60000*10); //睡眠10 分钟 //server.service(); } }
Client 试图与Server 进行100 次连接。在Server 类中,把连接请求队列的长度设
为3。这意味着当队列中有了3 个连接请求时,如果Client 再请求连接,就会被Server
拒绝
结是如下:
第1 次连接成功
第2 次连接成功
第3 次连接成功
Exception in thread "main" java.net.ConnectException: Connection refused: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(Unknown Source)
at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at Client.main(Client.java:10)
从以上打印结果可以看出,Client 与Server 在成功地建立了3 个连接后,就无法
再创建其余的连接了,因为服务器的队列已经满了。
如果是改为
public static void main(String args[])throws Exception {
Server server=new Server();
//Thread.sleep(60000*10); //睡眠10 分钟
server.service();
}
2.1 设定绑定的IP地址
如果主机只有一个IP 地址,那么默认情况下,服务器程序就与该IP 地址绑定。
ServerSocket的第4 个构造方法ServerSocket(int port, int backlog, InetAddress bindAddr)
有一个bindAddr 参数,它显式指定服务器要绑定的IP地址,该构造方法适用于具有多
个IP地址的主机。假定一个主机有两个网卡,一个网卡用于连接到Internet, IP地址
为222.67.5.94,还有一个网卡用于连接到本地局域网,IP 地址为192.168.3.4。如果服
务器仅仅被本地局域网中的客户访问,那么可以按如下方式创建ServerSocket:
ServerSocket serverSocket=new ServerSocket(8000,10,InetAddress.getByName ("192.168.3.4"));
2.2默认构造方法的作用
ServerSocket有一个不带参数的默认构造方法。通过该方法创建的ServerSocket不
与任何端口绑定,接下来还需要通过bind()方法与特定端口绑定。
这个默认构造方法的用途是,允许服务器在绑定到特定端口之前,先设置
ServerSocket的一些选项。因为一旦服务器与特定端口绑定,有些选项就不能再改变了。
在以下代码中,先把ServerSocket 的SO_REUSEADDR 选项设为true,然后再把
它与8000 端口绑定:
ServerSocket serverSocket=new ServerSocket();
serverSocket.setReuseAddress(true); //设置ServerSocket 的选项
serverSocket.bind(new InetSocketAddress(8000)); //与8000 端口绑定
如果把以上程序代码改为:
ServerSocket serverSocket=new ServerSocket(8000);
serverSocket.setReuseAddress(true); //设置ServerSocket 的选项
那么serverSocket.setReuseAddress(true)方法就不起任何作用了,因为SO_
REUSEADDR选项必须在服务器绑定端口之前设置才有效。
打印结果如下:
第1 次连接成功
第2 次连接成功
第3 次连接成功
…
第100 次连接成功
3. setResuseAddress(boolean b) 的用法
这个选项与Socket的SO_REUSEADDR选项相同,用于决定如果网络上仍然有数
据向旧的ServerSocket传输数据,是否允许新的ServerSocket绑定到与旧的ServerSocket
同样的端口上。SO_REUSEADDR选项的默认值与操作系统有关,在某些操作系统中,
允许重用端口,而在某些操作系统中不允许重用端口。
当ServerSocket 关闭时,如果网络上还有发送到这个ServerSocket 的数据,这个
ServerSocket不会立刻释放本地端口,而是会等待一段时间,确保接收到了网络上发送
过来的延迟数据,然后再释放端口。
许多服务器程序都使用固定的端口。当服务器程序关闭后,有可能它的端口还会
被占用一段时间,如果此时立刻在同一个主机上重启服务器程序,由于端口已经被占
用,使得服务器程序无法绑定到该端口,服务器启动失败,并抛出BindException:
Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind
为了确保一个进程关闭了ServerSocket 后,即使操作系统还没释放端口,同一个
主机上的其他进程还可以立刻重用该端口,可以调用ServerSocket 的setResuse-
Address(true)方法:
if(!serverSocket.getResuseAddress())serverSocket.setResuseAddress(true);
值得注意的是,serverSocket.setResuseAddress(true)方法必须在ServerSocket还没有
绑定到一个本地端口之前调用,否则执行serverSocket.setResuseAddress(true)方法无效。
此外,两个共用同一个端口的进程必须都调用serverSocket.setResuseAddress(true)方法,
才能使得一个进程关闭ServerSocket后,另一个进程的ServerSocket还能够立刻重用相
同端口。
4.SO_RCVBUF选项
SO_RCVBUF表示服务器端的用于接收数据的缓冲区的大小,以字节为单位。一般
说来,传输大的连续的数据块(基于HTTP或FTP协议的数据传输)可以使用较大的缓
冲区,这可以减少传输数据的次数,从而提高传输数据的效率。而对于交互式的通信
(Telnet和网络游戏),则应该采用小的缓冲区,确保能及时把小批量的数据发送给对方。
ServerSocket serverSocket=new ServerSocket();
int size=serverSocket.getReceiveBufferSize();
if(size<131072) serverSocket.setReceiveBufferSize(131072); //把缓冲区的大小设为128K
serverSocket.bind(new InetSocketAddress(8000)); //与8 000 端口绑定
5.创建线程池
l 除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。每个线程本
身都会占用一定的内存(每个线程需要大约1M 内存),如果同时有大量客户
连接服务器,就必须创建大量工作线程,它们消耗了大量内存,可能会导致
系统的内存空间不足。
l 如果线程数目固定,并且每个线程都有很长的生命周期,那么线程切换也是
相对固定的。不同操作系统有不同的切换周期,一般在20 毫秒左右。这里所
说的线程切换是指在Java 虚拟机,以及底层操作系统的调度下,线程之间转
让CPU的使用权。如果频繁创建和销毁线程,那么将导致频繁地切换线程,
因为一个线程被销毁后,必然要把CPU转让给另一个已经就绪的线程,使该
线程获得运行机会。在这种情况下,线程之间的切换不再遵循系统的固定切
换周期,切换线程的开销甚至比创建及销毁线程的开销还大。
采用线程池:预先创建了一些工作线程,它们不断从工作队列中取出任务,然后执行该任务。当工
作线程执行完一个任务时,就会继续执行工作队列中的下一个任务。线程池具有以下
优点:
l 减少了创建和销毁线程的次数,每个工作线程都可以一直被重用,能执行多
个任务。
l 可以根据系统的承载能力,方便地调整线程池中线程的数目,防止因为消耗
过量系统资源而导致系统崩溃。
package multithread2; import java.util.LinkedList; public class ThreadPool extends ThreadGroup { private boolean isClosed=false; //线程池是否关闭 private LinkedList<Runnable> workQueue; //表示工作队列 private static int threadPoolID; //表示线程池ID private int threadID; //表示工作线程ID public ThreadPool(int poolSize) { //poolSize 指定线程池中的工作线程数目 super("ThreadPool-" + (threadPoolID++)); setDaemon(true); workQueue = new LinkedList<Runnable>(); //创建工作队列 for (int i=0; i<poolSize; i++) new WorkThread().start(); //创建并启动工作线程 } /** 向工作队列中加入一个新任务,由工作线程去执行该任务 */ public synchronized void execute(Runnable task) { if (isClosed) { //线程池被关则抛出IllegalStateException异常 throw new IllegalStateException(); } if (task != null) { workQueue.add(task); notify(); //唤醒正在getTask()方法中等待任务的工作线程 } } /** 从工作队列中取出一个任务,工作线程会调用此方法 */ protected synchronized Runnable getTask()throws InterruptedException{ while (workQueue.size() == 0) { if (isClosed) return null; wait(); //如果工作队列中没有任务,就等待任务 } return workQueue.removeFirst(); } /** 关闭线程池 */ public synchronized void close() { if (!isClosed) { isClosed = true; workQueue.clear(); //清空工作队列 interrupt(); //中断所有的工作线程,该方法继承自ThreadGroup类 } } /** 等待工作线程把所有任务执行完 */ public void join() { synchronized (this) { isClosed = true; notifyAll(); //唤醒还在getTask()方法中等待任务的工作线程 } Thread[] threads = new Thread[activeCount()]; //enumerate()方法继承自ThreadGroup类,获得线程组中当前所有活着的工作线程 int count = enumerate(threads); for (int i=0; i<count; i++) { //等待所有工作线程运行结束 try { threads[i].join(); //等待工作线程运行结束 }catch(InterruptedException ex) { } } } /** 内部类:工作线程 */ private class WorkThread extends Thread { public WorkThread() { //加入到当前ThreadPool 线程组中 super(ThreadPool.this,"WorkThread-" + (threadID++)); } public void run() { while (!isInterrupted()) { //isInterrupted()方法继承自Thread 类,判断线程是否被中断 Runnable task = null; try { //取出任务 task = getTask(); }catch (InterruptedException ex){} // 如果getTask()返回null 或者线程执行getTask()时被中断,则结束此线程 if (task == null) return; try { //运行任务,异常在catch代码块中捕获 task.run(); } catch (Throwable t) { t.printStackTrace(); } } //#while } //#run() } //#WorkThread 类 }
在ThreadPool类中定义了一个LinkedList类型的workQueue成员变量,它表示工
作队列,用来存放线程池要执行的任务,每个任务都是Runnable实例。ThreadPool 类
的客户程序(利用ThreadPool 来执行任务的程序)只要调用ThreadPool 类的execute
(Runnable task)方法,就能向线程池提交任务。在ThreadPool类的execute()方法中,先
判断线程池是否已经关闭。如果线程池已经关闭,就不再接收任务,否则就把任务加
入到工作队列中,并且唤醒正在等待任务的工作线程。
在ThreadPool 类的构造方法中,会创建并启动若干工作线程,工作线程的数目由
构造方法的参数poolSize决定。WorkThread类表示工作线程,它是ThreadPool类的内
部类。工作线程从工作队列中取出一个任务,接着执行该任务,然后再从工作队列中
取出下一个任务并执行它,如此反复。
工作线程从工作队列中取任务的操作是由ThreadPool 类的getTask()方法实现的,
它的处理逻辑如下:
l 如果队列为空并且线程池已关闭,那就返回null,表示已经没有任务可以执
行了;
l 如果队列为空并且线程池没有关闭,那就在此等待,直到其他线程将其唤醒
或者中断;
l 如果队列中有任务,就取出第一个任务并将其返回。
线程池的join()和close()方法都可用来关闭线程池。join()方法确保在关闭线程池之
前,工作线程把队列中的所有任务都执行完。而close()方法则立即清空队列,并且中
断所有的工作线程。
ThreadPool 类是ThreadGroup类的子类。ThreadGroup 类表示线程组,它提供了一
些管理线程组中线程的方法。例如,interrupt()方法相当于调用线程组中所有活着的线
程的interrupt()方法。线程池中的所有工作线程都加入到当前ThreadPool 对象表示的线
程组中。ThreadPool类在close()方法中调用了interrupt()方法:
/** 关闭线程池 */
public synchronized void close() {
if (!isClosed) {
isClosed = true;
workQueue.clear(); //清空工作队列
interrupt(); //中断所有的工作线程,该方法继承自ThreadGroup类
}
}
以上interrupt()方法用于中断所有的工作线程。interrupt()方法会对工作线程造成以
下影响:
l 如果此时一个工作线程正在ThreadPool 的getTask()方法中因为执行wait()方
法而阻塞,则会抛出InterruptedException;
l 如果此时一个工作线程正在执行一个任务,并且这个任务不会被阻塞,那么
这个工作线程会正常执行完任务,但是在执行下一轮while (!isInterrupted())
{…}循环时,由于isInterrupted()方法返回true,因此退出while循环。
如例程3-7所示,ThreadPoolTester 类用于测试ThreadPool的用法。
评论
不过有个问题想请教博主
我想发送Http GET请求,使用了socket,请问有没有办法能够使用同一个socket进行多次请求呢?
因为不想每发一次请求就进行一次连接,不知是否有可能,我对HTTP协议不是很了解。请博主指教。
发表评论
-
UtilCommon
2016-08-16 07:23 0aaaaaa -
Java面试题全集(上)
2016-08-14 17:23 0版权声明:本文为博 ... -
设计模式之略见一斑(Visitor访问者模式)
2010-02-25 15:02 3226在开发中,我们可能会经常碰到客户提了一新的需求,那么在现 ... -
设计模式之略见一斑(Mediator中介者模式)
2010-02-25 10:07 1880设计模式 写道 通常,面向对象的软件开发要求尽可能 ... -
设计模式之略见一斑(Chain of Responsibility责任链模式)
2010-02-24 15:00 1494设计模式 写道 面向对 ... -
设计模式之略见一斑(Observer观察者模式)
2010-02-23 17:15 2209用户通常通过调用对象的方法来收集该对象的信息。但是当这 ... -
设计模式之略见一斑(Memento备忘录模式)
2010-02-23 11:46 1463有时候,我们需要 ... -
设计模式之略见一斑(Template Method模板方法模式)
2010-02-22 21:33 1440模板方法模式就是预 ... -
设计模式之略见一斑(策略模式strategy)
2010-02-22 17:56 1968java设计模式 写道 ... -
设计模式之略见一斑(状态模式State)
2010-02-22 15:42 1695设计模式中的状态模式相对比较简单,简单的说就是对某 ... -
设计模式之略见一斑(解释器模式Interpreter)
2010-02-22 10:27 3384解释器模式是 ... -
设计模式之略见一斑(命令模式Command)
2010-02-22 09:49 1555终于把构造模式的几种设计模式写完,接下来开始写的行为模 ... -
设计模式之略见一斑(享元模式flyweight)
2010-02-10 11:39 1439flyweight模式中文解释为轻量极模式,所以顾名 ... -
设计模式之略见一斑(代理模式Proxy)
2010-02-10 11:04 1964普通对象所需要完成的任务就是通过公共接口为外界提供自己所承诺的 ... -
设计模式之略见一斑(装饰模式decorator)
2010-02-09 11:08 1820标头:(引自设计模式 ... -
设计模式之略见一斑(组合模式Composite)
2010-02-08 14:45 1498定义: 将对象以树形结构组织起来,以达成"部 ... -
设计模式之略见一斑(外观模式Facade)
2010-02-07 09:30 1388外观模式又称门面模式,它是为了给子系统中提供一个一致的界面,从 ... -
设计模式之略见一斑(桥梁模式bridge)
2010-02-06 18:04 1560桥梁模式的宗旨就是将 ... -
设计模式之略见一斑(适配器模式Adapter)
2010-02-03 10:39 1604适配器模式就是基于客户提供的接口和功能,再此基础上实 ... -
设计模式之略见一斑(原型模式Prototype)
2010-02-01 17:15 1698原型模式允许一个对象 ...
相关推荐
在"Java Socket 视频流转发Socket"这个主题中,我们将深入探讨如何使用Java Socket来处理视频流的传输。 首先,让我们了解什么是ServerSocket。在Java中,`java.net.ServerSocket`类是服务器端使用的,它监听特定...
- **Socket的使用**:客户端通过`Socket(String host, int port)`建立到服务器的连接,`connect()`方法指定服务器地址和端口。 3. **数据交换** - **输入/输出流**:Socket通信基于字节流。服务器端使用`Socket....
Java Socket 是一种网络通信协议,它是Java编程语言中实现客户端-服务器模型的基础。Socket 提供了低级别的、面向连接的、双向通信的网络接口,允许应用程序通过网络进行数据传输。在本示例中,我们将深入探讨Java ...
这个"Java Socket使用实例"提供了一个基础的网络通信模板,通过学习和实践,你可以进一步了解Socket通信的原理,掌握在网络环境下进行数据交换的方法。在实际项目中,Socket通信常用于构建分布式系统、实时聊天应用...
在Java编程语言中,Socket是实现网络通信的基础组件,它为两台计算机之间的通信提供了低级别的接口。在本文中,我们将深入探讨Java Socket...通过理解Socket的工作原理和使用方法,你可以创建出功能强大的网络客户端。
`ServerSocket`的`accept()`方法是阻塞式的,当有客户端连接请求时,它会返回一个新的`Socket`对象,用于与客户端建立实际的数据交换通道。 客户端则使用`Socket`类来建立连接。例如,`Socket s = new Socket("168....
这个经典版本可能包含了一系列关于如何有效使用Java Socket进行网络通信的实例和最佳实践。 首先,让我们深入了解Java Socket的基本概念。Socket通常被称为套接字,它是两台计算机之间通信的端点。在TCP/IP协议族中...
- **Socket类**:在Java中,`java.net.Socket`类代表一个TCP连接,它封装了输入/输出流,用于与服务器进行数据交换。 - **ServerSocket类**:`java.net.ServerSocket`类用于监听客户端的连接请求,一旦有连接请求...
Java Socket 编程实现两台主机间的通信 Java Socket 编程是 Java 语言中用于实现网络通信的编程...通过了解 Java Socket 编程的原理、实现方法和应用场景,可以更好地应用 Java Socket 编程来实现网络通信和数据传输。
在这个"一个简单的java socket程序"中,我们将会探讨如何构建一个基础的服务器端(Server)和客户端(Client)模型,这对于初学者来说是一个很好的入门级教程。 首先,我们需要理解Socket的基本概念。Socket在Java...
Java Socket是Java编程语言中用于网络通信的核心API,它提供了低级别的、面向连接的、基于TCP/IP协议的网络通信服务。本教程将深入探讨Java Socket的相关知识点,旨在帮助开发者理解和掌握如何在Java环境中构建网络...
在Java中,我们可以使用`HttpServletRequest`对象的`getHeader()`方法来获取这个值。 ```java HttpServletRequest request = ...; // 获取HttpServletRequest实例 String clientIp = request.getHeader("X-...
- 服务器端使用`ServerSocket(int port)`创建一个监听特定端口的Socket,当有客户端请求连接时,会调用`accept()`方法来接收连接并返回一个新的Socket对象。 2. **输入输出流** - 一旦连接建立,Socket提供了两个...
在这个简单的Java Socket模型中,我们将深入探讨如何创建和使用Sockets进行数据传输,这对于初学者来说是一块重要的学习内容。 首先,理解Socket的基本概念是至关重要的。Socket可以被看作是两台计算机之间通信的...
在Java中,可以使用`Socket(String host, int port)`构造函数创建一个Socket实例,其中host参数是服务器的IP或域名,port参数是服务器的端口号。例如: ```java Socket socket = new Socket("www.example.com", 80...
在这个"Java socket聊天室+窗口抖动"项目中,我们将探讨如何使用Java Socket构建一个基本的多人在线聊天室,并且实现窗口抖动功能,以增加用户体验。 首先,我们要理解Socket的工作原理。Socket是两台计算机之间...
Java Socket编程是网络编程中的重要一环,它提供了在不同计算机之间进行低级通信的机制。在这个"java socket传输demo"中,我们将深入探讨Java如何使用Socket类来实现客户端和服务器端之间的数据交换。 首先,Socket...
2. 接受连接:当有客户端请求连接时,ServerSocket调用accept()方法,返回一个新的Socket对象,代表客户端的连接。 ```java Socket clientSocket = serverSocket.accept(); ``` 3. 数据传输:通过Socket对象的输入流...
Java Socket 多线程是网络编程中的一个重要概念,它结合了Java的并发处理能力和Socket通信技术,使得服务器能够同时处理多个客户端的连接请求。在Java中,Socket是用于在网络环境中进行双向通信的类,而多线程则允许...
Java Socket实用教程主要涵盖的是Java中的网络编程技术,特别是如何使用Socket进行客户端和服务端的通信。Socket在Java中是实现TCP/IP通信的基础,它提供了进程间网络通信的能力。本教程可能包括以下几个关键知识点...