`
行者买刀
  • 浏览: 191040 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

java socket用法(一)

    博客分类:
  • J2SE
阅读更多

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的用法。 

分享到:
评论
2 楼 行者买刀 2009-04-12  
B/S端建立的是TCP连接,端口一般都是80.理论上socket是可以的,但是很多协议内部封装了,像开源包HttpClient一样也是封装的,你可以研究下.
1 楼 tuoxie007 2009-04-07  
总结的不错,领教了
不过有个问题想请教博主
我想发送Http GET请求,使用了socket,请问有没有办法能够使用同一个socket进行多次请求呢?
因为不想每发一次请求就进行一次连接,不知是否有可能,我对HTTP协议不是很了解。请博主指教。

相关推荐

    Java语言Socket接口用法详解.doc

    Java语言Socket接口用法详解.doc Java语言Socket接口用法详解.doc Java语言Socket接口用法详解.doc Java语言Socket接口用法详解.doc Java语言Socket接口用法详解.doc Java语言Socket接口用法详解.doc Java语言Socket...

    java对Socket进行了封装,可以通过注解调用双端的方法,让socket使用起来极其简单方便,是一个jar包

    所以我对socket进行了封装,客户端和服务端可以通过配置注解,让双方相互调用注解方法,提供了专门的工具类,让注解方法可以支持返回list或map等集合或其他的类型,三行代码你就可以完美的使用socket进行通信,支持...

    java Socket 通讯 源代码

    通过socket客户/服务器编程可以创建一个能被许多人使用的分布式程序,并且所有客户均可以用统一的前端进行工作,并与服务器进行通信。  要想与服务器通信必须具备三个条件:服务器程序、客户程序和连接它们的...

    java socket用法

    java socket用法 含有java示例

    java Socket与C#通信中中文乱码问题的解决方案

    近正在做一个项目,其中遇到了一个问题是java与.NET之间的通信问题。具体的问题是这样的:  客户端使用java,服务器端使用的是C#。两者之间使用基于TCP的Socket通信方式。可是,做了一个测试小例子,结果从客户端...

    Java Socket实现的简单聊天室实例

    使用socket实现的简单聊天室功能,包含客户端和服务器端代码,可以通过本实例快速了解java中socket的使用方法

    java c++ 通信之间的乱码解决方法

    java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket java c++ socket

    封闭式Java Socket代码

    封闭式Java Socket代码,提供TCP UDP 阻塞和非阻塞的方法,无需知道具体实现方式,只需要Main.java(Server)Main2.java(Client)Main(UDP)三个例程,简单建立联接。SocketIO接口提供精简化的方法,一体化输入输出,...

    简单的java socket源码

    简单的java socket源码,使用方法同我的另一个RMI实例的资源说明

    三步学会Java Socket编程

     java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK-1.3文档。  . Accept方法用于产生"阻塞",直到接受到一个连接,并且返回一个客户端...

    Java中通信类Socket的用法

    网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的...Socket通常常使用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个port号唯一确定。

    Java SSLSocket的使用

    NULL 博文链接:https://410063005.iteye.com/blog/1751243

    HP Sokcet ForJava

    Socket的dll文件,采用的是非常新的HP-Socket版本,demo是整套java工程,导入eclipse修改HpSocket.java文件中的服务器的IP和端口就可以测试,里面还包含了客户端接收服务器端返回的信息方法,看懂了客户端基本也可以...

    基于java实现Socket套接字一对一聊天系统源码+项目说明.zip

    【资源说明】 基于java实现Socket套接字一对一聊天系统源码+项目说明.zip... // 方法一: socket.close(); // 方法二: // 给服务器发送相关消息,由服务器断开与指定客户端的连接 【备注】更多详情介绍见项目说明!

    Java TCP IP Socket编程(原书第2版)

    这本PDF书主要说java的 Socket的原理和用法

    websocket + java socket 聊天吗平台

    该项目是一个基于WebSocket和Java Socket的交互型BS平台,提供聊天、直播、模拟面试和弹幕互动功能。用户可以进行点对点聊天、群聊...建议先了解WebSocket和Java Socket的基本概念和使用方法,然后比对项目代码进行理解

    基于Socket的java网络编程

    基于Socket的java网络编程, Socket通讯的过程 Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server...

    Java TCP/IP Socket编程 原书第2版

    现在几乎所有互联网应用程序都使用Java Socket进行通信,而本书专注的、教导式的方法,使读者能够快速掌握所有计算机通信的任务和技术要点。 本书特点: 对Socket编程的重点进行了集中简明的介绍,以使读者能够迅速...

    Java中的Socket的用法

     Java中的网络通信时通过Socket实现的,Socket分为ServerSocket和Socket两大类,ServerSocket用于服务器端,可以通过accept方法监听请求,监听请求后返回Socket,Socket用于完成具体数据传输,客户端也可以使用...

    Java+TCP/IP+Socket编程 原书第2版

    在7CP/IP Socket首次发布时,Java作为一个新的未被关注的编程语 言刚出现。然而,自2001年以来,Java的网络编程能力已经得到了很大程度的扩展,而且 持续的应用优势已经使其成为了一个占主导地位的平台。考虑到Java...

Global site tag (gtag.js) - Google Analytics