前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是由于最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人;但主要是由于这段时间一直在看html5的东西,看到web socket时以为颇有意思,动手写几个demo,但web socket须要特定的服务器支持,因为标准制定工做还没完成,因此没有多少主流的服务器支持,本身在网上下载了几个实现,包括php的、C#的、甚至Node.js的,但一个是协议变化比较大,不少代码已通过时了,再就是有一些支持最新的标准,可是我想稍微改造一下,看人家源代码的时候云里雾里,看看别人的代码行数也很少,决定本身实现一个。php
悲剧由此开始,虽然哥们儿国内非知名工科大学毕业,但好歹也是科班CS出身,但大学得过且过,什么TCP/IP协议,什么socket了都没概念。为了作出一个简单的支持广播的websocket server,在网上找了不少相关代码,左抄一句,右抄一句,弄了一个星期居然仍是漏洞百出,调试不起来,只好从头来过了,先补一些基本知识,而后再一步步根据原理实现,今天终于实现了绝大部分功能,由此真的感觉到了,搞计算机必须得有理论指导实践,不然只能像个没头苍蝇处处乱撞。html
要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,html5
从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不一样于ISO模型的七个分层,TCP/IP协议参考模型把全部的TCP/IP系列协议归类到四个抽象层中web
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等编程
传输层:TCP,UDP浏览器
网络层:IP,ICMP,OSPF,EIGRP,IGMP服务器
数据链路层:SLIP,CSLIP,PPP,MTUwebsocket
每一抽象层创建在低一层提供的服务上,而且为高一层提供服务,看起来大概是这样子的网络
估计有兴趣打开此文的同窗都对此有必定了解了,加上我也是只知其一;不知其二,因此就不详细解释,有兴趣同窗能够上网上搜一下资料dom
在TCP/IP协议中两个因特网主机经过两个路由器和对应的层链接。各主机上的应用经过一些数据通道相互执行读取操做
咱们知道两个进程若是须要进行通信最基本的一个前提能可以惟一的标示一个进程,在本地进程通信中咱们可使用PID来惟一标示一个进程,但PID只在本地惟一,网络中的两个进程PID冲突概率很大,这时候咱们须要另辟它径了,咱们知道IP层的ip地址能够惟一标示主机,而TCP层协议和端口号能够惟一标示主机的一个进程,这样咱们能够利用ip地址+协议+端口号惟一标示网络中的一个进程。
可以惟一标示网络中的进程后,它们就能够利用socket进行通讯了,什么是socket呢?咱们常常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操做抽象为几个简单的接口供应用层调用已实现进程在网络中通讯。
socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在创建链接打开后,能够向本身文件写入内容供对方读取或者读取对方内容,通信结束时关闭文件。
socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通信的socket为例,其交互流程大概是这样子的
服务器根据地址类型(ipv4,ipv6)、socket类型、协议建立socket
服务器为socket绑定ip地址和端口号
服务器socket监听端口号请求,随时准备接收客户端发来的链接,这时候服务器的socket并无被打开
客户端建立socket
客户端打开socket,根据服务器ip地址和端口号试图链接服务器socket
服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回链接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回链接信息后才返回,开始接收下一个客户端谅解请求
客户端链接成功,向服务器发送链接状态信息
服务器accept方法返回,链接成功
客户端向socket写入信息
服务器读取信息
客户端关闭
服务器端关闭
在TCP/IP协议中,TCP协议经过三次握手创建一个可靠的链接
第一次握手:客户端尝试链接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
定睛一看,服务器socket与客户端socket创建链接的部分其实就是大名鼎鼎的三次握手
socket编程API
前面提到socket是"打开—读/写—关闭"模式的实现,简单了解一下socket提供了哪些API供应用程序使用,仍是以TCP协议为例,看看Unix下的socket API,其它语言都很相似(PHP甚至名字都几乎同样),这里我就简单解释一下方法做用和参数,具体使用有兴趣同窗能够看看博客参考中的连接或者上网搜索
int socket(int domain, int type, int protocol);
根据指定的地址族、数据类型和协议来分配一个socket的描述字及其所用的资源。
domain:协议族,经常使用的有AF_INET、AF_INET六、AF_LOCAL、AF_ROUTE其中AF_INET表明使用ipv4地址
type:socket类型,经常使用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
protocol:协议。经常使用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把一个地址族中的特定地址赋给socket
sockfd:socket描述字,也就是socket引用
addr:要绑定给sockfd的协议地址
addrlen:地址的长度
一般服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就能够经过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为何一般服务器端在listen以前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
int listen(int sockfd, int backlog);
监听socket
sockfd:要监听的socket描述字
backlog:相应socket能够排队的最大链接个数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
链接某个socket
sockfd:客户端的socket描述字
addr:服务器的socket地址
addrlen:socket地址的长度
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
TCP服务器监听到客户端请求以后,调用accept()函数取接收请求
sockfd:服务器的socket描述字
addr:客户端的socket地址
addrlen:socket地址的长度
ssize_t read(int fd, void *buf, size_t count);
读取socket内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度
ssize_t write(int fd, const void *buf, size_t count);
向socket写入内容,其实就是发送内容
fd:socket描述字
buf:缓冲区
count:缓冲区长度
int close(int fd);
socket标记为以关闭 ,使相应socket描述字的引用计数-1,当引用计数为0的时候,触发TCP客户端向服务器发送终止链接请求。
PS. 有同窗看完后发现没有demo示例,参考中的示例已经很不错了,我就不班门弄斧了,并且我用C#实现了一个websocket server,接下来的博客中会有介绍。另外因为刚刚实际接触socket,文中谬误较多,还望你们批评指正,文章内容主要参考上面两个博文,图片所有来源于网络,在百度图片搜索得来,没法注明第一源地址,若有版权问题请站内信联系,第一时间处理。
转:http://www.cnblogs.com/dolphinX/p/3460545.html