基本概念
TCP协议:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。
Socket:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
需求分析
* 1,客户端
* 创建Socket连接服务器(指定IP地址,端口号)通过IP地址找到对应的服务器
* 调用Socket的getInputStream()和getOutputStream()方法获取和服务器端相连的IO流
* 输入流可以读取服务器端输出流写出的数据
* 输出流可以写出数据到服务端的输入流
*
* 2,服务端
* 创建ServerSocket(需要指定端口号)
* 调用ServerSocket的accept()方法接受一个客户端请求,得到一个Socket
* 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
* 输入流可以读取客户端输出流写出的数据
* 输出流可以写出数据到客户端的输入流
问题分析
在使用socket时,需要约定好双方读写完成的条件,然后关闭输入输出流:
socket.shutdownInput();
socket.shutdownOutput();
即当一方写入完成后,调用shutdownOutput关闭输出流,这时候对方的read方法就会返回-1,这时候对方就知道你写完了,对方可以关闭输入流,然后等待对方写入完成调用shutdownOutput后己方再调用shutdownInput,双方就正常关闭了输入输出流,这时候socket就不会出现异常了。
源代码
Server端
public class OioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
System.out.println("socket = " + socket);
new Thread(() -> {
try {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write("hello! I get your message that is follow".getBytes(Charset.forName("UTF-8")));
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
System.out.print(new String(buf, 0, len, Charset.forName("UTF-8")));
out.write(buf, 0, len);
}
out.write("\n end \n".getBytes(Charset.forName("UTF-8")));
out.flush();
socket.shutdownInput();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
Client端
public class OioClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
InputStream in = socket.getInputStream();
new Thread(() -> {
BufferedInputStream bufferIn = new BufferedInputStream(in);
byte[] buf = new byte[1024];
try {
int len;
while ((len = bufferIn.read(buf)) != -1) {
System.out.print(new String(buf, 0, len, Charset.forName("UTF-8")));
}
}catch (Exception e) {
e.printStackTrace();
}
try {
socket.shutdownInput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
OutputStream out = socket.getOutputStream();
int cout = 10;
while (cout-- > 0) {
out.write(("this time is " + System.currentTimeMillis() + "\n").getBytes("UTF-8"));
}
socket.shutdownOutput();
}
}
常见异常
java.net.SocketException:socket is closed
错误提示的出现场景:
自己主动关闭了socket,但是之后还从里面读写数据
Software caused connection abort: socket write error
错误提示的出现场景:
对方已经关闭socket,依旧向对方写数据
connection reset (by peer)
错误提示出现的场景:
一端socket被关闭,另一端仍然发送数据,发送的第一个数据包 connection reset by peer
一端socket退出,退出时为关闭连接,另一端读数据 connection reset
参考文章
https://stackoverflow.com/questions/14010194/detecting-socket-disconnection