能否谈谈客户端编程语言SOCKET编程模型以及设计

本页链接:
网友们正在为您出谋划策,请耐心等待!
猜你感兴趣深度解析互联网技术学习栈,做互联网时代最接地气的架构师,用技术创新点亮程序人生。...
JavaSocket编程之Netty框架线程模型
1.Netty概述
Netty是一个由JBoss提供的高效的Java
NIO client-server(客户端-服务器)开发框架,使用Netty可以快速开发网络应用。Netty提供了一种新的方式来使开发网络应用程序,使其很容易使用且有很强的可扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,采用事件驱动机制,非阻塞执行,所以整个Netty都是异步的。Netty框架体系结构图如下:
2.Java NIO缓冲区与通道
一个Buffer对象是固定数量的数据的容器,其作用是一个存储器。通道Channel是I/O传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,您想传递出去的数据被置于一个缓冲区,被传送到通道。缓冲区是包在一个对象内的基本数据元素数组。Buffer类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中。缓冲区家族成员如下所示:
缓冲区Buffer具有四个属性来提供关于其所包含的数据元素的信息。
容量(Capacity)缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,且不能被改变。
上界(Limit)缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
位置(Position)下一个要被读或写的元素的索引。位置会自动由相应的get(
)函数更新。
标记(Mark)一个备忘位置。调用mark(
)来设定mark = postion。调用reset( )设定position = mark。标记在设定前是未定义的(undefined)。
这四个属性之间总是遵循以下关系
0 &= mark &= position &= limit &= capacity
3.Netty核心网络模型
Netty是典型的Reactor模型结构,在实现上,Netty中的Boss类充当mainReactor,NioWorker类充当subReactor(默认NioWorker的个数是当前服务器的可用核数)。在处理新来的请求时,NioWorker读完已收到的数据到ChannelBuffer中,之后触发ChannelPipeline中的ChannelHandler流。Netty是事件驱动,非阻塞的,可以通过ChannelHandler链来控制执行流向。
3.1 Refactor单线程模型
Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成;
Reactor单线程模型特点:
NIO服务端,接收客户端的TCP连接;
NIO客户端,向服务端发起TCP连接;
NIO服务端/客户端,读取通信对端的请求或者应答消息;
NIO服务端/客户端,向通信对端发送消息请求或者应答消息
使用场景:对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,比如一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送。Reactor单线程模型如下图所示:
3.2 Refactor多线程模型
Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操作。
Reactor多线程模型的特点:
1)有一个专门的NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;
2)网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;
3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。
Reactor多线程模型如下图所示:
3.3 Netty服务器端创建过程
Netty服务器启动时,创建两个NIOEventLoopGroup独立的Reator线程池,一个用于接收客户端的TCP连接,一个用于处理IO的相关的读写操作。
Netty线程模型是建立在Reactor模型的基础上,线程模型并不是一成不变的,通过启动参数的配置,可以在不同的线程模型之间切换。
Netty服务器端创建过程序列图如下:
1)首先创建一个ServerBootstrap实例,这是Netty服务端的启动辅助类;
2)设置并绑定Reactor线程池,Netty的Reactor线程池是EventLoopGroup,它其实就是EventLoop线程的数组。EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel;
3)设置并绑定服务端NIOserverSocketChannel,
Netty通过工厂类,利用反射方式创建NioServerSocketChannel对象
4)设置TCP连接参数,TCP链路建立时创建并初始化ChannelPipeline,它本质是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler的执行;
5)添加并设置ChannelHandler,加入到ChannelPipeline事件流;
6)服务器端绑定监听端口,并启动服务器;
7)启动NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合;
8)当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的相应方法;
9)执行Netty系统并调度执行ChannelHandler业务逻辑
3.4Netty线程模型实例
注册两个OutboundHandler,执行顺序为注册顺序的逆序,注册两个InboundHandler,执行顺序为注册顺序,注册HelloServerInHandler用于接收客户端消息已经向客户端发送消息,主要代码如下:
本订阅号提供Java相关技术分享,从Java编程基础到Java高级技术,从JavaWeb技术基础Jsp、Servlet、JDBC到SSH、SSM开发框架,从REST风格接口设计到分布式项目实战。剖析主流开源技术框架,用亲身实践来谱写深度Java技术日志。
Java技术日志
微信扫描二维码,关注Java技术日志
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!Socket编程模型之重叠IO(Overlapped I/O)模型_其它编程-织梦者
当前位置:&>&&>& > Socket编程模型之重叠IO(Overlapped I/O)模型
Socket编程模型之重叠IO(Overlapped I/O)模型
转载请注明来源:http://blog.csdn.net/caoshiying/article/details/
Winsock2的发布使得Socket I/O有了和文件I/O统一的接口。我们可以通过使用Win32文件操纵函数ReadFile和WriteFile来进行Socket I/O。伴随而来的,用于普通文件I/O的重叠I/O模型和完成端口模型对Socket I/O也适用了。这些模型的优点是可以达到更佳的系统性能,但是实现较为复杂,里面涉及较多的C语言技巧。例如我们在完成端口模型中会经常用到所谓的“尾随数据”。
这个模型与上述其他模型不同的是它使用Winsock2提供的异步I/O函数WSARecv。在调用WSARecv时,指定一个WSAOVERLAPPED结构,这个调用不是阻塞的,也就是说,它会立刻返回。一旦有数据到达的时候,被指定的WSAOVERLAPPED结构中的hEvent被Signaled。下面分别讲解关键API函数。
二、HeapAlloc
HeapAlloc是一个Windows API函数。它用来在指定的堆上分配内存,并且分配后的内存不可移动。它分配的内存不能超过4MB。函数原型是:
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);hHeap:要分配堆的句柄,可以通过HeapCreate()函数或GetProcessHeap()函数获得。
dwFlags:堆分配时的可选参数,其值可以为以下的一种或多种:
HEAP_GENERATE_EXCEPTIONS:如果分配错误将会抛出异常,而不是返回NULL。异常值可能是STATUS_NO_MEMORY,表示获得的内存容量不足,或是STATUS_ACCESS_VIOLATION,表示存取不合法。HEAP_NO_SERIALIZE:不使用连续存取。HEAP_ZERO_MEMORY:将分配的内存全部清零。
dwBytes:要分配堆的字节数。
返回值:如果成功分配内存,返回值为一个指向所分配内存块的首地址的(void*)指针。如果分配内存失败,并且没有指定HEAP_GENERATE_EXCEPTIONS,则返回NULL。如果指定了HEAP_GENERATE_EXCEPTIONS,则抛出异常,而不返回NULL,异常代码有:
STATUS_NO_MEMORY:由于缺少可用内存或者是堆损坏导致分配失败。STATUS_ACCESS_VIOLATION:由于堆损坏或者是不正确的函数参数导致分配失败。
三、GetProcessHeap
GetProcessHeap是一个Windows API函数。它返回调用进程的默认堆句柄。函数原型是:
HANDLE GetProcessHeap(void);函数没有参数。如果函数成功,返回调用进程的默认内存堆句柄。这个函数允许你从线程的堆动态分配内存,而不必使用HeapCreare函数建立一个堆。如果函数失败,返回 Null。若想,可以调用GetLastError获得更多错误信息。
四、HeapFree
HeapFree是一个Windows API函数。它用来释放堆内存。函数原型是:
BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);hHeap:堆内存块释放。这个参数是HeapCreate或GetProcessHeap函数返回的句柄。
dwFlags:指定几个可控释放的内存块。指定以下值将覆盖flOptions中指定参数对应的值,当堆是由使用HeapCreate函数。它的值可以是HEAP_NO_SERIALIZE,表示串行存取将不会被使用。为了确保序列化访问,所有调用这个函数将被禁用,在调用HeapCreate指定HEAP_NO_SERIALIZE。在这种情况下,没有必要在此函数调用另外指定HEAP_NO_SERIALIZE。访问进程堆时没有指定此值。该系统可以创建应用程序的过程中额外的线程,如CTRL + C处理程序,同时访问进程堆。
lpMem:被释放的内存块的指针。这HeapAlloc或HeapReAlloc函数返回的指针。如果这个指针为NULL,则为空。
如果函数成功,返回值是非零。否则为零。应用程序可以调用GetLastError扩展的错误信息。
五、WSARecv
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用到重叠结构。函数原型是:
int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);s:连接到此服务器的socket对象。
lpBuffers:接收到的数据保存在这里。
dwBufferCount:有几个缓冲区。
// 这里需要一个由WSABUF结构构成的数组
lpNumberOfBytesRecvd:实际收到了多少个字节的数据。如果lpOverlapped参数不为空,那个这个参数可以为空。据MSDN表述,这样做有助于避免潜在的错误。
lpFlags:这个参数很复杂。实在是不好翻译,我贴出MSDN的原文吧。它的值可以是:
MSG_PEEK:Peeks at the incoming data. The data is copied into the buffer, but is not removed from the input queue. This flag is valid only for nonoverlapped sockets.MSG_OOB:Processes OOB data.MSG_PARTIAL:This flag is for message-oriented sockets only. On output, this flag indicates that the data specified is a portion of the message transmitted by the sender. Remaining portions of the message will be specified in subsequent receive operations.
A subsequent receive operation with the MSG_PARTIAL flag cleared indicates end of sender's message. As an input parameter, this flag indicates that the receive operation should complete even if only part of a message has been received by the transport provider.MSG_PUSH_IMMEDIATE:This flag is for stream-oriented sockets only. This flag allows an application that uses stream sockets to tell the transport provider not to delay completion of partially filled pending receive requests. This is a hint to the transport
provider that the application is willing to receive any incoming data as soon as possible without necessarily waiting for the remainder of the data that might still be in transit. What constitutes a partially filled pending receive request is a transport-specific
matter. In the case of TCP, this refers to the case of incoming TCP segments being placed into the receive request data buffer where none of the TCP segments indicated a PUSH bit value of 1. In this case, TCP may hold the partially filled receive request a
little longer to allow the remainder of the data to arrive with a TCP segment that has the PUSH bit set to 1. This flag tells TCP not to hold the receive request but to complete it immediately. Using this flag for large block transfers is not recommended since
processing partial blocks is often not optimal. This flag is useful only for cases where receiving and processing the partial data immediately helps decrease processing latency. This flag is a hint rather than an actual guarantee. This flag is supported on
Windows 8.1, Windows Server 2012 R2, and later.MSG_WAITALL:The receive request will complete only when one of the following events occurs: The buffer supplied by the caller is completely full. The connection has been closed. The request has been canceled or an error occurred. Be aware that if the underlying
transport provider does not support MSG_WAITALL, or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then this call will fail with WSAEOPNOTSUPP.
This flag is not supported on datagram sockets or message-oriented sockets.
lpCompletionRoutine:回调函数,事件通知入口。详情请看下节。
WSA_IO_PENDING是最常见的返回值,这是说明我们的WSARecv操作成功了,但是I/O操作还没有完成,所以我们就需要绑定一个事件来通知我们操作何时完成。如果函数调用发生错误,接收操作立即返回0。这种情况下,这个完成例程将会被列入即将调用的时间表当线程变为警告状态, 否则,函数返回SOCKET_ERROR,我们可以通过WSAGetLastError获取错误代码. WSA_IO_PENDING表示这个重叠操作成功初始化,稍后便会显示。
六、CompletionROUTINE
这个函数是WSARecv函数的参数中回函数的参数格式。它本身是不存在的,用户代码应该按照以下范式声明:
void CALLBACK CompletionROUTINE(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
CompletionRoutine is a placeholder for an application-defined or library-defined function name. The dwError specifies the completion status for the overlapped operation as indicated by lpOverlapped. The cbTransferred parameter specifies the number of bytes
received. The dwFlags parameter contains information that would have appeared in lpFlags if the receive operation had completed immediately. This function does not return a value.
Returning from this function allows invocation of another pending completion routine for this socket. When using WSAWaitForMultipleEvents, all waiting completion routines are called before the alertable thread's wait is satisfied with a return code of WSA_IO_COMPLETION.
The completion routines can be called in any order, not necessarily in the same order the overlapped operations are completed. However, the posted buffers are guaranteed to be filled in the same order in which they are specified.
If you are using I/O completion ports, be aware that the order of calls made to WSARecv is also the order in which the buffers are populated. WSARecv should not be called on the same socket simultaneously from different threads, because it can result in
an unpredictable buffer order.
七、PEERIO_OPERATION_DATA结构
Windows API定义的LWSAOVERLAPPED结构远远不能满足从主线程到回调线程传递参数的需要,我们需要定义一个自己的数据结构,通过HeapAlloc分配内存,把指针传递到回调函数,结构如下:
typedef struct
WSAOVERLAPPED
char message[SOCKET_MESSAGE_SIZE];
DWORD received_byte_
iserver_manager *
}PEERIO_OPERATION_DATA, *LPPEERIO_OPERATION_DATA;overlap:记录原始的值。
buf:缓冲区指针。
message:缓冲区。
received_byte_count:收到的字节数。
flags:旗标。
client:这批缓冲区是哪个客户端发送的。
manager:回调结构。
八、示例代码
接着上面几篇Socket文章写,关于公共代码与反射式客户端请参见:Socket编程模型之简单选择模型。下面是新建的overlapped_server工程,新建了一个overlapped_server_manager类型,继承自iserver_manager接口,头文件完整代码如下:
#pragma once
#include &WinSock2.h&
#include &common_callback.h&
//#include &afxsock.h&
#define SOCKET_MESSAGE_SIZE 1024
typedef struct
WSAOVERLAPPED
char message[SOCKET_MESSAGE_SIZE];
DWORD received_byte_
iserver_manager *
}PEERIO_OPERATION_DATA, *LPPEERIO_OPERATION_DATA;
class overlapped_server_manager:
protected iserver_manager
int iclient_
int iaddr_
SOCKET snew_
LPPEERIO_OPERATION_DATA peers[WSA_MAXIMUM_WAIT_EVENTS];
BOOL bnew_
void cleanup();
int find_peer(LPPEERIO_OPERATION_DATA peer);
protected:
bool accept_by_crt();
bool accept_by_winapi();
void receive();
void shutdown();
void start_accept();
void start_receive();
void completion_routine(DWORD error, DWORD transfered, LPPEERIO_OPERATION_DATA peer, DWORD flags);
overlapped_server_manager();
virtual ~overlapped_server_manager();
void CALLBACK do_completion_routine(DWORD error, DWORD transfered, LPWSAOVERLAPPED overlapped, DWORD flags);
实现文件完整代码如下:
#include &overlapped_server_manager.h&
#include &stdio.h&
#include &tchar.h&
#include &cassert&
overlapped_server_manager::overlapped_server_manager()
iport = 5150;
iclient_count = 0;
iaddr_size = sizeof(SOCKADDR_IN);
ZeroMemory(peers, sizeof(LPPEERIO_OPERATION_DATA));
WSAStartup(MAKEWORD(2, 2), &data);
brunning = FALSE;
bnew_client = FALSE;
callback.set_manager(this);
overlapped_server_manager::~overlapped_server_manager()
cleanup();
bool overlapped_server_manager::accept_by_crt()
SOCKADDR_IN server_
SOCKADDR_IN client_
int iret = 0;
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(iport);
iret = bind(server, (struct sockaddr*)&server_addr, iaddr_size);
if (iret == SOCKET_ERROR)
server_addr.sin_port = htons(iport);
} while (iret == -1);
listen(server, 3);
printf(&服务启动成功,端口是:%d\n&, iport);
while (brunning)
snew_client = accept(server, (struct sockaddr*)&client_addr, &iaddr_size);
if (snew_client == INVALID_SOCKET)
printf(&新客户端连接:%s:%d\n&, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
bnew_client = TRUE;
bool overlapped_server_manager::accept_by_winapi()
void overlapped_server_manager::receive()
LPPEERIO_OPERATION_DATA peer = NULL;
while (brunning)
if (bnew_client)
peer = (LPPEERIO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PEERIO_OPERATION_DATA));
peer-&buf.len = SOCKET_MESSAGE_SIZE;
peer-&buf.buf = peer-&
peer-&client = snew_
peer-&manager =
peers[iclient_count] =
WSARecv(peer-&client, &peer-&buf, 1, &peer-&received_byte_count, &peer-&flags, &peer-&overlap, &do_completion_routine);
bnew_client = FALSE;
iclient_count++;
SleepEx(1000, TRUE);
void overlapped_server_manager::shutdown()
callback.shutdown();
cleanup();
void overlapped_server_manager::cleanup()
int i = 0;
brunning = FALSE;
for (i = 0; i & iclient_ i++)
HeapFree(GetProcessHeap(), 0, peers[i]);
iclient_count = 0;
void overlapped_server_manager::completion_routine(DWORD error, DWORD transfered, LPPEERIO_OPERATION_DATA peer, DWORD flags)
int index = -1;
index = find_peer(peer);
assert(index != -1);
if (error != 0 || transfered == 0)
closesocket(peer-&client);
HeapFree(GetProcessHeap(), 0, peer);
if (index & iclient_count - 1)
peers[index] = peers[iclient_count - 1];
peers[iclient_count - 1] =
iclient_count--;
peer-&message[transfered] = 0;
send(peer-&client, peer-&message, transfered, 0);
ZeroMemory(&peer-&overlap, sizeof(WSAOVERLAPPED));
peer-&buf.len = SOCKET_MESSAGE_SIZE;
peer-&buf.buf = peer-&
WSARecv(peer-&client, &peer-&buf, 1, &peer-&received_byte_count, &peer-&flags, &peer-&overlap, do_completion_routine);
int overlapped_server_manager::find_peer(LPPEERIO_OPERATION_DATA peer)
int index = -1;
int i = 0;
for (i = 0; i & iclient_ i++)
if (peers[i] != peer)
void overlapped_server_manager::start_accept()
brunning = TRUE;
callback.start_accept_by_crt();
void overlapped_server_manager::start_receive()
brunning = TRUE;
callback.start_receive();
void CALLBACK do_completion_routine(DWORD error, DWORD transfered, LPWSAOVERLAPPED overlapped, DWORD flags)
auto peer = (LPPEERIO_OPERATION_DATA)
auto pmanager = reinterpret_cast&overlapped_server_manager*&(peer-&manager);
pmanager-&completion_routine(error, transfered, peer, flags);
int main()
overlapped_server_
osm.start_accept();
osm.start_receive();
printf(&重叠(Overlapped)I/0模型服务端启动成功。按任意键关闭服务器。\n&);
system(&pause&);
osm.shutdown();
九、运行效果
<img alt="Socket编程模型之重叠IO(Overlapped I/O)模型" src="http://img.zhimengzhe.com/d/file/p//84faa08fcaef9.png"
以上就是Socket编程模型之重叠IO(Overlapped I/O)模型的全文介绍,希望对您学习和使用程序编程有所帮助.
这些内容可能对你也有帮助
更多可查看其它编程列表页。
猜您也会喜欢这些文章socket编程与线程模型四 - 信息加油站义工 - 博客园
评论 - 218
3、无连接socket与多线程
无连接socket很灵活,可以通过同一个socket向很多个地址进行数据写入,从同一个地址进行数据读取。所以这种服务器的组织形式也会很灵活。比如,利用多线程共享同一个服务器端的socket,进行数据读取和写入。
但是需要注意,socket是特殊的I/O,既然属于I/O,那么线程同步与互斥是非常重要的。因为它们读写socket的顺序将不能被保证,或者无法预料。理论上一个端口号对应于不同的缓冲区,也就是端口号是tcp/ip协议栈上数据缓冲区的句柄。
五、有连接的socket
有连接的socket,其编程方法与无连接的客户端和服务器端有很大差别。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 面向连接的socket
socketsocketsocket
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 面向连接的socket二
为了详细的了解面向连接的socket,我们从accept()开始。
accept()从指定的socket的连接请求等待队列里面取出第一个连接请求,然后它就返回新建的一个socket句柄。这个新建的socket可以完成本次取出的连接请求,并开始为它服务。这个被新建的socket具备和用于监听连接请求的socket一样的属性,包括与之一样的异步选择事件(用
函数选择的事件)。然后,由新建的socket为accept()本次取出的连接请求服务,而原来的监听请求的socket又可以回到监听状态。
那么面向连接的socket的通信细节和无连接的有何不一样呢?这个需要研究面向连接的socket使用的数据读取和写入接口和其它接口。
accept(),接受客户端的连接请求,并生成一个 socket为这个客户服务。accept()的出口参数可以提供客户方的SockAddr,即地址。但是服务方返回一些数据没必要用这个地址,在面向连接的数据写入方法中,只需要一个socket就可以了, send()。而面向连接的数据读取方法recv()也只需要同一个socket就可以完成。所以这个通信过程细节如下:
服务端创建一个特殊的I/O --- socket,这个socket用来监听客户连接请求。所以,这个socket需要和服务端的本地地址帮扎。从前面知道,bind()地址就是从这个socket上读取数据的地址,不管是显式还是隐式。
然后服务器调用listen(socket, num),num是一个表示连接请求队列的最大值的整数。对于这个socket上并发的连接请求(请求连接绑扎地址),服务器不能马上响应的,就会被缓存字这个队列,等待服务器处理。但是队列满了以后,到来的请求就会不能被响应。listen()是非常关键的一步,只有调用了这一步,服务器才能监听客户端请求。
listen()以后服务器就调用accept(),提供一个出口参数可以获取请求方的地址。当指定的被accept的socket上的连接请求队列空,accept()会被阻塞。但是accept之前,服务器一直在listen请求。
如果这个上的连接请求缓存队列有连接请求,那么()就会脱离阻塞状态执行。()新建一个为从队列中取出的当前请求服务,而被的,或者也就是被()的那个()继续返回到()状态。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 面向连接的通信过程
如上图,服务器端创建socket1套接字,然后必须把该套接字和一个本地地址sockaddr1进行显式绑扎,这样就可以从socket1上读取数据的,或者说别人可以发送数据到这个地址。
随后,服务器在套接字socket1上调用listen(),进行请求的监听。listen()指定了请求缓冲队列的大小。listen之前的客户端连接请求connect()会失败。
调用listen()之后,服务器调用accept()从连接请求队列中取出一个连接请求,进行服务。如果队列空,accept被阻塞。accept()从队列中取出一个请求,并创建一个为这个取出的请求服务的套接字newSocket,并从出口参数返回该请求的客户端地址ClientAddr1。newSocket具有两个特点,第一个是具备与socket1一样的属性,这就是说newSocket也是绑扎在地址sockaddr1,这也就是从它读取数据的地址。另外一个是newSocket与ClientAddr1也具备了联系,这个地址是把数据写入newSocket的地方。所以,不需要取出出口参数的ClientAddr1这个客户端地址,仅仅通过新的套接字newSocket服务器端就和某个特定的客户端建立了全相关,就可以读取或者写入数据了。
再看客户端。客户端必须首先得到服务器的绑扎地址,客户端创建一个套接字以后,就在该套接字上调用函数。()把一个本地地址隐式绑扎到套接字,作为数据接收地址。并且,把服务器地址和联系起来。所以,通过,客户端就可以进行读出和写入数据。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 面向连接三
socketbindsocketnewSocketnewSocketsocketnewSocket
刚开始学习使用博客,一不小心在博客园首页上发了好几篇, 第五篇就放到文章里啦:

我要回帖

更多关于 客户端编程语言 的文章

 

随机推荐