1、Sender和Receiver的通信
Receiver.java
Sender.java
自然结束:发送和接收所有数据
提前终止Receiver:Sender会继续发送,Receiver已经结束运行,但低层Socket并没有立即释放,操作系统探测到还有该Socket的数据,会使低层Socket继续占用一段时间
突然终止Sender:Receiver的readLine()抛异常
Exception in thread "main" java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:189)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.example.Receiver.receive(Receiver.java:42)
at com.example.Receiver.main(Receiver.java:73)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
关闭或半关闭Sender的Socket:程序不会报错,readLine()会继续读到流的末尾,只是返回null
public class Receiver {
private int port = 8000;
private ServerSocket serverSocket;
private static int stopWay = 1;// 结束通信的方式
private final int natural_stop=1; //自然结束
private final int sudden_stop=2; //突然终止程序
private final int socket_stop=3; //关闭Socket,再结束程序
private final int input_stop=4; //关闭输入流,再结束程序
private final int serversocket_stop=5; //关闭ServerSocket,再结束程序
public Receiver() throws IOException {
serverSocket = new ServerSocket(port);
System.out.println("服务器已经启动");
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream is = socket.getInputStream();
return new BufferedReader(new InputStreamReader(is));
}
public void receive() throws Exception {
Socket socket = null;
socket = serverSocket.accept();
BufferedReader br = getReader(socket);
for(int i=0;i<20;i++){
String msg = br.readLine();
System.out.println("receive:"+msg);
Thread.sleep(1000);
if(i==2){
if(stopWay==sudden_stop){
System.out.println("突然终止程序");
System.exit(0);
}else if(stopWay==socket_stop){
System.out.println("关闭Socket并终止程序");
socket.close();
break;
}else if(stopWay==input_stop){
System.out.println("关闭输入流并终止程序");
socket.shutdownInput();
break;
}else if(stopWay==serversocket_stop){
System.out.println("关闭ServerSocket并终止程序");
serverSocket.close();
break;
}
}
}
if(stopWay==natural_stop){
socket.close();
serverSocket.close();
}
}
public static void main(String args[])throws Exception {
if(args.length>0)stopWay=Integer.parseInt(args[0]);
new Receiver().receive();
}
}
public class Sender {
private String host = "localhost";
private int port = 8000;
private Socket socket;
private static int stopWay = 3;// 结束通信的方式
private final int natural_stop = 1;// 自然结束
private final int sudden_stop = 2;// 突然终止程序
private final int socket_stop = 3;// 关闭socket,再结束程序
private final int output_stop = 4;// 关闭输出流,再结束程序
public Sender() throws IOException {
socket = new Socket(host, port);
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream is = socket.getOutputStream();
return new PrintWriter(is, true);
}
public void send() throws Exception {
PrintWriter pw = getWriter(socket);
for(int i=0;i<20;i++){
String msg = "hello_"+i;
pw.println(msg);
System.out.println("send:"+msg);
Thread.sleep(500);
if(i==2){
if(stopWay == sudden_stop){
System.out.println("突然终止程序");
System.exit(0);
}else if(stopWay == socket_stop){
System.out.println("关闭Socket并终止程序");
socket.close();
break;
}else if(stopWay == output_stop){
socket.shutdownOutput();
System.out.println("关闭输出流并终止程序");
break;
}
}
}
if(stopWay == natural_stop){
socket.close();
}
}
public static void main(String[] args) throws Exception {
if(args.length>0){
stopWay = Integer.parseInt(args[0]);
}
new Sender().send();
}
}
2、Socket选项
2.1 tcp_nodelay 默认false
默认发送数据采用Negale算法,就是数据先放缓冲区,等缓冲区满了再发送。适合大数据量并且接收方及时回应的场合,减少通信次数提高通信效率
if(!socket.getTcpNoDelay()){
socket.setTcpNoDelay(true);
}
如果Socket低层不支持,调用上面方法时会抛SocketException
2.2 so_resuseaddr
上面接收Socket数据时当本地已经close()时,端口不会立即释放
确保数据不被同样端口新进程接收到,但可能性不大
如果需要在Socket关闭后,还没有释放端口,又可立即重用该端口,可调用setResuseAddress(true);方法
Socket还未绑定到一个端口前调用
Socket socket = new Socket();
socket.setResuseAddress(true);
SocketAddress remoteAddr = new InetSocketAddress("remotehost", 8000);
socket.connect(remoteAddr);
2.3 so_timeout
超时
2.4 so_linger
控制Socket关闭时行为
socket.setSoLinger(true, 0); socket立即关闭,未发送完的数据被丢弃
socket.setSoLinger(true, 3600); 数据发送完或阻塞3600秒
SimpleClient.java
SimpleServer.java
//s.setSoLinger(true,0); //Socket关闭后,底层Socket立即关闭
//s.setSoLinger(true,3600); //Socket关闭后,底层Socket延迟3600秒再关闭
虽然socket调用了close(),但低层socket并没有真正关闭,直到发送完数据
s.setSoLinger(true, 0);
socket立即关闭,SimpleServer睡眠结束抛出异常
s.setSoLinger(true,3600);
socket知道发送完毕才关闭
开始关闭Socket
关闭Socket所用时间:5650ms
2.5 so_rcvbuf
控制输入数据的缓冲区大小
setReceiveBufferSize(int size);
getReceiveBufferSize();
2.6 so_sndbuf
控制输出数据的缓冲区大小
setSendBufferSize(int size);
getSendBufferSize();
2.7 so_keepalive
TCP监视连接是否有效,不同平台时限有所差别
setKeepAlive(boolean on);
getKeepAlive();
2.8 oobinline
发送一个字节的TCP紧急数据
除非使用高层次的协议,接收方很难区分普通数据和紧急数据
public class SimpleClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 8000);
// s.setSoLinger(true,0); //Socket关闭后,底层Socket立即关闭
s.setSoLinger(true, 3600); //Socket关闭后,底层Socket延迟3600秒再关闭
OutputStream out = s.getOutputStream();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
out.write(sb.toString().getBytes());
System.out.println("开始关闭Socket");
long begin = System.currentTimeMillis();
s.close();
long end = System.currentTimeMillis();
System.out.println("关闭Socket所用时间:" + (end - begin) + "ms");
}
}
public class SimpleServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8000);
Socket s = serverSocket.accept();
Thread.sleep(5000);
InputStream in = s.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len = -1;
do{
len = in.read(buff);
if(len!=-1){
buffer.write(buff, 0, len);
}
}while(len != -1);
System.out.println(new String(buffer.toByteArray()));
}
}