Socket实现即时通讯服务(一)

Socket实现即时通讯服务(一)

前言

写这篇文章的目的是为了深入了解Socket编程,然后重构之前写的Android聊天软件的服务端。之前写的服务端只是单纯为了实现聊天这个功能而已,没有考虑用户量,没有考虑并发,没考虑其他东西,虽然勉强实现了,但还是想重新写一个聊天服务端,后面可以供大家学习交流。在了解Socket编程之前,首先还是大概介绍下了Socket这个东西吧。

正文

学过计算机网络的都知道五层网络模型和七层网络模型,五层网络模型从上到下依次为应用层、传输层、网络层、链路层和物理层,而七层网络模型则是对应用层进行了细分,应用层又可以分为应用层、表示层和会话层。一般程序之间的通信都是通过TCP或者UDP来进行实现的,当然还涉及到底下的网络层等,就不再深入介绍了。而Socket是在哪一层呢?Socket是在应用层和传输层之间,是一个抽象层,它封装了TCP/IP协议,方便用户调用。在程序实现的时候只要知道如何调用Socket就可以完成程序之间的通讯了,当然也要了解下计算机网络的。
Socket通讯示意图如下:在这里插入图片描述

上图就是Socket通讯整个过程的流程图了,虽然Socket封装了TCP/IP协议,但是其实还是存在TCP三次握手和四次挥手的,两端通过Socke通讯前都需要三次握手后才能进行数据传输的。上图中两端分别是发送方和接收方,为什么这样写呢?在很多博客中都能看到这类图,上面写的都是客户端和服务端,这也没错,但是这样稍微有点片面,因为假设是两个服务端,也能通过Socket进行通讯的,所以这里用发送方和接收方来描述了。但是在后面开发中,发送方采用了Android客户端来代替了,接收方则是消息处理服务端。

再来细看上图,其实发送方和接收方都是通过一个Socket对象实现的(接收方首先是用ServerSocket来进行端口监听和绑定,后续IO操作都是通过Socket对象来完成)。由于Java中都已经封装了Socket对象,所以只需直接调用即可(后需开发都是通过Java实现)。下面来看下接收方(服务端)的简单实现:

ServerSocket serversocket = new ServerSocket(port);
while(true){
	Socket socket = serversocket.accept();//这里serversocket调用了accept()方法返回一个socket对象
}

这里ServerSocket调用完accept()方法后,返回Socket对象后,发送方和接收方直接的连接已经建立了。

  • ServerSocket类源码:
class ServerSocket implements java.io.Closeable {
	//此处省略部分代码
    public ServerSocket(int port) throws IOException { //port端口值传入,后调用下个构造方法
        this(port, 50, null);
    }
    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl();
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException(
                       "Port value out of range: " + port);
        if (backlog < 1)
          backlog = 50;
        try {
            bind(new InetSocketAddress(bindAddr, port), backlog);  //调用bind()方法进行端口绑定
        } catch(SecurityException e) {
            close();
            throw e;
        } catch(IOException e) {
            close();
            throw e;
        }
    }
	//省略部分代码
    public void bind(SocketAddress endpoint) throws IOException {
        bind(endpoint, 50);  //接着调用重载的bind()方法
    }
    public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!oldImpl && isBound())
            throw new SocketException("Already bound");
        if (endpoint == null)
            endpoint = new InetSocketAddress(0);
        if (!(endpoint instanceof InetSocketAddress))
            throw new IllegalArgumentException("Unsupported address type");
        InetSocketAddress epoint = (InetSocketAddress) endpoint;
        if (epoint.isUnresolved())
            throw new SocketException("Unresolved address");
        if (backlog < 1)
          backlog = 50;
        try {
            SecurityManager security = System.getSecurityManager();
            if (security != null)
                security.checkListen(epoint.getPort());
            getImpl().bind(epoint.getAddress(), epoint.getPort());
            getImpl().listen(backlog);  //这里可以看到调用了listen()方法进行了端口监听
            bound = true;
        } catch(SecurityException e) {
            bound = false;
            throw e;
        } catch(IOException e) {
            bound = false;
            throw e;
        }
    }
    //省略部分代码
	public Socket accept() throws IOException {  //调用accept()方法
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isBound())
            throw new SocketException("Socket is not bound yet");
        Socket s = new Socket((SocketImpl) null);  //这里new出Socket对象,并返回对象
        implAccept(s);
        return s;
    }
//省略部分代码,完整的代码可以去JDK包中查看
}

到这里连接已经建立了,后续的IO操作都将通过Socket对象来完成。

  • Socket类的源码:
class Socket implements java.io.Closeable {
	//省略一些代码
	public Socket(Proxy proxy) {
        // Create a copy of Proxy as a security measure
        if (proxy == null) {
            throw new IllegalArgumentException("Invalid Proxy");
        }
        Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY
                                          : sun.net.ApplicationProxy.create(proxy);
        Proxy.Type type = p.type();
        if (type == Proxy.Type.SOCKS || type == Proxy.Type.HTTP) {
            SecurityManager security = System.getSecurityManager();
            InetSocketAddress epoint = (InetSocketAddress) p.address();
            if (epoint.getAddress() != null) {
                checkAddress (epoint.getAddress(), "Socket");
            }
            if (security != null) {
                if (epoint.isUnresolved())
                    epoint = new InetSocketAddress(epoint.getHostName(), epoint.getPort());
                if (epoint.isUnresolved())
                    security.checkConnect(epoint.getHostName(), epoint.getPort());
                else
                    security.checkConnect(epoint.getAddress().getHostAddress(),
                                  epoint.getPort());
            }
            impl = type == Proxy.Type.SOCKS ? new SocksSocketImpl(p)
                                            : new HttpConnectSocketImpl(p);
            impl.setSocket(this);
        } else {
            if (p == Proxy.NO_PROXY) {
                if (factory == null) {
                    impl = new PlainSocketImpl();
                    impl.setSocket(this);
                } else
                    setImpl();
            } else
                throw new IllegalArgumentException("Invalid Proxy");
        }
    }
}

---------未完,敬请关注-------------

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值