Donald_Draper 阅读(195) 评论(0)
java Socket用法详解:http://blog.csdn.net/semillon/article/details/7515926
JAVA 通过 Socket 实现 TCP 编程 :http://blog.csdn.net/qq_23473123/article/details/51461894
Socket和ServerSocket学习笔记:http://www.cnblogs.com/rond/p/3565113.html
Socket缓冲区探讨,是否有拆包的方式:http://www.cnblogs.com/cfas/p/5795350.html
Socket TCP粘包拆包 :http://blog.csdn.net/robinjwong/article/details/50155115
Socket 粘包 封包 拆包 :http://blog.csdn.net/bristar_zon/article/details/38239285
对UDP socket缓冲区的理解:http://blog.csdn.net/vonzhoufz/article/details/39080259
通过mark和reset方法重复利用InputStream:http://zhangbo-peipei-163-com.iteye.com/blog/2022460
在以前的工作中接触过MINA,没有深入研究过,从今天起,我们从Socket(阻塞式),JavaNIO(非阻塞式),JUC,再到MINA,深入解析一下java网络编程,今天我们先来看一下java Socket编程,我们要做的是一个可以计算加法与乘法的Server,客户端发送计算数值,服务器(多线程)接受客户端发送过来的数值,并将计算结果发送给客户端,首先我们要定义客户端与
服务器通信协议格式:如下图




具体实现

协议常量:

package socket;
/**
 * 协议常量
 * @author donald
 * 2017年2月11日
 * 下午12:05:29
 */
public class ProtocolConstants {
	/**
	 * 加法协议编码
	 */
	public static final String SUM_PROTOCOL_300000 = "300000";
	/**
	 * 乘法协议编码
	 */
	public static final String MULTI_PROTOCOL_300100 = "300100";
	/**
	 * 计算结果
	 */
	public static final String ACK_PROTOCOL_300200 = "300200";
	/**
	 * 协议编码长度
	 */
	public static final Integer PROTOCOL_CODE_LENGTH = 6;
	/**
	 * 协议操作数长度
	 */
	public static final Integer OPERATE_NUM_LENGTH = 10;
	/**
	 * 协议计算结果长度
	 */
	public static final Integer PROTOCOL_ACK_LENGTH = 2;
	/**
	 * 协议结束符
	 */
	public static final String PROTOCOL_END = "\r\n";
	/**
	 * 协议结束符长度
	 */
	public static final Integer PROTOCOL_END_LENGTH = 2;
	/**
	 * 字符集
	 */
	public static final String CHARSET_UTF8 = "UTF-8";
}


协议辅助工具:

package socket;
/**
 * 协议辅助工具
 * @author donald
 * 2017年2月11日
 * 下午12:05:13
 */
public class ProtocolUtils {
	private static volatile ProtocolUtils instance = null;
	public static synchronized ProtocolUtils getInstance(){
		if(instance == null){
			instance = new ProtocolUtils();
		}
		return instance;
	}
	/**
	 * 如果orgStr的长度不够length,左侧填充0
	 * @param orgStr
	 * @param length
	 * @return
	 */
	public String fillString(String orgStr, int length){
		if(orgStr.length() < length){
        	int orgStrLength = orgStr.length();
        	int zeroNum = length - orgStrLength;
        	for(int i=0; i< zeroNum; i++){
        		orgStr = "0" + orgStr;
        	}
        }
		return orgStr;
	}
	public static void main(String[] args) {
		 int firstNum = 15;
         String firstNumStr = String.valueOf(firstNum);
         if(firstNumStr.length() <= 10){
        	 firstNumStr = ProtocolUtils.getInstance().fillString(firstNumStr,10);
         }
         System.out.println("=======firstNumStr:"+firstNumStr);
         System.out.println("=======firstNumStr Integer Value:"+Integer.valueOf(firstNumStr));
	}
}

服务端:

