Socket基本方法

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

  

posted @ 2016-06-23 15:40  轻云沉峰  阅读(484)  评论(0)    收藏  举报