Socket通信

文章出处: http://www.cnblogs.com/wangcq/p/3520400.html

前面讲了TCP/IP的一些皮毛。又来了个Socket。what?什么玩意儿,之前网络通信的各大层中没见过这丫的身影啊!他在哪里起作用啊?这东西听说过,大学说实话还用过,但是差不多忘了具体的情况了。只知道传输数据写过这样的代码。 

Socket是应用层与传输层之间的一个中间软件抽象层,它是一组接口。 所以TCP/IP分层结构中不见他的身影。那么它到底是干嘛的呢?在设计模式中,Socket其实就是一个门面模式,他把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是全部,让Socket去组织数据,以符合制定的协议。那么这里特指的协议肯定是传输层和应用层之间涉及的协议。

socket的大致工作原理如图:

先从服务器说起。服务器端首先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。这个大学的时候写过类似的代码,但是具体想不起来了。  接续哈,在这时如果有一个客户端初始化一个socket,然后连接服务器,如果连接成功了,此时客户端与服务端的连接也就算是建立了,客户端发送数据请求,服务端接收数据请求并且处理请求,然后把数据再发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。


发现我看的这篇文章很不错。我打算都码上一遍了。

    我们深谙信息交流的价值,那网络的进程之间是如何进行通信的呢?我们每天打开浏览器网页时,浏览器进程究竟是如何和服务器的进程进行通信的呢?当我们使用QQ聊天的时候,QQ进程怎么与服务器或者你好友所在的QQ进行进程通信?也就是,我怎么知道要发到哪个网络设备,而且被指定的进程程序也就是QQ接收到呢?这些都得靠socket?那么到底什么是socket?socket的类型有哪些?还有socket有哪些基本函数?这些都是本文想要介绍的,本文的目录如下:

    1 网络中如何进行通信?

    2Socket是什么?

    3 socket的基本操作

            3.1 socket()函数

             3.2 bind()函数

            3.3 lisent(), connect()函数

            3.4 accept() 函数

            3.5 read() write()

            3.6 close()

    4 socket中TCP的三次握手建立详解

    5 socket中TCP的四次分手详解

    6 一个例子

1 网络中如何进行通信?

本地的进程间通信 IPC 有很多方式,但大约为4类, 这几个一定要写一篇文章单独理解,太重要!

    消息传递(管道, FIFO, 消息队列),

     同步 (互斥量,条件变量,读写锁, 文件和写记录锁, 信号量),

     共享内存(匿名的和具名的), 

    远程过程调用(Solaris门和Sun RPC)。

但这些目前都不是本文的重点,我们现在要讨论的是网络进程之间如何进行通信?首先要解决的是如何标识一个唯一的进程,否则找不到目标通信无从谈起。在本地的话可以通过PID来唯一标识一个进程,但是在网络中这个是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题。网络层的ip地址,可以唯一的标识网络中的主机,而传输层的协议+端口可以唯一标识主机中的应用程序(进程),这样利用三元组(IP地址, 协议, 端口)就可以标识网络中的进程了,网络中的进程通信就可以利用这个标志与其他进程进行交互了。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD 的套接字 (socket)和 UNIX System V 的TLI(目前已经被淘汰)来实现网络进程间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中的通信无处不在,这就是我们说的“一切皆socket”

2 什么是socket?

上面我们已经知道了网络中的进程是通过socket来进行的,那么什么是socket呢?socket起源于UNIX,而Unix/Linux基本哲学之一就是 一切皆文件,都可以 打开, 读写,关闭  这种模式进行操作。socket其实就是该模式的一个实现而已,socket即是一种特殊的文件,一些socket函数就是对其进行的一些操作, 读写,打开,关闭等,这些函数后面进行介绍。

3 socket的基本操作

既然socket是 打开--读写--关闭 这种模式的一种实现,那么socket就提供了这些操作对应的接口,下面以TCP为例子,介绍几个基本的socket接口函数

    3.1 socket()

        int socket (int domain, int type, int protocol);

        socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个吻戏描述,而socket函数用于创建一盒socket描述符,它唯一标识一个socket,这个socket描述字跟文件描述字一样,后续的操作都要用到它,把它作为参数,用过他进行一些读写操作。正如可以给fopen的传入不同参数值,以打开不同的文件,创建socket的时候,也可以指定不同的参数创建不同的socket描述符,下面来看socket函数的三个参数:

        domain:即协议域,又称为协议族。长用的协议族有AF_INET, AF_INET6,AF_LOCAL, AF_ROUTE 等等,协议族决定了socket的地址类型,在通信中必须曹勇对应的地址,如果AF_INET决定了要用IPV4地址(32位)与端口号(16位)的组合,AF_UNIX决定了要用一个绝对路径作为地址。

    type: 指的是socket的类型,常用的有 SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_PACHET, SOCK_SEDPACHET等等。

          protocol:顾名思义,就是指定协议。常用的协议有,IPPROTO_TCP, IPPTOTO_UDP,IPPROTO_SCIP,IPPRPTO_TIPC等,分别对应tcp, udp, stcp, tipc。  

            当我们调用socket函数创建一个socket时,返回的socket描述字它存在于协议族空间中,但是没有一个具体的地址,如果要给出一个具体的地址,就必须调用bind函数,否则就当调用connect函数,listen函数的时候紫铜自动分配一个端口。

    3.2 bind()函数

    正如上面所说,bind函数把一个地址族中的特定地址给socket, 例如对应 AF_INET, AF_INET6 就是把一个ipv4或ipv6地址和端口号组合赋给socket。

int bind (int sockfd, const struct sockaddr*addr, socklen_t addrlen);

sockfd:即socket描述字,他是通过socket函数创建的,唯一标识一个socket。bind()函数就是给这个描述字绑定一个名字。

addr:一个const struct sockaddr* 指针,指向要绑定给sockfd的协议地址。这个地质结构根据地址创建socket时的地址协议族的不同而不同。

addrlen:对应的是地址长度

通常服务器在启动的时候都会绑定一个众做周知的地址,如 ip地址+端口号,用于提供服务,客户就可以通过他来连接服务器,而客户端就不用指定,在系统自动分配一个端口号和自身的ip地址组合。这就是为什么服务器端在调用listen()函数之前会调用bind(),而客户端就不用管这件事情。在其connet的时候系统随机生成一个就行。

3.3 listen(), connet()函数

如果作为一个服务器,在调用socket, bind两个函数之后就会调用listen这个函数来监听这个socket,如果是客户端的话就调用connect发出连接请求,服务器就会收到这个请求。

int listen(int sockfd, int backlog);

int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

listen函数的第一个参数就是要监听的socket描述字,第二个参数是相应socket可以排队的最大连接数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户端连接请求

connect()函数的第一个参数是客户端socket的描述字,第二个是服务器的socket

地址,第三个参数是socket地址的长度,客户端通过调用connect函数来与TCP服务器进行连接。

3.4 accept()函数

TCP的服务器端依次调用了 socket,bind, listen函数之后,就会监听指定的socket地址了。tcp客户端依次调用了 socket, connect之后,就想TCP服务器发送一个连接请求。TCP服务端监听到这和请求之后,就会调用accept()函数去接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类似于普通文件的读写I/O操作。

int accept(int sockfd, struct sockaddr*addr, socklen_t*addrlen)

accept函数的第一个参数是服务器的socket描述字,第二个参数为指向struct sockaddr* 的指针,用于返回客户端的协议地址,第三个参数是协议地址的长度。如果accept成功,那么返回值是由内核自动生成的一个全新的描述符,代表返回客户的TCP连接。

3.5 read() write()等函数

万事俱备只欠东风,至此服务器与客户已经建立好了连接,可以调用网络I/O进行0读写操作了,实现网络中不同进程之间的通信。常见的IO操作有下面几组

read() write()  recv() send() readv() writev() recvmsg() sendmsg() recvfrom() sendto()

3.6 close()函数

在服务器与客户端建立了连接之后,会进行一些读写操作,完成了读写操作就得关闭相应的socket描述字。好比操作完打开的文件要调用fclose关闭打开的文件,

int close(int fd)

close一个TCP socket的缺省行为时把该socket标记为已关闭,然后立即返回到调用程序。该描述字不能再由进程使用,也就是不能再作为read或者write的第一个参数。

     应当注意的是, close操作只是使得相应的socket描述字的引用计数-1,只有当引用计数为0的时候,才出发TCP向客户端向服务器发送终止连接请求。

4 sockte中TCP三次握手建立连接详解

主要分为三个阶段,1 客户端向服务端发一个消息,附带SYN J

2 服务端向客户端响应一个信息,附带SYN K, 另外发过来ACK J + 1

3 客户端再次向服务端发送一个确认消息,附带 ACK K+ 1

这样就完成了著名的三次握手过程。那么他在函数中是如何实现的呢?请看下图:


从图中可以看出来,客户端调用connect时,会触发连接请求,向服务端发送一个 SYN J包,这是connect进入了阻塞状态,服务端监听到了连接请求,即收到了SYN J 包,调用Accept函数接受请求并向客户端发送 SYN K, ACK J+1,这时accept也进入了阻塞状态,客户端收到了SYN K, ACK J+1 之后,这时connect返回,并对SYN K 进行进一步确认,服务器收到ACK K + 1时,accept返回,至此三次握手完毕,连接建立。

5 socket中TCP的四次握手释放连接详解。

image

某个应用进程首先调用close方法主动关闭连接,这时TCP发送一个 FIN M,

另一端接受到FIN M 之后,执行被动关闭的过程,对FIN进行确认。他的接受也作为文件结束符传递给应用程序,因为FIN的接受意味着应用进程在相应的连接上不会再接受额外的数据了。

一段时间之后,接受到文件结束符的应用程序调用到其自身的close方法关闭它的sicket,这导致他的TCP发送一个FIN N;

接收到这个FIN的源发送端TCP对它确认。这样没法个方向飞上都有一个 FIN,和 ACK。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娅娅梨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值