package socket;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * Socket Server
 * @author donald
 * 2017年2月12日
 * 下午2:43:58
 */
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpServerSocket {

	private static int  port = 4003;//端口号
	private static int backlog = 20;//Server最大连接数
	private static InetAddress serverHost = null;//server地址
	static{
		try {
			serverHost = InetAddress.getLocalHost();
			System.out.println("主机名:"+serverHost.getHostName());
			System.out.println("主机地址:"+serverHost.getHostAddress());
		} catch (UnknownHostException e) {
			System.out.println("获取主机地址信息异常:"+e.getMessage());
			e.printStackTrace();
		}
	}
	//搭建服务器端
    public static void main(String[] args){
    	TcpServerSocket tcpServerSocket = new TcpServerSocket();
        //创建一个服务器端Socket,即SocketService 
    	tcpServerSocket.startServer();
    }
    public  void startServer(){
            ServerSocket tcpServer=null;
            ExecutorService  exec = Executors.newCachedThreadPool();
            try {
				tcpServer=new ServerSocket(port,backlog,serverHost);
				System.out.println("服务器启动成功.........");
				 while(true){
		            	try {
		            		Socket socket = tcpServer.accept();
							System.out.println("服务器接受客户端连接.........");
							SocketHandleRunnable sHandleRunnable = new SocketHandleRunnable(socket);
			            	exec.execute(sHandleRunnable);
			            	//有返回值
			            	//exec.submit(sHandleRunnable);
						} catch (IOException e) {
							e.printStackTrace();
						}
		            }
			} catch (IOException e) {
				System.out.println("服务器启动失败:"+e.getMessage());
				e.printStackTrace();
			}
            finally{
            	try {
					tcpServer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
            	exec.shutdown();
            }
           
    }
}


Socket处理线程:

package socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
 * Socket处理线程
 * @author donald
 * 2017年2月12日
 * 下午2:54:47
 */
public class SocketHandleRunnable implements Runnable {
	private  static ProtocolUtils protocolUtils = null;
	private Socket socket;
	static {
		protocolUtils = ProtocolUtils.getInstance();
	}
	public SocketHandleRunnable(Socket socket) {
		super();
		this.socket = socket;
	}
	@Override
	public void run() {
		System.out.println("开始处理Socket........");
		InputStream serverInputStream = null;
		OutputStream serverOutputStream = null;
		BufferedInputStream serverBufferInputStream = null;
		BufferedOutputStream serverBufferOutputStream = null;
		  try {
			serverInputStream = socket.getInputStream();
			serverOutputStream =  socket.getOutputStream();
			serverBufferInputStream = new BufferedInputStream(serverInputStream);
			serverBufferOutputStream = new BufferedOutputStream(serverOutputStream);
			int protocolLenght = ProtocolConstants.PROTOCOL_CODE_LENGTH + ProtocolConstants.OPERATE_NUM_LENGTH*2 + ProtocolConstants.PROTOCOL_END_LENGTH;
			boolean flag = true;
			while(flag){
				if(serverBufferInputStream.available() >= protocolLenght){
					 byte[] protcBuf  = new byte[protocolLenght];
		        	 int readLength = serverBufferInputStream.read(protcBuf, 0, protocolLenght);
		        	 System.out.println("从接受缓冲区读取的实际协议长度为:"+readLength);
		        	 if(readLength == protocolLenght){
		        		 String protcStr = new String(protcBuf,ProtocolConstants.CHARSET_UTF8);
		        		 String endStr = protcStr.substring(protcStr.length()-2, protcStr.length());
		        		 if(endStr.equals(ProtocolConstants.PROTOCOL_END)){
		        			 System.out.println("开始解析计算协议......");
		        			 String protocolCode = protcStr.substring(0, 6);
			        		 String firstNumStr = protcStr.substring(6, 16);
			        		 int firstNum = Integer.valueOf(firstNumStr);
			        		 String secNumStr = protcStr.substring(16, 26);
			        		 int secNum = Integer.valueOf(secNumStr);
			        		 System.out.println("计算协议解析完毕......");
			        		 int result = 0;
			        		 if(protocolCode.equals(ProtocolConstants.SUM_PROTOCOL_300000)){
			        			 result = firstNum + secNum;
			        		 }
			        		 if(protocolCode.equals(ProtocolConstants.MULTI_PROTOCOL_300100)){
			        			 result = firstNum*secNum;
			        		 }
			        		 System.out.println("开始发送计算结果协议......");
			        		 //将计算结果值发送给Client
			        		//发送计算结果协议编码
					        byte[] AckProtocolBytes = ProtocolConstants.ACK_PROTOCOL_300200.getBytes(ProtocolConstants.CHARSET_UTF8);
					        serverBufferOutputStream.write(AckProtocolBytes);
			        		 //结果值
			        		 String resultStr = String.valueOf(result);
			        		 System.out.println("服务器计算结果为:"+resultStr);
			        		 byte[] resultBytes =  resultStr.getBytes(ProtocolConstants.CHARSET_UTF8);
			        		 //结果长度
			        		 int resultLength = resultStr.length();
			        		 String reultLenthStr = String.valueOf(resultLength);
			        		 reultLenthStr = protocolUtils.fillString(reultLenthStr, ProtocolConstants.PROTOCOL_ACK_LENGTH);
			        		 byte[] resultLengthBytes = reultLenthStr.getBytes(ProtocolConstants.CHARSET_UTF8);
			        		 serverBufferOutputStream.write(resultLengthBytes);
			        		 //发送结果值
			        		 serverBufferOutputStream.write(resultBytes);
			        		 //发送结束符
			        		 serverBufferOutputStream.write(ProtocolConstants.PROTOCOL_END.getBytes(ProtocolConstants.CHARSET_UTF8));
			        		 System.out.println("发送计算结果协议结束......");
			        		//将缓冲区发送到Client,我们这里是强制清空缓冲区,实际不要这样做,以免影响数据传输效率
			        		 serverBufferOutputStream.flush();
		        		 }
		        	 }
		        	 if(readLength < 0){
		        		 System.out.println("与客户端失去连接.....");
		        	 }
		        	 if(readLength < protocolLenght){
		        		 //从缓冲区继续读取数据,直至读取的数据长度为协议长度+结束符;
		        		 //当数据解析异常时,可以用InputStream.skip(long n),丢弃一些数据,以保证一次协议包的完成性
		        	 }
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			 try {
				serverBufferInputStream.close();
				serverBufferOutputStream.close();
				socket.close(); 
			} catch (IOException e) {
				e.printStackTrace();
			} 
		     
		}
		System.out.println("Socket处理结束........");
	     
	}

}

客户端:

package socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * Socket Client
 * @author donald
 * 2017年2月12日
 * 下午2:43:46
 */
public class TcpClient {
	private  static ProtocolUtils protocolUtils = null;
	private static final String ip ="192.168.132.126";
	private static final int port = 4003;
	private static final int bufferSize = 1024;
	static {
		protocolUtils = ProtocolUtils.getInstance();
	}
	// 搭建客户端
	public static void main(String[] args){
			Socket socket = null;
			OutputStream clientOutputStream = null;
			InputStream clientInputStream = null;
			BufferedInputStream clientBufferInputStream = null;
			BufferedOutputStream clientBufferOutputStream = null;
			try {
				socket = new Socket(ip, port);
				socket.setSendBufferSize(bufferSize);
				socket.setReceiveBufferSize(bufferSize);
				socket.setKeepAlive(true);
				if(socket.isConnected()){
					System.out.println("连接服务器成功...........");
					try {
						clientOutputStream =  socket.getOutputStream();
						clientInputStream = socket.getInputStream();
						clientBufferInputStream = new BufferedInputStream(clientInputStream);
						clientBufferOutputStream = new BufferedOutputStream(clientOutputStream);
						 //只做长度为10以内的加法与乘法
						//加法
						System.out.println("发送加法计算协议开始...........");
						//发送协议编码
			            byte[] sumProtocolBytes = ProtocolConstants.SUM_PROTOCOL_300000.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(sumProtocolBytes);
			            //发送第一个操作数
			            int firstNum = 15;
			            String firstNumStr = String.valueOf(firstNum);
			            //如果操作符不够长度,则左侧补零
			            firstNumStr = protocolUtils.fillString(firstNumStr, ProtocolConstants.OPERATE_NUM_LENGTH);
			            byte[] firstNumBytes = firstNumStr.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(firstNumBytes);
			            //发送第二个操作数
			            int secondNum = 6;
			            String secondNumStr = String.valueOf(secondNum);
			            
			            secondNumStr = protocolUtils.fillString(secondNumStr, ProtocolConstants.OPERATE_NUM_LENGTH);
			            byte[] secondNumBytes = secondNumStr.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(secondNumBytes);
			            //发送协议结束符
			            byte[] endBytes = ProtocolConstants.PROTOCOL_END.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(endBytes);
			            System.out.println("发送加法计算协议结束...........");
			            System.out.println("发送乘法计算协议开始...........");
			            // 乘法
			            byte[] sumProtocolBytesx = ProtocolConstants.MULTI_PROTOCOL_300100.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(sumProtocolBytesx);
			            //发送第一个操作数
			            int firstNumx = 17;
			            String firstNumStrx = String.valueOf(firstNumx);
			            //如果操作符不够长度,则左侧补零
			            firstNumStrx = protocolUtils.fillString(firstNumStrx, ProtocolConstants.OPERATE_NUM_LENGTH);
			            byte[] firstNumBytesx = firstNumStrx.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(firstNumBytesx);
			            //发送第二个操作数
			            int secondNumx = 8;
			            String secondNumStrx = String.valueOf(secondNumx);
			            
			            secondNumStrx = protocolUtils.fillString(secondNumStrx, ProtocolConstants.OPERATE_NUM_LENGTH);
			            byte[] secondNumBytesx = secondNumStrx.getBytes(ProtocolConstants.CHARSET_UTF8);
			            clientBufferOutputStream.write(secondNumBytesx);
			            //发送协议结束符
			            clientBufferOutputStream.write(endBytes);
			            //将缓冲区发送到Server,我们这里是强制清空缓冲区,实际不要这样做,以免影响数据传输效率
			            clientBufferOutputStream.flush();
			            System.out.println("发送乘法计算协议结束...........");
			            try {
							Thread.sleep(3000);
							System.out.println("等待服务器计算结果...........");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
			            boolean flag = true;
			            //返回的协议长度,协议编码6+结果长度2
			            int ackLength = ProtocolConstants.PROTOCOL_CODE_LENGTH + ProtocolConstants.PROTOCOL_ACK_LENGTH;
			            while(flag){
//			               System.out.println("等待解析服务器计算结果...........");
			               //结果值长度
			               int valueLenth = 0;
			               //接受缓冲区的可利用长度大于等于8
				           if(clientBufferInputStream.available()>=ackLength){
				        	   byte[] codeBuf  = new byte[6];
				        	   int codeLength = clientBufferInputStream.read(codeBuf, 0, 6);
				        	   System.out.println("从接受缓冲区读取协议编码的实际长度为:"+codeLength);
				        	   if(codeLength == 6){
				        		   String ackProtcolCode = new String(codeBuf,ProtocolConstants.CHARSET_UTF8);
				        		   if(ackProtcolCode.equals(ProtocolConstants.ACK_PROTOCOL_300200)){
				        			   System.out.println("协议编码:"+ackProtcolCode);
				        			   byte[] resultBuf  = new byte[2];
				        			   int resultLength = clientBufferInputStream.read(resultBuf, 0, 2);
				        			   if(resultLength == 2){
				        				   String resultLengthStr = new String(resultBuf,ProtocolConstants.CHARSET_UTF8);
				        				   valueLenth = Integer.valueOf(resultLengthStr);
				        				   System.out.println("结果值长度为:"+valueLenth);
				        			   }
				        			   if(resultLength < 0){
						        		   //与服务器失去连接
						        		   flag = false;
						        		   System.out.println("与服务器失去连接.....");
						        	   }
				        		   }
				        	   }
				        	   if(codeLength < 0){
				        		   //与服务器失去连接
				        		   flag = false;
				        		   System.out.println("与服务器失去连接.....");
				        	   }
				        	   
				           }
				        if(valueLenth > 0){
				        	//结果值+协议结束符(2)
				        	int valueEndLenth = valueLenth + ProtocolConstants.PROTOCOL_END_LENGTH;
				        	if(clientBufferInputStream.available()>=valueEndLenth){
				        		byte[] valueEndBuf  = new byte[valueEndLenth];
			        			int readLength = clientBufferInputStream.read(valueEndBuf, 0, valueEndLenth);
			        			System.out.println("从接受缓冲区读取计算结果值和结束符实际长度为:"+readLength);
			        			if(readLength == valueEndLenth){
			        				String valueEndStr = new String(valueEndBuf,ProtocolConstants.CHARSET_UTF8);
			        				String endStr = valueEndStr.substring(valueEndStr.length()-2, valueEndStr.length());
			        				if(endStr.equals(ProtocolConstants.PROTOCOL_END)){
			        					System.out.println("计算结果协议结束:"+readLength);
			        					String valueStr = valueEndStr.substring(0,valueEndStr.length()-2);
			        					System.out.println("计算结果为:"+valueStr);
			        				}
			        			}
			        			 if(readLength < 0){
					        		   //与服务器失去连接
					        		   flag = false;
					        		   System.out.println("与服务器失去连接.....");
					        	   }
				        	}
				        	
				        }
			           }
			            
					} catch (IOException e) {
						System.out.println("服务器IO异常:"+e.getMessage());
						e.printStackTrace();
					}
					finally{
						try {
							clientBufferInputStream.close();
							clientInputStream.close();
						} catch (IOException e) {
							System.out.println("关闭资源异常:"+e.getMessage());
							e.printStackTrace();
						} 
						
					}
				}
			} catch (UnknownHostException e) {
				System.out.println("连接服务器异常:"+e.getMessage());
				e.printStackTrace();
			} catch (IOException e) {
				System.out.println("连接服务器IO异常:"+e.getMessage());
				e.printStackTrace();
			}
			finally{
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			
	}
}



服务端控制台输出

主机名:donaldHP
主机地址:192.168.132.126
服务器启动成功.........
服务器接受客户端连接.........
开始处理Socket........
//第一次解析
从接受缓冲区读取的实际协议长度为:28
开始解析计算协议......
计算协议解析完毕......
开始发送计算结果协议......
//15+6
服务器计算结果为:21
发送计算结果协议结束......
//第二次解析
从接受缓冲区读取的实际协议长度为:28
开始解析计算协议......
计算协议解析完毕......
开始发送计算结果协议......
//17*8
服务器计算结果为:136
发送计算结果协议结束......


客户端控制台输出
连接服务器成功...........
发送加法计算协议开始...........
发送加法计算协议结束...........
发送乘法计算协议开始...........
发送乘法计算协议结束...........
等待服务器计算结果...........
//第一次解析结果
从接受缓冲区读取协议编码的实际长度为:6
协议编码:300200
结果值长度为:2
// 4为结果值长度2+协议结束符2
从接受缓冲区读取计算结果值和结束符实际长度为:4
计算结果协议结束:4
//15+6
计算结果为:21
//第二次解析结果
从接受缓冲区读取协议编码的实际长度为:6
协议编码:300200
结果值长度为:3
// 5为结果值长度3+协议结束符2
从接受缓冲区读取计算结果值和结束符实际长度为:5
计算结果协议结束:5
//17*8
计算结果为:136

总结:
java socket编程是阻塞模式的,从socket获取InputStream,OutputStream,而InputStream,OutputStreamm要经过
(BufferedInputStream,BufferedOutputStream),(DataInputStream,DataOutputStream)等的包装才可以写读socket的缓冲区;输入流skip函数,可以丢掉一些不必要的包,mark,reset函数可以标记读取位置,从标记位置从新读取;flush函数发送缓冲区里的所有数据,我们这里是强制清空缓冲区,实际不要这样做,以免影响数据传输效率。



辅助测试类:
package socket;

import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
 * 辅助测试
 * @author donald
 * 2017年2月12日
 * 下午5:12:28
 */
public class testByte {
	public static void main(String[] args) {
		
	    try {
	    	String end = "\r\n";
			byte[] endBytes = end.getBytes("UTF-8");
			System.out.println("====endBytes Length:"+endBytes.length);
			String endStr = new String(endBytes,"UTF-8");
			System.out.println("是否结束:"+endStr.equals("\r\n"));
			System.out.println("====结束符:"+endStr);
			
			String sumProtocol = "300000";
			byte[] sumProtocolBytes = sumProtocol.getBytes("UTF-8");
			System.out.println("====sumProtocol Length:"+sumProtocolBytes.length);
			String sumProtocolStr = new String(sumProtocolBytes,"UTF-8");
			System.out.println("====加法协议:"+sumProtocolStr);
			
			String multiProtocol = "3000100";
			byte[] multiProtocolBytes = multiProtocol.getBytes("UTF-8");
			System.out.println("====multiProtocol Length:"+multiProtocolBytes.length);
			String multiProtocolStr = new String(multiProtocolBytes,"UTF-8");
			System.out.println("====乘法协议:"+multiProtocolStr);
			
			
			
			 int firstNum = 15;
             String firstNumStr = String.valueOf(firstNum);
             //如果第一个操作符不够长度,则补零
             if(firstNumStr.length()<10){
             	int NumLength = firstNumStr.length();
             	int zeroNum = 10 - NumLength;
             	String zeroNumStr="";
             	for(int i=0; i< zeroNum; i++){
             		zeroNumStr += "0";
             	}
             	firstNumStr = zeroNumStr + firstNumStr;
             }
             System.out.println("补零后的字符串:"+firstNumStr);
			
             
             try {
				InetAddress host = InetAddress.getLocalHost();
				System.out.println("主机名:"+host.getHostName());
				System.out.println("主机地质:"+host.getHostAddress());
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	 
}


//ServerSocket
//服务端
package java.net;

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.ServerSocketChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

/**
 * This class implements server sockets. A server socket waits for
 * requests to come in over the network. It performs some operation
 * based on that request, and then possibly returns a result to the requester.
 * <p>
 * The actual work of the server socket is performed by an instance
 * of the <code>SocketImpl</code> class. An application can
 * change the socket factory that creates the socket
 * implementation to configure itself to create sockets
 * appropriate to the local firewall.
 *
 * @author  unascribed
 * @see     java.net.SocketImpl
 * @see     java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory)
 * @see     java.nio.channels.ServerSocketChannel
 * @since   JDK1.0
 */
public
class ServerSocket implements java.io.Closeable {


//Socket
//客户端
/**
 * This class implements client sockets (also called just
 * "sockets"). A socket is an endpoint for communication
 * between two machines.
 * <p>
 * The actual work of the socket is performed by an instance of the
 * <code>SocketImpl</code> class. An application, by changing
 * the socket factory that creates the socket implementation,
 * can configure itself to create sockets appropriate to the local
 * firewall.
 *
 * @author  unascribed
 * @see     java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
 * @see     java.net.SocketImpl
 * @see     java.nio.channels.SocketChannel
 * @since   JDK1.0
 */
public
class Socket implements java.io.Closeable {


//InputStream
//从Socket获取的InputStream,要经过BufferedInputStream,DataInputStream
//等的包装才可以读数据,注意skip,mark,reset函数
package java.io;

/**
 * This abstract class is the superclass of all classes representing
 * an input stream of bytes.
 *
 * <p> Applications that need to define a subclass of <code>InputStream</code>
 * must always provide a method that returns the next byte of input.
 *
 * @author  Arthur van Hoff
 * @see     java.io.BufferedInputStream
 * @see     java.io.ByteArrayInputStream
 * @see     java.io.DataInputStream
 * @see     java.io.FilterInputStream
 * @see     java.io.InputStream#read()
 * @see     java.io.OutputStream
 * @see     java.io.PushbackInputStream
 * @since   JDK1.0
 */
public abstract class InputStream implements Closeable {

    // MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
    // use when skipping.
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the stream
     * has been reached, the value <code>-1</code> is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * <p> A subclass must provide an implementation of this method.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public abstract int read() throws IOException;

    /**
     * Reads some number of bytes from the input stream and stores them into
     * the buffer array <code>b</code>. The number of bytes actually read is
     * returned as an integer.  This method blocks until input data is
     * available, end of file is detected, or an exception is thrown.
     *
     * <p> If the length of <code>b</code> is zero, then no bytes are read and
     * <code>0</code> is returned; otherwise, there is an attempt to read at
     * least one byte. If no byte is available because the stream is at the
     * end of the file, the value <code>-1</code> is returned; otherwise, at
     * least one byte is read and stored into <code>b</code>.
     *
     * <p> The first byte read is stored into element <code>b[0]</code>, the
     * next one into <code>b[1]</code>, and so on. The number of bytes read is,
     * at most, equal to the length of <code>b</code>. Let <i>k</i> be the
     * number of bytes actually read; these bytes will be stored in elements
     * <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
     * leaving elements <code>b[</code><i>k</i><code>]</code> through
     * <code>b[b.length-1]</code> unaffected.
     *
     * <p> The <code>read(b)</code> method for class <code>InputStream</code>
     * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
     *
     * @param      b   the buffer into which the data is read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  If the first byte cannot be read for any reason
     * other than the end of the file, if the input stream has been closed, or
     * if some other I/O error occurs.
     * @exception  NullPointerException  if <code>b</code> is <code>null</code>.
     * @see        java.io.InputStream#read(byte[], int, int)
     */
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    /**
     * Reads up to <code>len</code> bytes of data from the input stream into
     * an array of bytes.  An attempt is made to read as many as
     * <code>len</code> bytes, but a smaller number may be read.
     * The number of bytes actually read is returned as an integer.
     *
     * <p> This method blocks until input data is available, end of file is
     * detected, or an exception is thrown.
     *
     * <p> If <code>len</code> is zero, then no bytes are read and
     * <code>0</code> is returned; otherwise, there is an attempt to read at
     * least one byte. If no byte is available because the stream is at end of
     * file, the value <code>-1</code> is returned; otherwise, at least one
     * byte is read and stored into <code>b</code>.
     *
     * <p> The first byte read is stored into element <code>b[off]</code>, the
     * next one into <code>b[off+1]</code>, and so on. The number of bytes read
     * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
     * bytes actually read; these bytes will be stored in elements
     * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
     * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
     * <code>b[off+len-1]</code> unaffected.
     *
     * <p> In every case, elements <code>b[0]</code> through
     * <code>b[off]</code> and elements <code>b[off+len]</code> through
     * <code>b[b.length-1]</code> are unaffected.
     *
     * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
     * for class <code>InputStream</code> simply calls the method
     * <code>read()</code> repeatedly. If the first such call results in an
     * <code>IOException</code>, that exception is returned from the call to
     * the <code>read(b,</code> <code>off,</code> <code>len)</code> method.  If
     * any subsequent call to <code>read()</code> results in a
     * <code>IOException</code>, the exception is caught and treated as if it
     * were end of file; the bytes read up to that point are stored into
     * <code>b</code> and the number of bytes read before the exception
     * occurred is returned. The default implementation of this method blocks
     * until the requested amount of input data <code>len</code> has been read,
     * end of file is detected, or an exception is thrown. Subclasses are encouraged
     * to provide a more efficient implementation of this method.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset in array <code>b</code>
     *                   at which the data is written.
     * @param      len   the maximum number of bytes to read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException If the first byte cannot be read for any reason
     * other than end of file, or if the input stream has been closed, or if
     * some other I/O error occurs.
     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
     * <code>len</code> is negative, or <code>len</code> is greater than
     * <code>b.length - off</code>
     * @see        java.io.InputStream#read()
     */
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

    /**
     * Skips over and discards <code>n</code> bytes of data from this input
     * stream. The <code>skip</code> method may, for a variety of reasons, end
     * up skipping over some smaller number of bytes, possibly <code>0</code>.
     * This may result from any of a number of conditions; reaching end of file
     * before <code>n</code> bytes have been skipped is only one possibility.
     * The actual number of bytes skipped is returned.  If <code>n</code> is
     * negative, no bytes are skipped.
     *
     * <p> The <code>skip</code> method of this class creates a
     * byte array and then repeatedly reads into it until <code>n</code> bytes
     * have been read or the end of the stream has been reached. Subclasses are
     * encouraged to provide a more efficient implementation of this method.
     * For instance, the implementation may depend on the ability to seek.
     *
     * @param      n   the number of bytes to be skipped.
     * @return     the actual number of bytes skipped.
     * @exception  IOException  if the stream does not support seek,
     *                          or if some other I/O error occurs.
     */
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }

    /**
     * Returns an estimate of the number of bytes that can be read (or
     * skipped over) from this input stream without blocking by the next
     * invocation of a method for this input stream. The next invocation
     * might be the same thread or another thread.  A single read or skip of this
     * many bytes will not block, but may read or skip fewer bytes.
     *
     * <p> Note that while some implementations of {@code InputStream} will return
     * the total number of bytes in the stream, many will not.  It is
     * never correct to use the return value of this method to allocate
     * a buffer intended to hold all data in this stream.
     *
     * <p> A subclass' implementation of this method may choose to throw an
     * {@link IOException} if this input stream has been closed by
     * invoking the {@link #close()} method.
     *
     * <p> The {@code available} method for class {@code InputStream} always
     * returns {@code 0}.
     *
     * <p> This method should be overridden by subclasses.
     *
     * @return     an estimate of the number of bytes that can be read (or skipped
     *             over) from this input stream without blocking or {@code 0} when
     *             it reaches the end of the input stream.
     * @exception  IOException if an I/O error occurs.
     */
    public int available() throws IOException {
        return 0;
    }

    /**
     * Closes this input stream and releases any system resources associated
     * with the stream.
     *
     * <p> The <code>close</code> method of <code>InputStream</code> does
     * nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {}

    /**
     * Marks the current position in this input stream. A subsequent call to
     * the <code>reset</code> method repositions this stream at the last marked
     * position so that subsequent reads re-read the same bytes.
     *
     * <p> The <code>readlimit</code> arguments tells this input stream to
     * allow that many bytes to be read before the mark position gets
     * invalidated.
     *
     * <p> The general contract of <code>mark</code> is that, if the method
     * <code>markSupported</code> returns <code>true</code>, the stream somehow
     * remembers all the bytes read after the call to <code>mark</code> and
     * stands ready to supply those same bytes again if and whenever the method
     * <code>reset</code> is called.  However, the stream is not required to
     * remember any data at all if more than <code>readlimit</code> bytes are
     * read from the stream before <code>reset</code> is called.
     *
     * <p> Marking a closed stream should not have any effect on the stream.
     *
     * <p> The <code>mark</code> method of <code>InputStream</code> does
     * nothing.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.InputStream#reset()
     */
    public synchronized void mark(int readlimit) {}

    /**
     * Repositions this stream to the position at the time the
     * <code>mark</code> method was last called on this input stream.
     *
     * <p> The general contract of <code>reset</code> is:
     *
     * <p>[list]
     *
     * <li> If the method <code>markSupported</code> returns
     * <code>true</code>, then:
     *
     *     [list]<li> If the method <code>mark</code> has not been called since
     *     the stream was created, or the number of bytes read from the stream
     *     since <code>mark</code> was last called is larger than the argument
     *     to <code>mark</code> at that last call, then an
     *     <code>IOException</code> might be thrown.
     *
     *     <li> If such an <code>IOException</code> is not thrown, then the
     *     stream is reset to a state such that all the bytes read since the
     *     most recent call to <code>mark</code> (or since the start of the
     *     file, if <code>mark</code> has not been called) will be resupplied
     *     to subsequent callers of the <code>read</code> method, followed by
     *     any bytes that otherwise would have been the next input data as of
     *     the time of the call to <code>reset</code>. [/list]
     *
     * <li> If the method <code>markSupported</code> returns
     * <code>false</code>, then:
     *
     *     [list]<li> The call to <code>reset</code> may throw an
     *     <code>IOException</code>.
     *
     *     <li> If an <code>IOException</code> is not thrown, then the stream
     *     is reset to a fixed state that depends on the particular type of the
     *     input stream and how it was created. The bytes that will be supplied
     *     to subsequent callers of the <code>read</code> method depend on the
     *     particular type of the input stream. [/list][/list]
     *
     * <p>The method <code>reset</code> for class <code>InputStream</code>
     * does nothing except throw an <code>IOException</code>.
     *
     * @exception  IOException  if this stream has not been marked or if the
     *               mark has been invalidated.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.IOException
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    /**
     * Tests if this input stream supports the <code>mark</code> and
     * <code>reset</code> methods. Whether or not <code>mark</code> and
     * <code>reset</code> are supported is an invariant property of a
     * particular input stream instance. The <code>markSupported</code> method
     * of <code>InputStream</code> returns <code>false</code>.
     *
     * @return  <code>true</code> if this stream instance supports the mark
     *          and reset methods; <code>false</code> otherwise.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return false;
    }
}



//OutputStream,从socket获取的输出流,需要BufferedOutputStream,DataOutputStream
//的包装才可以发送数据,注意,flush函数,将flush函数调用是,立即发送缓冲区里的所有数//据
package java.io;

/**
 * This abstract class is the superclass of all classes representing
 * an output stream of bytes. An output stream accepts output bytes
 * and sends them to some sink.
 * <p>
 * Applications that need to define a subclass of
 * <code>OutputStream</code> must always provide at least a method
 * that writes one byte of output.
 *
 * @author  Arthur van Hoff
 * @see     java.io.BufferedOutputStream
 * @see     java.io.ByteArrayOutputStream
 * @see     java.io.DataOutputStream
 * @see     java.io.FilterOutputStream
 * @see     java.io.InputStream
 * @see     java.io.OutputStream#write(int)
 * @since   JDK1.0
 */
public abstract class OutputStream implements Closeable, Flushable {
    /**
     * Writes the specified byte to this output stream. The general
     * contract for <code>write</code> is that one byte is written
     * to the output stream. The byte to be written is the eight
     * low-order bits of the argument <code>b</code>. The 24
     * high-order bits of <code>b</code> are ignored.
     * <p>
     * Subclasses of <code>OutputStream</code> must provide an
     * implementation for this method.
     *
     * @param      b   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> may be thrown if the
     *             output stream has been closed.
     */
    public abstract void write(int b) throws IOException;

    /**
     * Writes <code>b.length</code> bytes from the specified byte array
     * to this output stream. The general contract for <code>write(b)</code>
     * is that it should have exactly the same effect as the call
     * <code>write(b, 0, b.length)</code>.
     *
     * @param      b   the data.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.OutputStream#write(byte[], int, int)
     */
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    /**
     * Writes <code>len</code> bytes from the specified byte array
     * starting at offset <code>off</code> to this output stream.
     * The general contract for <code>write(b, off, len)</code> is that
     * some of the bytes in the array <code>b</code> are written to the
     * output stream in order; element <code>b[off]</code> is the first
     * byte written and <code>b[off+len-1]</code> is the last byte written
     * by this operation.
     * <p>
     * The <code>write</code> method of <code>OutputStream</code> calls
     * the write method of one argument on each of the bytes to be
     * written out. Subclasses are encouraged to override this method and
     * provide a more efficient implementation.
     * <p>
     * If <code>b</code> is <code>null</code>, a
     * <code>NullPointerException</code> is thrown.
     * <p>
     * If <code>off</code> is negative, or <code>len</code> is negative, or
     * <code>off+len</code> is greater than the length of the array
     * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs. In particular,
     *             an <code>IOException</code> is thrown if the output
     *             stream is closed.
     */
    public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be written out. The general contract of <code>flush</code> is
     * that calling it is an indication that, if any bytes previously
     * written have been buffered by the implementation of the output
     * stream, such bytes should immediately be written to their
     * intended destination.
     * <p>
     * If the intended destination of this stream is an abstraction provided by
     * the underlying operating system, for example a file, then flushing the
     * stream guarantees only that bytes previously written to the stream are
     * passed to the operating system for writing; it does not guarantee that
     * they are actually written to a physical device such as a disk drive.
     * <p>
     * The <code>flush</code> method of <code>OutputStream</code> does nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void flush() throws IOException {
    }

    /**
     * Closes this output stream and releases any system resources
     * associated with this stream. The general contract of <code>close</code>
     * is that it closes the output stream. A closed stream cannot perform
     * output operations and cannot be reopened.
     * <p>
     * The <code>close</code> method of <code>OutputStream</code> does nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {
    }
}

//BufferedInputStream
package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * A <code>BufferedInputStream</code> adds
 * functionality to another input stream-namely,
 * the ability to buffer the input and to
 * support the <code>mark</code> and <code>reset</code>
 * methods. When  the <code>BufferedInputStream</code>
 * is created, an internal buffer array is
 * created. As bytes  from the stream are read
 * or skipped, the internal buffer is refilled
 * as necessary  from the contained input stream,
 * many bytes at a time. The <code>mark</code>
 * operation  remembers a point in the input
 * stream and the <code>reset</code> operation
 * causes all the  bytes read since the most
 * recent <code>mark</code> operation to be
 * reread before new bytes are  taken from
 * the contained input stream.
 *
 * @author  Arthur van Hoff
 * @since   JDK1.0
 */
public
class BufferedInputStream extends FilterInputStream {

    private static int defaultBufferSize = 8192;






//BufferedOutputStream
package java.io;

/**
 * The class implements a buffered output stream. By setting up such
 * an output stream, an application can write bytes to the underlying
 * output stream without necessarily causing a call to the underlying
 * system for each byte written.
 *
 * @author  Arthur van Hoff
 * @since   JDK1.0
 */
public
class BufferedOutputStream extends FilterOutputStream {






//DataInputStream
package java.io;

/**
 * A data input stream lets an application read primitive Java data
 * types from an underlying input stream in a machine-independent
 * way. An application uses a data output stream to write data that
 * can later be read by a data input stream.
 * <p>
 * DataInputStream is not necessarily safe for multithreaded access.
 * Thread safety is optional and is the responsibility of users of
 * methods in this class.
 *
 * @author  Arthur van Hoff
 * @see     java.io.DataOutputStream
 * @since   JDK1.0
 */
public
class DataInputStream extends FilterInputStream implements DataInput {





//DataOutputStream
package java.io;

/**
 * A data output stream lets an application write primitive Java data
 * types to an output stream in a portable way. An application can
 * then use a data input stream to read the data back in.
 *
 * @author  unascribed
 * @see     java.io.DataInputStream
 * @since   JDK1.0
 */
public
class DataOutputStream extends FilterOutputStream implements DataOutput {





//FilterInputStream




//FilterOutputStream









  • 大小: 30.2 KB
  • 大小: 8.7 KB
  • 大小: 20.1 KB
  • 大小: 17.4 KB
  • 大小: 22.2 KB
  • 大小: 20.5 KB
  • 大小: 23.3 KB