java socket
前言
本文主要包含以下内容:
- 代码示例
- wireshark抓包内容,帮助理解
- 一些自己的简单理解和胡乱想法
才疏学浅,如有错误欢迎指证。
正文
Part - 1
先从一个最简单的代码开始:
展开Server代码
public class ServerOne {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
}
}
展开client代码
public class ClientOne {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 9999);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("Hello, this is my first socket");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意,先起Server服务,再运行Client的代码,这时你会发现:server并没有打印本应收到的message,而是报了一个异常java.net.SocketException: Connection reset
我们用wireshark抓一下包,看看都发生了什么
展开图片
从上图中可以看到,三次握手之后,本应发送一个PSH
标志的报文段,但是却并没有,而是直接发了一个RST
的报文。
找了一个RST
标志位的解释:
RST标志位用来复位一条连接。当RST=1时,表示出现严重错误,必须释放连接,然后再重新建立。
我以自己的小脑子理解了一下,大概就是非正常关闭的情况下,会给对方发一个RST
的报文,告诉对方:我要挂了,你要想继续跟我玩儿的话,得重新建立一次连接(我妈拿着棍子让我回家吃饭了,你要想找我玩儿的话,下午再找个理由来叫我)。
既然这样的话,那我们改动一下client的代码,让它在结束的时候,正常关闭连接,看看这个时候会发生什么,代码如下:
展开client代码
public class ClientOne {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 9999);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("Hello, this is my first socket");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
从上面client的代码看出,好,我现在正式通知你,我要关闭连接了。
再重新运行一下代码,发现:咦,没有报错了,但是server仍然没有打印任何message呀。
我们再来看一下抓包内容:
展开图片
观察上面图片,发现:咦,这次client倒是正常的发送了FIN
报文告诉server,我要关闭了,但是server端没有正常关闭,没有发送FIN
给client,这好办,我们再改造一下server端的代码。
展开server代码
public class ServerOne {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = reader.readLine();
System.out.println(line);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们再看看这次的报文:
展开图片
三次握手,四次挥手倒是一切正常,但是丫的没数据呀。。。仔细一看,噢,忘了flush缓冲区了,继续改代码:
展开client代码
public class ClientOne {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 9999);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("Hello, this is my first socket");
bufferedWriter.flush();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我发誓这一定是最后一次看这该死的报文:
展开图片
后记
这里补充一下不用缓冲区的代码:
server
public class ServerTwo {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
System.out.println(dataInputStream.readUTF());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
client
public class ClientTwo {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF("Hello, this is my second socket");
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}