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");
}
}
}
---------未完,敬请关注-------------