I want to fly higher
programming Explorer
posts - 114,comments - 263,trackbacks - 0

Socket用法详解
     在C/S通信模式中,client需要主动创建于server连接的Socket(套接字).服务器端收到了客户端的连接请求,也会创建与客户连接的Socket.Socket可看做是通信两端的收发器.server与client都通过Socket来收发数据.
 
1.构造Socket

 1.Socket()
 2.Socket(InetAddress address,int port) throws UnknownHostException,IOException
 3.Socket(InetAddress addrss,int port,InetAddress localAddr,int localPort) throws IOException
 4.Socket(String host,int port) throws UnknownHostException,IOException
 5.Socket(String host,int port,InetAddress localAddr,int localPort) throws IOExcception

 除了第一个不带参数的构造方法外,其他构造方法都会试图建立与服务器的连接.如果连接成功,就返回Socket对象.如果因为某些原因连接失败,则抛出IOException.
 
2.设定等待建立连接的超时时间

 1.客户端的Socket构造方法请求与server连接时,可能要等待一段时间.默认会一直等待下去,直到连接成功或者出现异常.Socket构造方法请求连接时,受底层网络传输速度的影响,可能处于长时间的等待状态.
 ->希望限定等待连接的时间
 ->Socket socket = new Socket();
    SocketAddress rermoteAddr = new InetSocketAddress("localhost",8000);
    socket.connect(remoteAddr,60000);//设置等待建立连接的超时时间为1分钟
 ->如果1分钟之内连接成功,则connect顺利返回.如果1分钟之内出现异常,则抛出该异常.如果超过了1分钟,即没有连接成功,也没有出现其他异常.则会抛出
 SocketTimeoutException

 2.Socket#connect(SocketAddress endpoint,int timeout).endpoint为服务器的地址,timeout设定超时时间.ms->
  timeout为0,表示永远不会超时.
  
3.设定服务器的地址.

  Socket(InetAddress address,int port)
  Socket(String host,int port)
 InetAddress表示服务器的IP地址.->该类提供了一系列的静态工厂方法.用于构造自身的实例.如
  1.InetAdress addr1 = InetAdress.getLocalHost();
  2.InetAddress addr2 = InetAddress.getByName("10.10.137.44");
  3.InetAddress addr2 = InetAddress.getByName("www.javathinker.org");
  
4.设定客户端的地址
 1.在一个Socket对象中,即包含远程服务器的IP和端口信息,也包含本地客户端的IP地址和端口信息.默认情况下,客户端的IP地址来自客户程序所在的主机,而客户端的端口则有操作系统随机分配.
 ->Socket(InetAddress address,int port,InetAddress localAddress,int localPort) throws IOException
 ->Socket(String host,int port,InetAddress localAddress,int localPort) throws IOException
 上两个方法用来设置客户端的ip端口和地址
 ->这种情况主要适用于一个主机同时属于两个以上的网络,它可能拥有两个以上的IP地址.如一个在Internet,一个在局域网.
 ->如果希望和局域网的服务器程序通讯,则可以以局域网的IP地址作为localAddress来构造Socket.
 
5.客户连接服务器时可能抛出的异常

 1.UnknownHostException:无法识别主机的名字或ip地址时,就会抛出此异常
 2.ConnectException:如果没有服务器监听指定的端口;或者服务器进程拒接连接,则会抛出此异常.
 3.SocketTimeoutException:如果等待连接超时,就会抛出此异常.
 4.BindException:如无法把Socket对象与指定的本地IP地址或端口绑定则会抛出此异常.
 
 IOException
  -UnknownHostException
  -InterruptedIOException
        -SocketTimeoutException
  -SocketException
        -BindException
        -ConnectException
        
6.获取Socket的信息

 1.同时包含了远程服务器的IP和端口信息以及客户本地的IP和端口信息.
 2.获取输出流合输入流,分别用于向服务器发送数据以及接收从服务端发来的数据.
 
 1.getInetAddress():获得远程服务器的IP地址
 2.getPort():获得远程服务器的端口
 3.getLocalAddress():获得客户本地的IP地址
 4.getLocalPort():获得客户本地的端口
 5.getInputStream():获得输入流.如果Sokcet还没有连接或已经关闭或者已经通过shutdownInput方法关闭输入流,则才方法会抛出IOException.
 6.getOutputStream(): 获得输入流..如果Sokcet还没有连接或已经关闭或者已经通过shutdownOutput方法关闭输出流,则才方法会抛出IOException.
 
