socket编程与线程模型四

 

3、无连接socket与多线程

无连接socket很灵活,可以通过同一个socket向很多个地址进行数据写入,从同一个地址进行数据读取。所以这种服务器的组织形式也会很灵活。比如,利用多线程共享同一个服务器端的socket,进行数据读取和写入。

但是需要注意,socket是特殊的I/O,既然属于I/O,那么线程同步与互斥是非常重要的。因为它们读写socket的顺序将不能被保证,或者无法预料。理论上一个端口号对应于不同的缓冲区,也就是端口号是tcp/ip协议栈上数据缓冲区的句柄。

 

五、有连接的socket

1、概述

有连接的socket,其编程方法与无连接的客户端和服务器端有很大差别。

                                                                                   面向连接的socket
 

需要说明的是,面向连接的socket变成模型中,服务器端创建一个socket,并把一个地址与这个socket显式绑扎。

                                                                                         
                                                                 面向连接的socket
 

为了详细的了解面向连接的socket,我们从accept()开始。

accept()从指定的socket的连接请求等待队列里面取出第一个连接请求,然后它就返回新建的一个socket句柄。这个新建的socket可以完成本次取出的连接请求,并开始为它服务。这个被新建的socket具备和用于监听连接请求的socket一样的属性,包括与之一样的异步选择事件(用 WSAAsyncSelect 或者WSAEventSelect 函数选择的事件)。然后,由新建的socketaccept()本次取出的连接请求服务,而原来的监听请求的socket又可以回到监听状态。

那么面向连接的socket的通信细节和无连接的有何不一样呢?这个需要研究面向连接的socket使用的数据读取和写入接口和其它接口。

accept(),接受客户端的连接请求,并生成一个 socket为这个客户服务。accept()的出口参数可以提供客户方的SockAddr,即地址。但是服务方返回一些数据没必要用这个地址,在面向连接的数据写入方法中,只需要一个socket就可以了, send()。而面向连接的数据读取方法recv()也只需要同一个socket就可以完成。所以这个通信过程细节如下:

服务端创建一个特殊的I/O --- socket,这个socket用来监听客户连接请求。所以,这个socket需要和服务端的本地地址帮扎。从前面知道,bind()地址就是从这个socket上读取数据的地址,不管是显式还是隐式。

然后服务器调用listensocket, num),num是一个表示连接请求队列的最大值的整数。对于这个socket上并发的连接请求(请求连接绑扎地址),服务器不能马上响应的,就会被缓存字这个队列,等待服务器处理。但是队列满了以后,到来的请求就会不能被响应。listen()是非常关键的一步,只有调用了这一步,服务器才能监听客户端请求。

listen()以后服务器就调用accept(),提供一个出口参数可以获取请求方的地址。当指定的被acceptsocket上的连接请求队列空,accept()会被阻塞。但是accept之前,服务器一直在listen请求。

如果这个 socket 上的连接请求缓存队列有连接请求,那么 accept ()就会脱离阻塞状态执行。 accept ()新建一个 socket 为从队列中取出的当前请求服务,而被 accept socket ,或者也就是被 listen ()的那个 socket ()继续返回到 listen ()状态。
                                                                                         面向连接的通信过程

 

如上图,服务器端创建socket1套接字,然后必须把该套接字和一个本地地址sockaddr1进行显式绑扎,这样就可以从socket1上读取数据的,或者说别人可以发送数据到这个地址。

随后,服务器在套接字socket1上调用listen(),进行请求的监听。listen()指定了请求缓冲队列的大小。listen之前的客户端连接请求connect()会失败。

调用listen()之后,服务器调用accept()从连接请求队列中取出一个连接请求,进行服务。如果队列空,accept被阻塞。accept()从队列中取出一个请求,并创建一个为这个取出的请求服务的套接字newSocket,并从出口参数返回该请求的客户端地址ClientAddr1newSocket具有两个特点,第一个是具备与socket1一样的属性,这就是说newSocket也是绑扎在地址sockaddr1,这也就是从它读取数据的地址。另外一个是newSocketClientAddr1也具备了联系,这个地址是把数据写入newSocket的地方。所以,不需要取出出口参数的ClientAddr1这个客户端地址,仅仅通过新的套接字newSocket服务器端就和某个特定的客户端建立了全相关,就可以读取或者写入数据了。

再看客户端。客户端必须首先得到服务器的绑扎地址 sockaddr1 ,客户端创建一个套接字 socket2 以后,就在该套接字上调用 connect 函数。 connet ()把一个本地地址 ClientAddr1 隐式绑扎到套接字 socket2 ,作为数据接收地址。并且, connect 把服务器地址 sockaddr1 socket2 联系起来。所以,通过 socket2 ,客户端就可以进行读出和写入数据。
                                                                                            面向连接三
 

如上图所示,全相关的建立过程。现在我们有个统一的观点,在一个socket上进行bind()一个本地地址(只能是本地地址才能被绑扎),就是本地程序在这个socket上的数据读取地址;但对通信的另一方来说就是数据的写入地址。服务器端为每一个不同的客户端产生一个newSocket进行服务,它们不同的地方就是这些newSocket具有不同的数据写入地址。但是具有一致的绑扎地址,尽管如此,不同的客户端发送的数据不会混淆,看来读取地址与socket句柄有关,所以不同的newSocket虽然具备同一个读取地址,但是会读到各自的数据。

客户端提前知道服务端地址,这是客户端的写地址。通过 connect ()请求, connect ()隐式给客户端绑扎一个本地地址作为读取地址,并且显示绑扎服务端地址作为发送地址 。服务器接受请求,并取得客户端地址,作为写地址。这样一来,双方的 socket 都具备了读写能力,所以建立了一个数据连接通道。


刚开始学习使用博客,一不小心在博客园首页上发了好几篇, 第五篇就放到文章里啦:
socket编程与线程模型五

转载于:https://www.cnblogs.com/worldreason/archive/2008/05/09/1189920.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值