欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Socket网络通信之BIO

程序员文章站 2022-05-05 14:07:54
Socket网络通信之BIO 如果要让两台计算机实现通信,需要的条件:ip,port,协议。 目前我们用的最多的就是TCP/IP协议和UDP协议。TCP三次握手,所以比较慢,且安全;UDP速度快,但是可能丢包,不能保证安全。 网络通讯基本都是通过Socket来通讯的。(客户端的Socket类;服务端 ......

socket网络通信之bio

如果要让两台计算机实现通信,需要的条件:ip,port,协议。

目前我们用的最多的就是tcp/ip协议和udp协议。tcp三次握手,所以比较慢,且安全;udp速度快,但是可能丢包,不能保证安全。

网络通讯基本都是通过socket来通讯的。(客户端的socket类;服务端的serversocket类)

客户端和服务端这样建立连接:第一步客户端发起建立连接的请求,第二部服务端收到请求建立连接的请求,并同意和该客户端建立连接,并响应给客户端,第三步客户端收到服务端响应的建立连接的消息,并确认和服务端建立连接,通过这样三部客户端和服务端就真正的建立了连接,服务端和客户端就可以开始通讯,交互了.通过这样三次的握手交互服务端和客户端就成功的建立了连接,如下图所示

Socket网络通信之BIO

而java中实现通信的io主要是:同步阻塞io(bio)、同步非阻塞io(nio)、异步非阻塞io(aio)

同步阻塞io(bio)— 原生态

Socket网络通信之BIO
public class bioserver {

    private  static charset charset = charset.forname("utf-8");
    public static void main(string[] args) {
        int port = 1100;

        try ( serversocket  socketserver = new serversocket(port)){
            while (true){
                //接收连接,如果没有连接建立,这里会阻塞
                socket socket = socketserver.accept();
                bufferedreader reader = new bufferedreader(
                        new inputstreamreader(socket.getinputstream(), charset)
                );
                string msg = null;
                //连接进来后,会在这里等待客户端发送消息。
                while ((msg =reader.readline())!=null){
                    system.out.println(msg);
                }
            }
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }
}
bioserver

如上为server代码,下面为client代码

Socket网络通信之BIO
public class bioclient implements  runnable{
    private  string address;
    private  int  port;

    public static void main(string[] args) {
        bioclient client = new bioclient("localhost", 1100);
        client.run();
    }

    @override
    public void run() {
        try(socket socket = new socket(address, port);
            outputstream outputstream = socket.getoutputstream()) {

            scanner scanner = new scanner(system.in);
            system.out.println("请输入:");
            string msg = scanner.nextline();
            outputstream.write(msg.getbytes("utf-8"));
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }

    public int getport() {
        return port;
    }

    public void setport(int port) {
        this.port = port;
    }

    public string getaddress() {
        return address;
    }

    public void setaddress(string address) {
        this.address = address;
    }
    public bioclient(string address, int port) {
        super();
        this.address=address;
        this.port = port;
    }
}
bioclient

首先用debug启动bioserver。
可以看到在debug模式下,下一步不能再执行,程序阻塞在连接处,等待连接。

Socket网络通信之BIO

启动客户端bioclient以后,可以看到服务端已经连接,debug可以下一步,如下图。

Socket网络通信之BIO

执行下一步下一步之后,等待客户端发送信息处,又阻塞,程序debug再次执行不下去,如下图:Socket网络通信之BIO

现在如果在客户端输入信息,再次debug下一步,服务端就会收到相应的信息。
这就是一个简单的bio,从这个过程中,我们可以看到:服务器端代码必须要在连接处和接收消息处阻塞,只用得到了相应的信息,才会往下继续执行。如果多个客户端一起请求,大家都会阻塞在此处,这样的程序效率及其低下,甚至长期等待不能用。有什么办法能够解决这样的问题呢?。

同步阻塞io(bio)— 多线程

Socket网络通信之BIO
public class bioserverv2 {
    private static charset charset = charset.forname("utf-8");
    public static void main(string[] args) {
        int port = 1101;
        try (serversocket ss = new serversocket(port);) {
            while (true) {
                socket s = ss.accept();
                // 开一个线程去处理这个连接
                new thread(new socketprocess(s)).start();
            }
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }

    static class socketprocess implements runnable {
        socket s;
        public socketprocess(socket s) {
            super();
            this.s = s;
        }

        @override
        public void run() {
            try (bufferedreader reader = new bufferedreader(
                    new inputstreamreader(s.getinputstream(), charset));) {
                // 接收数据、打印
                string mess = null;
                while ((mess = reader.readline()) != null) {
                    system.out.println(mess);
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}
bioserverv2

如上代码所示,我们稍微改动了一下服务器端的代码,客户端只需要改动和服务端一样的端口号就行,这里使用多线程的方式。

这里是利用多线程的方式来实现,在连接处多开线程去处理,这样多个客户端连接时,就不会大家一致排队阻塞在此处,但是阻塞还是存在的。还有什么更好的办法来解决这样的问题吗?

同步阻塞io(bio)— 线程池

Socket网络通信之BIO
public class bioserverv3 {
    private static charset charset = charset.forname("utf-8");
    public static void main(string[] args) {
        int port = 1102;
        int threads = 100;
        executorservice tpool = executors.newfixedthreadpool(threads);

        try (serversocket ss = new serversocket(port);) {
            while (true) {
                socket s = ss.accept();
                // 丢到线程池中去跑
                tpool.execute(new socketprocess(s));
            }
        } catch (ioexception e) {
            e.printstacktrace();
        }
        tpool.shutdown();
    }

    static class socketprocess implements runnable {
        socket s;

        public socketprocess(socket s) {
            super();
            this.s = s;
        }

        @override
        public void run() {
            try (bufferedreader reader = new bufferedreader(
                    new inputstreamreader(s.getinputstream(), charset));) {
                // 接收数据、打印
                string mess = null;
                while ((mess = reader.readline()) != null) {
                    system.out.println(mess);
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}
bioserverv3

如上所示,我们再次改动,改成以线程池的办法来连接。但是线程池也有问题:

请求大于线程,没有足够的线程来处理,响应时间很长,甚至服务器拒绝。

阻塞等待接收客户端的数据时,这段时间占着线程,而池中线程数是有限的,并发量大时,将导致没有线程处理请求,请求的响应时间长,甚至拒绝服务。

从上面三部分的代码可以看出来,代码性能等问题,全是阻塞惹的祸,如果没有阻塞就好了,如果能不阻塞,在没有数据时,就去干点别的事情,有数据了才处理数据。

详情请见下一个博客:socket网络通信之nio