7.关闭Socket

 1.当client与Server通信结束,应该及时关闭Socktt,以释放Socket占用的包括端口在内的各种资源.
 2.Socket#close方法负责关闭socket.当一个Socket对象被关闭就不能通过其输入和输出流进行io操作.否则会导致IOException.
 3.确保关闭Socket的操作就是被执行,建议把该操作放在finally代码块中.如
  Socket socket = null;
  try
  {
   socket = new Socket("www.thinker.org",80);
   //执行接收和发送数据的操作
   ...
  }
  catch(IOException e)
  {
   e.printStackTrace();
  }
  finally
  {
   try
   {
    if(socket != null)
    {
     socket.close();
    }
    catch(IOException e)
    {
     e.printStackTrace();
    }
   }
  }

 4.Socket类提供了3个状态测试方法.
  1.isClosed()
   1.public synchronized void close() throws IOException {
          synchronized(closeLock) {
              if (isClosed())
                  return;
              if (created)
                  impl.close();
              closed = true;
          }
    }
     2.public boolean isClosed() {
        synchronized(closeLock) {
            return closed;// true if the socket has been closed
        }
    }

  2.isConnected()
   1.true if the socket was successfuly connected to a server
   2. Note: Closing a socket doesn't clear its connection state, which means this method will return true for a closed socket
       (see {@link #isClosed()}) if it was successfuly connected prior to being closed.
  3.isBound()
   1. true if the socket was successfuly bound to an address
   2.Note: Closing a socket doesn't clear its connection state, which means this method will return true for a closed socket
       (see {@link #isClosed()}) if it was successfuly bound prior to being closed.
      
     4.判断一个Socket对象是否处于连接状态,用以下形式:
     boolean isConnected = socket.isConnected() && !socket.isClosed()
     
   8.半关闭Socket

    A进程与B进程通过Socket通信.假定A输出数据,B读入数据.A如何告诉B所有数据已经输出完毕:
    1.A与B交换的是字符流,且一行一行的读写.可事先约定以一个特殊标志作为结束标志,如以"bye"作为结束标志.当A向B发送一行字符串"bye"时,B读到这一行数据时,则停止读数据.{@link EchoServer},{@link EchoClient}
    2.进程A先发送消息,告诉B所发送正文的长度.->再发送正文.->B先获知A发送的正文长度->接下来只要读取完该长度的字符或者字节,就停止读数据.
    3.A发完所有数据后,关闭Socket->B读取A发送的所有数据后->InputStream#read->该方法返回-1.->BufferedReader#readLine->返回null.
     {@link HTTPClient}
    4.Socket#close->输入输出流都被关闭->有时候希望仅关闭输入流或输出流之一->Socket半关闭方法->
     shutdownInput():关闭输入流
     shutdownOutput():关闭输出流
     ->B读取数据时,如果A的输出流已经关闭->B读入所有数据后,就会读到输入流的末尾.
     ->先后调用Socket的shutdonwInput和shutdownOutput方法.仅仅是关闭了输入流和输出流,并不等价Socket#close.->通信结束后,依然要调用Socket的close方法.只有该方法才会释放Socket占用的资源.如占用的本能地端口等.
    5.Socket#isInputShutdown()->输入流关闭,返回true.
       Socket#isOutputShutdown()->输出流关闭,返回true
    6.client与Server通信时,如果有一方突然结束程序或者关闭了Socket或者单独关闭了输入流或输出流.对另一方会造成什么影响.
     {@link Sender} {@link Receiver}.
     

9.设置Socket选项

 1.TCP_NODELAY:表示立即发送数据
 
  1.public void setTcpNoDelay(boolean on) throws SocketException
  2.public boolean getTcpNoDelay() throws SocketException
  3.默认情况,发送数据采用Negale算法.即指发送方发送的数据不会立即发出,而是先放到缓冲区内.等缓冲区区慢了再发出.->发送完一批数据等待接收方对这批数据的回应.->再发送下一批数据.->适用于发送方需要发送大批量数据,且接收方会及时回应的场合->通过减少传输数据的次数来提高通信效率.
  ->对于发送方持续发送小批量数据,且接收方不一定立即发送响应->该算法会使发送方运行很慢->如实时网络游戏
  4.TCP_NODELAY默认值为false->即表示采用Negale算法.->setTcpNoDelay(true)->关闭Socket缓存,确保数据及时发送
  5.if(!socket.getTcpNoDelay()){socket.setTcpNoDelay(true)}
  6.Socket底层不支持该选项,则抛出SocketException.
  
 2.SO_REUSEADDR:表示是否允许重用Socket所绑定的本地地址
 
  1.public void setReuseAddress(boolen on) throws SocketException
  2.public boolean getReuseAddress() throws SocketException
  3.接收方通过Socket#close关闭Socket->如果网络上还有发送到这个Socket的数据,那么底层的Socket不会立刻释放本地端口->会等待一段时间->确保接收到了网络上发送过来的延迟数据->释放端口->Socket收到延迟数据后,不会对这些数据做任何处理->Socket接收延迟数据的目的->确保这些数据不会被其他恰巧绑定到同样端口的新进程接收到.
  4.客户端程序一般采用随机端口->出现两个client程序绑定到同样端口的可能性不大
  5.server程序采用固定端口->server关闭后,其端口可能还会被占用一段时间->此时如果重启程序,端口已经被占用->使得程序无法绑定到给端口->启动失败
  6.确保一个进程关闭Socket后,即使其还未释放端口->同一个主机上的其他进程还可以立即重用该端口->
   if(!socket.getReuseAddress()){socket.setReuseAddress(true)}
  7.该方法必须在Socket还未绑定到一个本地端口之前调用.否则无效.
   1.Socket socket = new Socket();
      socket.setResueAddress(true);
      socket.connect(new InetSocketAddress("localhost",8080));
   2.Socket socket = new Socket();
      socket.setResueAddress(true);
      socket.bind(new InetSocketAddress("localhost",9000));
      socket.connect(new InetSocketAddress("remotehost",8000));
  8.两个公用一个端口的进程必须都调用socket.SetResueAddress(true)->才能使得一个进程关闭Socket后,另一个进程的Socket能立即重用相同端口.
  9.当多个ServerSocket对象同时绑定一个端口时,系统会随机选择一个ServerSocket对象来接收客户端请求->接收客户端请求的ServerSocket对象必须关闭才能轮到其他的ServerSocket对象接收客户端请求。如果不关闭这个ServerSocket对象,那么其他的ServerSocket对象将永远无法接收客户端请求 

 3.SO_TIMEOUT:表示接收数据时的等待时间
 
  1.public void setSoTimeout(int milliseconds) throws SocketException
  2.public int getSoTimeout() throws SocketException
  3.通过Socket的输入流读数据时,如果还未有数据,则等待:
   如:
    byte[] buff = new byte[1024];
    InputStream in = socket.getInputStream();
    in.read(buff);
   ->输入流没有数据,则in.read(buff)就会等待发送方发送数据->结束等待条件:
    1.输入流中有1024个字节->read将其读到buff中->返回读到的字节数
    2.距离输入流末尾还有小雨1024个字节->read读到buff中,返回读到的字节数
    3.读到输入流的末尾
    4.连接已经断开,抛出IOException
    5.Socket#setSoTimeout设置了等待超时时间,超过这一时间则抛出SocketTimeoutException
  4.该选项用于设定接收数据的等待超时时间,单位为毫秒->默认值为0,表示无限等待,永远不会超时.
  5.该方法必须在接收数据之前执行才有效.
  6.输入流的read方法抛出SocketTimeoutException后,Socket依然是连接的->可尝试再次读取数据->
    socket.setTimeout(3 * 60 * 1000);
    byte[] buff = new byte[1024];
    InputStream in = socket.getInputStream();
    
    int len = -1;
    
    do
    {
     try
     {
      len = in.read(buff);
      // 处理读到的数据
      ...
     }
     catch(SocketTimeoutException e)
     {
      e.printStackTrace();
      len = 0;
     }
    }
    while(len != -1)
 
 4.SO_LINGER:表示当执行Socket的close方法时,是否立即关闭底层Socket
 
  1.public void setSoLinger(boolean on,int seconds) throws SocketException
  2.public int getSoLinger() throws SocketException
  3.该选项用来控制Socket关闭时的行为->默认执行Socket的close,该方法会立即返回.但是底层的Socket不立即关闭,会延迟一段时间,知道发送完所有剩余的数据->真正关闭socket->断开连接.
  4.socket.setSoLinger(true,0)->执行Socket#close时,该方法立即返回且底层的Socket也会立即关闭->所有未发送完的剩余数据被丢弃.
  5.socket.setSoLinger(true,60)->
   1.Socket#close->该方法不会立即返回->进入阻塞状态
   2.底层的Socket会尝试发送剩余的数据->返回条件:
    1.底层的Socket已经发送完所有剩余数据
    2.尽管底层的Socket还没有发送完所有的剩余数据->但是已经阻塞了60秒->也会返回->剩余未发送的数据将被丢弃
     1.->close返回后->底层的Socket会被关闭,断开连接
     2.setSoLinger(boolean on,int seconds)->seconds参数以秒为单位->
  6.程序通过输出流写数据时,->仅表示程序向网络提交了一批数据->由网络负责输送到到接收方->程序关闭Socket时,有可能这批数据还在网络上传输,未达到接收方->未发送完的数据指还在网络上传输未被接收方接收的数据
  {@link TestLingerClient} {@link TestLingerServer}
 
 5.SO_SNDBUF:表示发送数据的缓冲区大小
  1.public void setSendBufferSize(int size) throws SocketException
  2.public int getSendBufferSize() throws SocketException
  3.该选项用来表示Socket用于输出数据的缓冲区的大小->底层Socket不支持该选项->set 抛出SocketException
 
 6.SO_RCVBUF:表示接收数据的缓冲区大小
  1.public void setReceiveBufferSize(int size) throws SocketException
  2.public int getReceiveBufferSize() throws SocketException
  3.该选项用来表示Socket的用于输入数据的缓冲区的大小->传输大的连续的数据块,如基于HTTP和FTP协议的通信,可以使用较大缓冲区->减少数据传输的次数->提高传输数据的效率->对于交互频繁且单次传送数据量比较小的通信方式如Telnet和网络游戏,则应该采用交换缓冲区.确保小批量的数据能及时发送给对方.-->设定缓冲区大小的原则使用与SO_SNDBUf选项
  4.底层Socket不支持该选项->set 抛出SocketException.
  
 
 7.SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭.
 
  1.public void setKeepAlive(boolean on) throws SocketException
  2.public boolean getKeepAlive() throws SocketException
  3.该选项为true->底层的TCP实现会监视该连接是否有效->当连接处于空闲状态(连接的两端没有互相传送数据)->超过2小时->本地的TCP实现会发送一个数据包一个远程的Socket->远程Socket没有发回响应->TCP实现持续尝试11分钟->直到接收到响应->12分钟内未收到响应->TCP实现就会自动关闭本地Socket,断开连接->不同的网络平台,TCP实现尝试与远程Socket对话的实现会有所差别.
  4.该选项为false->表示TCP不会监视连是否有效->不活动的client可能会永久存在下去->而不会注意server已经崩溃
  5.if(!socket.getKeepAlive()){socket.setKeepAlive(true)}
  
 8.OOBINLINE:表示是否支持发送一个字节的TCP紧急数据
  //注OOB:out-of-band 带外
  1.public void setOOBInline(boolean on) throws SocketException
  2.public int getOOBInline() throws SocketException
  3.该选项为true,表示支持发送一个自己的TCP紧急数据->Socket#sendUrgentData(int data),用于发送一个字节的TCP紧急数据
  4.该选项为false->接收方收到紧急数据时不做处理,直接丢弃->需要socket.setOOBInline(true)->接收方会将接收到的紧急数据与普通数据放在同样的队列->注:除非采用更高层次的协议,否则接收方处理紧急数据的能力非常有限->紧急数据到来时,接收方不会得到任何通知->因此很难区分普通数据与紧急数据->只好按照同样的方式处理.
  
  
10.服务类型选项

 1.用户去邮局时,可选择不同的服务->发送普通信 | 挂号信 | 快件->价格,发送速度及可靠性均不同.
 2.Internet上传输数据也分为不同的服务器类型.->如发送视频需要较高的宽带,快速到达目的,保证接收方看到连续的画面.
 3.IP规定了4种服务类型,定性的描述服务的质量:
  1.低成本->发送成本低.
  2.高可靠性->保证把数据可靠的送达目的地.
  3.最高吞吐量->一次性可以接收或发送大批量的数据
  4.最小延迟->传输数据的速度要快,把数据快速送达目的地.
 4.4种服务类型可以组合->即可进行或运算
  IPTOS_LOWCOST (0x02)
      IPTOS_RELIABILITY (0x04)
  IPTOS_THROUGHPUT (0x08)
    IPTOS_LOWDELAY (0x10)
    {@link Socket#setTrafficClass}
   5.public void setTrafficClass(int trafficClass) throws SocketException
      public int getTrafficClass() throws SocketException
     
11.设置连接时间,延迟和带宽的相对重要性(注意相对二字)
 1.Socket#setPerformancePreferences(int connectionTime,int latency,int bandwidth)
 2.3个参数为网络传输数据的3项指标
  1.connectionTime-表示用最少时间建立连接
  2.latency-表示最小延迟
  3.bandwidth-表示最高带宽
  ->三项指标的相对重要性.->3项参数的整数之前的相对大小决定了响应参数的相对重要性.
   如setPerformancePreferences(2,1,3)->则表示最高带宽最重要,其实是最少连接时间,最后是最小延迟.
   
12.小结:
 1.通信过程中,如果发送方没有关闭Socket,就突然中止程序,则接收方在接收数据时会抛出SocketException.
 2.发送方发送完数据后,应该及时关闭Socket或关闭Socket的输出流,这样,接收方就能顺利读到输入流的末尾.

部分源代码:

package com.game.landon.socket;

import java.io.IOException;
import java.net.BindException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

/**
 *
 *测试Socket连接服务器可能抛出的异常
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-10-9
 *
 
*/


public class ConnectTester
{
    
public static void main(String[] args)
    
{
        
//默认的host+port
        String host = "localhost";
        
int port  = 8000;
        
        
//通过main参数解析host+port
        if(args.length > 1)
        
{
            host 
= args[0];
            port 
= Integer.parseInt(args[1]);
        }

        
        
new ConnectTester().connect(host, port);
    }

    
    
public void connect(String host,int port)
    
{
        SocketAddress remoteAddress 
= new InetSocketAddress(host,port);
        Socket socket 
= null;
        String result 
= ""
        
        
try
        
{
            
long begin = System.currentTimeMillis();
            socket 
= new Socket();//这里未指定任何参数
//            socket.connect(remoteAddress,5000);//设置超时时间为5秒
            socket.connect(remoteAddress,100);//超时时间设短,用来测试SocketTimeoutException
            long end = System.currentTimeMillis();
            result 
= (end - begin) + "ms";//计算连接所化的时间
        }

        
catch(BindException e)//绑定异常
        {
            result 
= "Local address and port can't be binded";
            
//1.调用Socket#bind方法绑定本地IP|端口
            
//2.Socket构造方法中指定本地IP|端口
                
//->如果本地主机不具有IP地址或者端口已经被占用,则会抛出此异常
        }

        
catch(UnknownHostException e)//无法识别主机server的ip地址
        {
            result 
= "Unknown host";//测试参数 unknownhost 80
        }

        
catch(ConnectException e)//如果没有服务器进程监听指定的端口,或者服务器进程拒绝连接,就会抛出这种异常
        {
            result 
= "Connection refused";//测试参数1: localhost 7777(没有服务器进程监听7777端口)        测试2:server指定连接请求队列的长度
        }

        
catch(SocketTimeoutException e)//服务器超时就会抛出此异常
        {
            result 
= "Timeout";// 测试参数 www.javathinker.org 80
        }

        
catch(IOException e)
        
{
            result 
= "failure";
        }

        
finally
        
{
            
try
            
{
                
if(socket != null)
                
{
                    socket.close();
                }

            }

            
catch(IOException e)
            
{
                e.printStackTrace();
            }

        }

        
        
//打印结果
        System.out.println(remoteAddress + " : " + result );
    }

}

 

package com.game.landon.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;

/**
 *
 *Socket连接超时
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-9
 *
 
*/


public class ConnectTimeout
{
    
public static void main(String[] args)
    
{
        
try
        
{
            Socket socket 
= new Socket();
            SocketAddress serverAddr 
= new InetSocketAddress("localhost"8000);
            
            socket.connect(serverAddr, 
60 * 1000);//指定1分钟超时时间
        }

        
catch(SocketTimeoutException timeoutException)
        
{
            System.out.println(
"connect localhost:8000 timeout in 1minutes:" + timeoutException);
        }

        
catch(IOException ioException)
        
{
            System.out.println(
"connect localhost:8000 fail:" + ioException);
        }

    }

}

 

package com.game.landon.socket;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

/**
 *
 *测试连接到一个http服务器,然后发送http协议的请求,接着接收从http服务器发回的响应结果
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-10-9
 *
 
*/


public class HTTPClient
{
    String host 
= "www.javathinker.org";
    
int port = 80;
    Socket socket;
    
    
public void createSocket() throws Exception
    
{
        socket 
= new Socket(host,port);
    }

    
    
//访问网页www.javathinker.org/index.jsp
    public void communication() throws Exception
    
{
        
//组装http请求协议
        StringBuffer sb = new StringBuffer("GET " + "/index.jsp" + " HTTP/1.1\r\n");
        sb.append(
"Host:www.javathinker.org\r\n");
        sb.append(
"Accept:*/*\r\n");
        sb.append(
"Accept-Language:zh-cn\r\n");
        sb.append(
"Accept-Encoding:gzip,deflate\r\n");
        sb.append(
"User-Agent:Mozilla/4.0(compatible;MSIE 6.0;Window NT 5.0)\r\n");
        sb.append(
"Connection:Keep-Alive\r\n\r\n");
        
        
//发出http请求->request
        OutputStream socketOut = socket.getOutputStream();
        
// 发送数据时,先把字符串形式的请求信息转换为字节数组,即字符串的编码    sb.toString().getBytes()
        socketOut.write(sb.toString().getBytes());
        socket.shutdownOutput();
//关闭输出流
        
        
//接收响应结果->response
        InputStream socketIn = socket.getInputStream();
        
// 接收数据时把接收到的字节写到一个ByteArrayOutputSteam中,其是一个容量能够自动增长的缓冲区.
        
//socketIn.read(buff)返回-1,则表示独到了输入流的末尾
        
// 问题,如果接收的网页数据量很大,则先把这些数据全部保存在ByteArrayOutputSteam,很不明智,因为这些数据会占用大量内存.->
        
//更有效的做法是利用BufferReader来逐行读取数据
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        
byte[] buff = new byte[1024];
        
int len = -1;
        
        
while((len = socketIn.read(buff)) != -1)
        
{
            
//将buff写入buffer
            buffer.write(buff, 0, len);
        }

        
        System.out.println(
new String(buffer.toByteArray()));//把字节数组转为字符串
        socket.close();
    }

    
    
//利用BufferReader逐行读取数据
    public void communication2() throws Exception
    
{
        
//组装http请求协议
        StringBuffer sb = new StringBuffer("GET " + "/index.jsp" + " HTTP/1.1\r\n");
        sb.append(
"Host:www.javathinker.org\r\n");
        sb.append(
"Accept:*/*\r\n");
        sb.append(
"Accept-Language:zh-cn\r\n");
        sb.append(
"Accept-Encoding:gzip,deflate\r\n");
        sb.append(
"User-Agent:Mozilla/4.0(compatible;MSIE 6.0;Window NT 5.0)\r\n");
        sb.append(
"Connection:Keep-Alive\r\n\r\n");
        
        
//发出http请求->request
        OutputStream socketOut = socket.getOutputStream();
        
// 发送数据时,先把字符串形式的请求信息转换为字节数组,即字符串的编码    sb.toString().getBytes()
        socketOut.write(sb.toString().getBytes());
        socket.shutdownOutput();
//关闭输出流
        
        
//接收响应结果->response
        InputStream socketIn = socket.getInputStream();
        
// 问题,如果接收的网页数据量很大,则先把这些数据全部保存在ByteArrayOutputSteam,很不明智,因为这些数据会占用大量内存.->
        
//更有效的做法是利用BufferReader来逐行读取数据
        
        BufferedReader br 
= new BufferedReader(new InputStreamReader(socketIn));
        String data;
        
        
while((data = br.readLine()) != null)
        
{
            System.out.println(data);
        }

        
        socket.close();
    }

    
    
public static void main(Stringargs)  throws Exception
    
{
        HTTPClient client 
= new HTTPClient();
        client.createSocket();
//        client.communication();
        client.communication2();
    }

}

 

package com.game.landon.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

/**
 *
 *发送邮件的SMTP客户程序
 *
 *<pre>
 *1.SMTP-Simple Mail Transfer Protocol,简单邮件传输协议,应用层协议,建立在TCP/IP协议基础之上.
 *2.RFC821
 *3.SMTP服务器默认监听25端口.客户程序请求发送邮件,服务器负责将邮件传输到目的地.
 *4.client会发送一系列SMTP命令,服务器会做出响应,返回应答码及对应答码的描述
 *<pre>
 *
 *<output>
Server>220 EX-01.hec.intra Microsoft ESMTP MAIL Service ready at Wed, 26 Jun 2013 12:56:47 +0800
Client>HELO PC
Server>250 EX-01.hec.intra Hello [10.130.137.44]
Client>MAIL FROM:<wenyong.lv@happyelements.com>
Server>550 5.7.1 Client does not have permissions to send as this sender
Client>RCPT TO:<wenyong.lv@happyelements.com>
Server>503 5.5.2 Need mail command
Client>DATA
Server>503 5.5.2 Need mail command
Client>Subject:hello
I just test smtp using java.
Client>.
Server>500 5.3.3 Unrecognized command
Client>QUIT
Server>500 5.3.3 Unrecognized command
 *</output>
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-25
 *
 
*/


public class MailSender
{
    
private String smtpServer = "EX-01.hec.intra";//公司邮箱服务器的域名
    private int port = 25;
    
    
private PrintWriter getWriter(Socket socket) throws IOException
    
{
        OutputStream socketOut 
= socket.getOutputStream();
        
return new PrintWriter(socketOut, true);
    }

    
    
private BufferedReader getReader(Socket socket) throws IOException
    
{
        InputStream socketIn 
= socket.getInputStream();
        
return new BufferedReader(new InputStreamReader(socketIn));
    }

    
    
/**
     * 
     * 发送一行字符串并接收服务器的响应数据
     * 
     * 
@param str
     * 
@param reader
     * 
@param writer
     
*/

    
private void sendAndReceive(String str,BufferedReader reader,PrintWriter writer) throws IOException
    
{
        
if(str != null)
        
{
            System.out.println(
"Client>" + str);
            writer.println(str);
//是println.需要发送\r\n
        }

        
        String response;
        
        
if((response = reader.readLine()) != null)
        
{
            System.out.println(
"Server>" + response);
        }

    }

    
    
/**
     * 
     * 发送邮件
     * 
     * 
@param msg
     
*/

    
public void sendMail(Message msg)
    
{
        Socket socket 
= null;
        
        
try
        
{
            socket 
= new Socket(smtpServer,port);//连接至邮件服务器
            
            BufferedReader reader 
= getReader(socket);
            PrintWriter writer 
= getWriter(socket);
            
            String localhost 
= InetAddress.getLocalHost().getHostName();
            
            
//因为连接成功时,SMTP服务器会返回一个应答码为220的响应,表示就绪.
            
//214-帮助信息    220-服务就绪 221-服务关闭 250-邮件操作完成    354-开始输入邮件内容,以.结束    421-服务未就绪,关闭传输通道
            
//501 命令参数格式错误    502 命令不支持    503 错误的命令序列    504 命令参数不支持
            sendAndReceive(null, reader, writer);//为了接收服务器的响应数据
            
            sendAndReceive(
"HELO " + localhost, reader, writer);//HELO | EHLO表示邮件发送者的主机地址
            sendAndReceive("MAIL FROM:<" + msg.from + ">", reader, writer);//邮件发送者的邮件地址
            sendAndReceive("RCPT TO:<" + msg.to + ">", reader, writer);//邮件接收者送者的邮件地址
            
            
//邮件内容
            sendAndReceive("DATA", reader, writer);
            writer.println(msg.data);
            
            System.out.println(
"Client>" + msg.data);
            
            
// 发送完毕
            sendAndReceive(".", reader, writer);
            
            
// 退出
            sendAndReceive("QUIT", reader, writer);
        }

        
catch(IOException e)
        
{
            e.printStackTrace();
        }

        
finally
        
{
            
try
            
{
                
if(socket != null)
                
{
                    socket.close();
                }

            }

            
catch(IOException e)
            
{
                e.printStackTrace();
            }

        }

    }

    
    
public static void main(Stringargs)
    
{
        Message message 
= new Message("wenyong.lv@happyelements.com"
                
"wenyong.lv@happyelements.com"
                
"hello",
                
"I just test smtp using java.");
        
        
new MailSender().sendMail(message);
    }

}


/**
 * 
 * 一封邮件消息
 * 
 * 
@author landon
 *
 
*/

class Message
{
    
/** 发送者地址 */
    String from;
    
/** 接收者地址 */
    String to;
    
/** 标题 */
    String subject;
    
/** 正文 */
    String content;
    
/** 数据<标题+正文> */
    String data;
    
    
public Message(String from,String to,String subject,String content)
    
{
        
this.from = from;
        
this.to = to;
        
this.subject = subject;
        
this.content = content;
        
        data 
= "Subject:" + subject + "\r\n" + content;
    }

    
}

 

package com.game.landon.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

/**
 *
 *有些SMTP服务器要求客户提供身份认证信息.本例使用126邮箱进行测试,向qq邮箱测试,注意发送主题和内容要正规一些
 *否则发送时会被认为是垃圾邮件,不被发送
 *
 *1.先发送EHLO
 *2.发送AUTH LOGIN
 *3.采用Base64编码用户名和口令.即可通过认证.
 *
 *{
@link MailSender}
 *Server>550 5.7.1 Client does not have permissions to send as this sender
 *
 *<output>
Server>220 126.com Anti-spam GT for Coremail System (126com[20121016])
Client>HELO PC
Server>250 OK
Client>AUTH LOGIN
Server>334 dXNlcm5hbWU6
Client>c210cGxhbmRvbg==
Server>334 UGFzc3dvcmQ6
Client>YTEyMzQ1Ng==
Server>235 Authentication successful
Client>MAIL FROM:<smtplandon@126.com>
Server>250 Mail OK
Client>RCPT TO:<340706410@qq.com>
Server>250 Mail OK
Client>DATA
Server>354 End data with <CR><LF>.<CR><LF>
Client>Subject:hello,我是stmplandon,测试一下stmp
I just test smtp using java.ok??
Client>.
Server>250 Mail OK queued as smtp4,jdKowECpUWU+gcpR0HQUCA--.1261S2 1372225854
Client>QUIT
Server>221 Bye
 *</output>
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-26
 *
 
*/


public class MailSenderWithAuth
{
    
private String smtpServer = "smtp.126.com";//使用126邮箱进行测试
    private int port = 25;
    
    
private PrintWriter getWriter(Socket socket) throws IOException
    
{
        OutputStream socketOut 
= socket.getOutputStream();
        
return new PrintWriter(socketOut, true);
    }

    
    
private BufferedReader getReader(Socket socket) throws IOException
    
{
        InputStream socketIn 
= socket.getInputStream();
        
return new BufferedReader(new InputStreamReader(socketIn));
    }

    
    
/**
     * 
     * 发送一行字符串并接收服务器的响应数据
     * 
     * 
@param str
     * 
@param reader
     * 
@param writer
     
*/

    
private void sendAndReceive(String str,BufferedReader reader,PrintWriter writer) throws IOException
    
{
        
if(str != null)
        
{
            System.out.println(
"Client>" + str);
            writer.println(str);
//是println.需要发送\r\n
        }

        
        String response;
        
        
if((response = reader.readLine()) != null)
        
{
            System.out.println(
"Server>" + response);
        }

    }

    
    
/**
     * 
     * 发送邮件
     * 
     * 
@param msg
     
*/

    
public void sendMail(Message msg)
    
{
        Socket socket 
= null;
        
        
try
        
{
            socket 
= new Socket(smtpServer,port);//连接至邮件服务器
            
            BufferedReader reader 
= getReader(socket);
            PrintWriter writer 
= getWriter(socket);
            
            String localhost 
= InetAddress.getLocalHost().getHostName();
            
            
//因为连接成功时,SMTP服务器会返回一个应答码为220的响应,表示就绪.
            
//214-帮助信息    220-服务就绪 221-服务关闭 250-邮件操作完成    354-开始输入邮件内容,以.结束    421-服务未就绪,关闭传输通道
            
//501 命令参数格式错误    502 命令不支持    503 错误的命令序列    504 命令参数不支持
            sendAndReceive(null, reader, writer);//为了接收服务器的响应数据
            
            sendAndReceive(
"HELO " + localhost, reader, writer);//HELO | EHLO表示邮件发送者的主机地址
            
            sendAndReceive(
"AUTH LOGIN", reader, writer);//认证命令
            
            
// 新注册的一个126账号
            String userName = new sun.misc.BASE64Encoder().encode("smtplandon".getBytes());
            String pwd 
= new sun.misc.BASE64Encoder().encode("a123456".getBytes());
            
            sendAndReceive(userName, reader, writer);
            sendAndReceive(pwd, reader, writer);
            
            sendAndReceive(
"MAIL FROM:<" + msg.from + ">", reader, writer);//邮件发送者的邮件地址
            sendAndReceive("RCPT TO:<" + msg.to + ">", reader, writer);//邮件接收者送者的邮件地址
            
            
//邮件内容
            sendAndReceive("DATA", reader, writer);
            writer.println(msg.data);
            
            System.out.println(
"Client>" + msg.data);
            
            
// 发送完毕
            sendAndReceive(".", reader, writer);
            
            
// 退出
            sendAndReceive("QUIT", reader, writer);
        }

        
catch(IOException e)
        
{
            e.printStackTrace();
        }

        
finally
        
{
            
try
            
{
                
if(socket != null)
                
{
                    socket.close();
                }

            }

            
catch(IOException e)
            
{
                e.printStackTrace();
            }

        }

    }

    
    
public static void main(Stringargs)
    
{
        Message message 
= new Message("smtplandon@126.com"
                
"340706410@qq.com"
                
"hello,我是stmplandon,测试一下stmp",
                
"I just test smtp using java.ok??");
        
        
new MailSenderWithAuth().sendMail(message);
    }

}

 

package com.game.landon.socket;

import java.io.IOException;
import java.net.Socket;

/**
 *
 *扫描1到1024的端口,用Socket连接这些端口,如果Socket对象创建成功,则说明在这些端口中有服务器程序在监听
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-9-27
 *
 
*/


public class PortScanner
{
    
public static void main(Stringargs)
    
{
        String host 
= "localhost";
        
        
new PortScanner().scan(host);
    }

    
    
/**
     * 
     * 扫描指定的Host下的各端口的服务器程序
     * 
     * 
@param host
     
*/

    
public void scan(String host)
    
{
        Socket socket 
= null;
        
        
for(int port = 0;port < 1024;port++)
        
{
            
try
            
{
                
//该构造方法就会试图建立与服务器的连接
                socket = new Socket(host,port);
                System.out.println(
"There is a Server on Port:" + port);
            }

            
catch(IOException e)
            
{
                System.out.println(
"Can't connect to port:" + port);
            }

            
finally
            
{
                
try
                
{
                    
if(socket != null)
                    
{
                        socket .close();
                    }

                }

                
catch(IOException e)
                
{
                    e.printStackTrace();
                }

            }

        }

    }

}

 

package com.game.landon.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

/**
 *
 *接收数据的服务器程序,每隔1秒接收一行字符串.共接收20行字符串
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-14
 *
 
*/


public class Receiver
{
    
private int port = 8000;
    
private ServerSocket serverSocket;
    
    
private static int stopWay = 1;
    
    
private final int NATURAL_STOP = 1;
    
private final int SUDDEN_STOP = 2;
    
private final int SOCKET_STOP = 3;
    
private final int INPUT_STOP = 4;
    
    
/** 关闭ServerSocket,再结束程序 */
    
private final int SERVERSOCKET_STOP = 5;
    
    
public Receiver() throws IOException
    
{
        serverSocket 
= new ServerSocket(port);
        System.out.println(
"server has started.");
    }

    
    
private BufferedReader getReader(Socket socket) throws IOException
    
{
        InputStream socketIn 
= socket.getInputStream();
        
        
return new BufferedReader(new InputStreamReader(socketIn));
    }

    
    
public void receive() throws Exception
    
{
        Socket socket 
= null;
        socket 
= serverSocket.accept();
        
        BufferedReader br 
= getReader(socket);
        
        
for(int i = 0;i < 20;i++)
        
{
            
//client socket close后,readLine则会抛出此异常
//            Exception in thread "main" java.net.SocketException: Connection reset
//            at java.net.SocketInputStream.read(Unknown Source)
//            at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
//            at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
//            at sun.nio.cs.StreamDecoder.read(Unknown Source)
//            at java.io.InputStreamReader.read(Unknown Source)
//            at java.io.BufferedReader.fill(Unknown Source)
//            at java.io.BufferedReader.readLine(Unknown Source)
//            at java.io.BufferedReader.readLine(Unknown Source)
//            at com.game.landon.socket.Receiver.receive(Receiver.java:59)
            String msg = br.readLine();
            System.out.println(
"receive:" + msg);
            
            TimeUnit.MILLISECONDS.sleep(
1000);
            
            
if(i == 2)
            
{
                
//2.通过提前停止receiver.发现Sender依然会发送全部的20行字符.
                
//因为进入Receiver结束运行,但是底层的Socket并没有立即释放本地端口.OS检测还没有发送给Socket的数据,会使底层Socket继续占用本地端口一段时间
                if(stopWay == SUDDEN_STOP)
                
{
                    System.out.println(
"sudden stop");
                    System.exit(
0);
                }

                
else if(stopWay == SOCKET_STOP)
                
{
                    System.out.println(
"close socket and stop");
                    socket.close();
                    
break;
                }

                
else if(stopWay == INPUT_STOP)
                
{
                    System.out.println(
"shutdown the input and stop");
                    socket.shutdownInput();
                    
break;
                }

                
else if(stopWay == SERVERSOCKET_STOP)
                
{
                    System.out.println(
"close serverSocket and stop");
                    serverSocket.close();
                    
break;
                }

            }

        }

        
        
//1.server和client均已正常结束方式运行的话,因为二者sleep的时间不同.所以server可能再次read的时候会出现异常:
        
//Exception in thread "main" java.net.SocketException: Connection reset
        
//at java.net.SocketInputStream.read(Unknown Source)
        
//这样的话,其实server可能会丢失读了部分数据(Connection reset.Client的Socket已经close了->client的数据可能还在网络传输,即还未被接收方接收).
        
//查一下是否是Socket选项问题
        if(stopWay == NATURAL_STOP)
        
{
            socket.close();
            serverSocket.close();
        }

    }

    
    
public static void main(String[] args) throws Exception
    
{
        
if(args.length > 0)
        
{
            stopWay 
= Integer.parseInt(args[0]);
        }

        
        
new Receiver().receive();
    }

}

 

package com.game.landon.socket;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

/**
 *
 *读取SendClient发送来的数据,直到抵达输入流的末尾
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-18
 *
 
*/


public class ReceiveServer
{
    
public static void main(Stringargs) throws Exception
    
{
        ServerSocket serverSocket 
= new ServerSocket(8000);
        
        Socket socket 
= serverSocket.accept();
        
// 设置接收数据的等待时间
        socket.setSoTimeout(3 * 1000);
        
        InputStream in 
= socket.getInputStream();
        ByteArrayOutputStream bufferStream 
= new ByteArrayOutputStream();
        
byte[] buff = new byte[1024];
        
        
int len = -1;
        
        
do
        
{
            
try
            
{
                
//1.启动ReceiveServer再启动SendClient.->因为client至发送了helloworld.所以不能读到足够的数据填满buff.->一直等待->client睡眠结束,关闭Socket
                
//->ReceiverServer读到输入流末尾->立即结束等待->read返回-1.
                
//2.启动ReceiveServer再启动SendClient->in.read一直在等待->在client随眠期间,关掉client->抛出Exception in thread "main" java.net.SocketException: Connection reset
                
//3.socket.setSoTimeout(3 * 1000)->加上这个后,in.read则会超时抛出异常
                len = in.read(buff);
                
                
if(len != -1)
                
{
                    bufferStream.write(buff, 
0, len);
                }

            }

            
catch(SocketTimeoutException e)
            
{
                System.err.println(
"read timeout");
                len 
= 0;
            }

        }

        
while(len != -1);
        
        System.out.println(
new String(bufferStream.toByteArray()));
    }

}

 

package com.game.landon.socket;

import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

/**
 *
 *发送字符串->sleep->关闭Socket
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-18
 *
 
*/


public class SendClient
{
    
public static void main(Stringargs) throws Exception
    
{
        Socket socket 
= new Socket("localhost",8000);
        OutputStream out 
= socket.getOutputStream();
        
        out.write(
"hello".getBytes());
        out.write(
"world".getBytes());
        
        
// sleep
        TimeUnit.MILLISECONDS.sleep(5 * 1000);
        
        socket.close();
    }

}

 

package com.game.landon.socket;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

/**
 * 
 * 发送数据的客户程序,每隔500毫秒发送一行字符串.共发送20行字符串
 * 
 * 
@author landon
 * 
@since 1.6.0_35
 * 
@version 1.0.0 2013-6-14
 * 
 
*/


public class Sender
{
    
private String host = "localhost";
    
private int port = 8000;
    
private Socket socket;

    
/** 结束通信的方式 */
    
private static int stopWay;

    
/** 自然结束 */
    
private final int NATURAL_STOP = 1;
    
/** 突然终止程序 */
    
private final int SUDDEN_STOP = 2;
    
/** 关闭Socket,再结束程序 */
    
private final int SOCKET_STOP = 3;
    
/** 关闭输出流,再结束程序 */
    
private final int OUTPUT_STOP = 4;
    
    
public static void main(Stringargs) throws Exception
    
{
        
if(args.length > 0)
        
{
            stopWay 
= Integer.parseInt(args[0]);
        }

        
        
new Sender().send();
    }


    
public Sender() throws IOException
    
{
        socket 
= new Socket(host, port);
    }


    
private PrintWriter getWriter(Socket socket) throws IOException
    
{
        
return new PrintWriter(socket.getOutputStream(), true);
    }


    
public void send() throws Exception
    
{
        PrintWriter pw 
= getWriter(socket);
        
        
for(int i = 0;i < 20;i++)
        
{
            String msg 
= "hello_" + i;
            pw.println(msg);
            
            System.out.println(
"send:" + msg);
            
            TimeUnit.MILLISECONDS.sleep(
500);
            
            
if(i == 2)
            
{
                
//1.sender突然中止,server会抛出:Exception in thread "main" java.net.SocketException: Connection reset
                
//at java.net.SocketInputStream.read(Unknown Source)
                if(stopWay == SUDDEN_STOP)
                
{
                    System.out.println(
"sudden stop");
                    System.exit(
0);
                }

                
else if(stopWay == SOCKET_STOP)
                
{
                    System.out.println(
"socket close");
                    socket.close();
                    
break;
                }

                
else if(stopWay == OUTPUT_STOP)//2.如果send以这种方式运行,则server会出现:receive:null    receive:null    receive:null.
                    
//因为已经shutdownOutput.server调用readLine方法时读到了输入流的末尾,因为返回null
                {
                    System.out.println(
"socket shutdown outputstream");
                    socket.shutdownOutput();
                    
break;
                }

            }

        }

        
        
if(stopWay == NATURAL_STOP)
        
{
            socket.close();
        }

    }

}

 

package com.game.landon.socket;

import java.net.Socket;

/**
 *
 *simple client,用来测试服务器的连接请求队列的长度
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-10-9
 *
 
*/


public class SimpleClient
{
    
public static void main(Stringargs) throws Exception
    
{
        Socket s1 
= new Socket("localhost",8000); 
        System.out.println(
"第一次连接成功");
        Socket s2 
= new Socket("localhost",8000);
        System.out.println(
"第二次连接成功");
        
//这里会抛出异常
        
//Exception in thread "main" java.net.ConnectException: Connection refused: connect
        Socket s3 = new Socket("localhost",8000);
        System.out.println(
"第三次连接成功");
        
    }

}

 

package com.game.landon.socket;

import java.net.ServerSocket;

/**
 *
 *一个SimpleServer,用来测试服务器的连接请求队列的长度
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-10-9
 *
 
*/


public class SimpleServer
{
    
public static void main(Stringargs) throws Exception
    
{
        
//设置连接请求队列的长度为2
        ServerSocket serverSocket = new ServerSocket(8000,2);//ServerSocket(int port,int backlog)
        Thread.sleep(6 * 60 * 1000);//sleep 6分钟
        
        
// 个人认为这个连接请求队列只有在server端将连接的socket断掉后,才会从队列移除(属个人猜测)
    }

}

 

package com.game.landon.socket;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 *
 *测试BindException
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2012-10-9
 *
 
*/


public class TestBindException
{
    
public static void main(Stringargs) throws Exception
    
{
        Socket socket 
= new Socket();
        
//直接运行程序,即抛出Exception in thread "main" java.net.BindException: Cannot assign requested address: JVM_Bind
//        socket.bind(new InetSocketAddress(InetAddress.getByName("10.10.0.0"),5678));
        
        
// 抛出异常:Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind
        socket.bind(new InetSocketAddress("127.0.0.1"3306));//3306为mysql所占端口
        
//        Socket socket =  new Socket("localhost",80,InetAddress.getByName("10.10.0.0"),5678);
    }

}

 

package com.game.landon.socket;

import java.io.OutputStream;
import java.net.Socket;

/**
 *
 *测试SO_LINGER选项的一个client.发送100个字符(10000个的话控制台就显示不出来了)给Server.然后调用close关闭Socket
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-18
 *
 
*/


public class TestLingerClient
{
    
public static void main(Stringargs) throws Exception
    
{
        Socket socket 
= new Socket("localhost",8000);
        
//        socket.setSoLinger(true, 0);
        socket.setSoLinger(true60);
        
        OutputStream out 
= socket.getOutputStream();
        StringBuilder builder 
= new StringBuilder();
        
        
for(int i = 0;i < 100;i++)
        
{
            builder.append(i);
        }

        
        
//1.注释掉两句setSoLinger的代码->启动Server再启动client.
            
//1.close方法立即返回 输出close socket cost Time:0 ms
            
//2.因为server执行了sleep->client已经执行了close且client程序本身也结束了->但是server依然受到了全部所有的数据.
                
//因为client执行了Socket#close后,底层的Socekt其实并没有真正关闭,与server的连接仍然存在.底层的Socket会存在一段时间,知道发送完所有的数据.
        
//2.socket.setSoLinger(true, 0)->先后启动server|client.->client执行Socket#close时会强制关闭底层Socket.->所有未发送数据丢失.->Server
            
//结束休眠后,读数据抛出异常->Exception in thread "main" java.net.SocketException: Connection reset
        
//3.socket.setSoLinger(true, 60)->先后启动server|client->client执行Socket#close会阻塞状态,直到等待了60秒.->或者底层已经将所有未发送的数据
            
//发送完毕,才会从close返回。
            
//close socket cost Time:1651 ms->server结束休眠后,因为client还在执行close并处于阻塞状态.client与server之前的连接依然存在.所以可以收到所有数据.
        out.write(builder.toString().getBytes());//发送100个字符
        
        
long begin = System.currentTimeMillis();
        socket.close();
        
long end = System.currentTimeMillis();
        
        System.out.println(
"close socket cost Time:" + (end - begin) + " ms");
    }

}

 

package com.game.landon.socket;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *
 *测试SO_LINGER选项的一个简单server.接收连接请求后,不立即接收client发送的数据,而是睡眠5秒再接收数据.
 *等到其开始接收数据时,client可能已经执行了close方法.server还会接收到client发送的数据吗?
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-18
 *
 
*/


public class TestLingerServer
{
    
public static void main(String[] args) throws Exception
    
{
        ServerSocket serverSocket 
= new ServerSocket(8000);
        Socket socket 
= serverSocket.accept();
        
        Thread.sleep(
5000);//睡眠5秒再读输入流
        
        InputStream in 
= socket.getInputStream();
        ByteArrayOutputStream buffer 
= new ByteArrayOutputStream();
        
byte[] buff = new byte[1024];
        
        
int len = -1;
        
        
do
        
{
            len 
= in.read(buff);
            
            
if(len != -1)
            
{
                buffer.write(buff, 
0, len);
            }

        }
 
        
while (len != -1);
        
        System.out.println(
new String(buffer.toByteArray()));//字节数组转为字符串
    }

}

 

package com.game.landon.socket;

/**
 *
 *测试String字符串长度问题
 *
 *
@author landon
 *
@since 1.6.0_35
 *
@version 1.0.0 2013-6-18
 *
 
*/


public class TestStringMax
{
    
public static void main(String[] args)
    
{
        StringBuilder builder 
= new StringBuilder();
        
        
for(int i = 0;i < 10000;i++)
        
{
            builder.append(i);
        }

        
        
// 打印出了长度
        System.out.println(builder.toString().length());
        
// 但是字符串却无法打印,原因是控制台设置的原因->Window->Preferences->Run/Debug->Console->Fixed with Console->Maximum Character Width
        System.out.println(builder.toString());
    }

}



 

posted on 2013-07-02 16:00 landon 阅读(12293) 评论(2)  编辑  收藏 所属分类: Program

FeedBack:
# re: Java网络编程精解笔记2:Socket详解
2013-07-03 08:45 | 开发吧
以前一直没用过sokect编程。感觉挺你难的...  回复  更多评论
  
# re: Java网络编程精解笔记2:Socket详解
2013-07-06 22:26 | tb
好复杂的啊 现在好像这个都封装好了 直接调用就可以了  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: