Socket类的getInputStream方法与getOutputStream方法的使用
客户端上的使用
- getInputStream方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到输入流其实就是从服务器端发回的数据。
- getOutputStream方法得到的是一个输出流,客户端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据。
服务器端上的使用
getInputStream方法得到的是一个输入流,服务端的Socket对象上的getInputStream方法得到的输入流其实就是从客户端发送给服务器端的数据流。
getOutputStream方法得到的是一个输出流,服务端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给客户端的数据。
read()阻塞
从Socket上读取对端发过来的数据一般有两种方法:
1)按照字节流读取
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
int r = -1;
List<Byte> l = new LinkedList<Byte>();
while ((r = in.read()) != -1) {
l.add(Byte.valueOf((byte) r));
}
2)按照字符流读取
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s;
while ((s = in.readLine()) != null) {
System.out.println("Reveived: " + s);
}
这两个方法read()和readLine()都会读取对端发送过来的数据,如果无数据可读,就会阻塞直到有数据可读。或者到达流的末尾,这个时候分别返回-1和null。
这个特性使得编程非常方便也很高效。
但是这样也有一个问题,就是如何让程序从这两个方法的阻塞调用中返回。
总结一下,有这么几个方法:
- 发送完后调用Socket的shutdownOutput()方法关闭输出流,这样对端的输入流上的read操作就会返回-1。
注意不能调用socket.getInputStream().close()。这样会导致socket被关闭。
当然如果不需要继续在socket上进行读操作,也可以直接关闭socket。
但是这个方法不能用于通信双方需要多次交互的情况。 - 发送数据时,约定数据的首部固定字节数为数据长度。这样读取到这个长度的数据后,就不继续调用read方法。
- 为了防止read操作造成程序永久挂起,还可以给socket设置超时。
如果read()方法在设置时间内没有读取到数据,就会抛出一个java.net.SocketTimeoutException异常。
例如下面的方法设定超时3秒。
socket.setSoTimeout(3000);
代码实例:
public class TCPClient {
public static void main(String[] args)throws IOException {
Socket socket = new Socket("127.0.0.1",5000);
OutputStream out = socket.getOutputStream();
//创建文件对象 如果文件路径写错 Client报找不到文件异常,Server报Connection Reset异常
File file = new File("E:\\Pictures\\010.jpg");
//字节输入流,读取本地文件到java程序中
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[1024]; //数组,增强传输效率
int len = 0;
while((len = fis.read(data)) != -1) //read方法返回数组的有效字符个数
out.write(data, 0, len);
socket.shutdownOutput(); //数据传输完毕,关闭socket输出流,避免服务器端read方法阻塞
InputStream in = socket.getInputStream(); //字节输入流,读取服务器返回的数据
len = in.read(data);
System.out.println(new String(data,0,len));
socket.close();
fis.close();
}
}
---------------------------------------------------------------------------------
public class TCPServer {
public static void main(String[] args)throws IOException {
ServerSocket server = new ServerSocket(5000);//打开服务器制定端口,等待客户端连接
//获得与服务器相连的套接字对象 套接字:绑定ip地址和端口号的网络对象
Socket socket = server.accept();
//查看该地址文件夹是否存在,如果不存在,创建一个
File file = new File("E:\\TestFolder\\upload");
if(!file.exists()){
boolean b = file.mkdirs();
System.out.println(b);
}
InputStream in = socket.getInputStream(); //套接字的字节输入流,读取客户端传过来的数据
String name = System.currentTimeMillis()+""+new Random().nextInt(9999); //随机文件名
FileOutputStream fos = new FileOutputStream(file+File.separator+name+".jpg"); //File.separator表示分隔符,windows是\,linux是/
byte[] data = new byte[1024];
int len = 0;
//如果客户端没有关闭socket输出流,这里的read方法会一直读取,对于socket流没有流末尾之说,不可能返回-1
while((len = in.read(data)) != -1)
fos.write(data, 0, len);
data = "上传成功!".getBytes(); //字符串转化为字节数组
OutputStream out = socket.getOutputStream(); //创建字节输出流
out.write(data); //写入上传成功 ,反馈给客户端
server.close();
fos.close();
}
}