Java Socket通信实例

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


[img]http://dl2.iteye.com/upload/attachment/0123/0192/63a7b80d-0b5d-3c9f-b339-00c731cd47cf.png[/img]

[size=medium]具体实现[/size]:

协议常量:

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();
}
}


}
}



[size=medium]服务端控制台输出[/size]:

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


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

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


辅助测试类:
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;



[img]http://dl2.iteye.com/upload/attachment/0123/0178/259e74a5-4e0c-37dc-a092-354aeb684cb8.png[/img]


//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 {



[img]http://dl2.iteye.com/upload/attachment/0123/0180/ab31500d-7b8a-304f-b8be-e3107944d615.png[/img]


//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 {



[img]http://dl2.iteye.com/upload/attachment/0123/0182/0b15005a-d426-3e39-8067-1dcc477837bf.png[/img]

//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 {



[img]http://dl2.iteye.com/upload/attachment/0123/0184/61343632-15ca-3676-a474-22e5996dfcdb.png[/img]

//FilterInputStream


[img]http://dl2.iteye.com/upload/attachment/0123/0186/33d34d58-a3ff-3034-8d9c-dc3fcf770502.png[/img]

//FilterOutputStream


[img]http://dl2.iteye.com/upload/attachment/0123/0188/3ab3afc0-51a7-3ead-b525-a1b0e2a6e3e9.png[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值