第六章Socket函数说明.docx
《第六章Socket函数说明.docx》由会员分享,可在线阅读,更多相关《第六章Socket函数说明.docx(74页珍藏版)》请在冰点文库上搜索。
![第六章Socket函数说明.docx](https://file1.bingdoc.com/fileroot1/2023-5/20/e48ee98e-ac5e-444a-9941-4d54f2034b3a/e48ee98e-ac5e-444a-9941-4d54f2034b3a1.gif)
第六章Socket函数说明
第六章Socket函数说明
6.1库函数综述
6.1.1套接字函数
表6.1WindowsSockets1.1版本的BerkeleySockets函数
accept()*
确认外来连接,并将它与一个立即建立的数据套接字联系起来。
原始套接字返回到监听状态。
bind()
给未命名套接字赋一个本地名。
closesocket()*
从进程对象参考表中删去一个套接字,只有当SO_LINGER设置时才阻塞。
connect()*
在指定套接字上初始化连接。
getpeername()
获取与指定套接字连接的对等方的名字。
getsockname()
获取指定套接字的当前名字。
getsockopt()
获取与指定套接字相关的选项。
htonl()
将一个32位数从主机字节顺序转换为网络字节顺序。
htons()
将一个16位数从主机字节顺序转换为网络字节顺序。
inet_addr()
将一个用网际标准点分表示法表示的字符串地址转换成网际地址值。
inet_ntoa()
将一个网际地址值转换成一个用点分十进制表示法表示的字符串地址
ioctlsocket()
为套接字提供控制。
listen()
在指定套接字上监听外来连接。
ntohl()
将一个32位数从网络字节顺序转换为主机字节顺序。
ntohs()
将一个16位数从网络字节顺序转换为主机字节顺序。
recv()*
从一个连接的套接字上接收数据。
recvfrom()*
从一个连接或未连接的套接字上接收数据。
select()*
执行多路同步I/O。
send()*
给一个连接套接字发送数据。
sendto()*
给一个连接或未连接套接字发送数据。
setsockopt()
设置与指定套接字相关的选项。
shutdown()
关闭全双工连接的一部分。
socket()
建立一个通讯用的末端点,返回一个套接字。
*=如果作用于阻塞套接字上,此例程可用阻塞。
这些函数根据功能的不同可以分为如下几类:
(1)套接字函数。
此类函数包括sockets(),bind(),getpeername(),getsockname()和closesocket(),它们主要完成创建,关闭套接字功能,以及对套接字命名与名字获取。
(2)网络连接函数。
此类函数包括listen(),accept(),connect()和shutdown(),它们完成网络连接(如虚电路)的建立与关闭。
此类函数中有部分可阻塞。
(3)数据传输函数。
此类函数包括send(),recv(),sendto()和recvfrom(),它们完成网络数据的发送与接收,全部是可以阻塞的函数。
(4)字节定序函数。
此类函数包括htonl(),htons(),ntohl()和ntohs(),它们完成主机和网络之间数据字节顺序的转换。
(5)地址转换函数。
此类函数包括inet_addr(),inet_ntoa(),它们完成网络字符串地址和Internet地址之间的转换。
(6)套接字控制函数。
此类函数包括getsockopt(),setsockopt(),ioctlsocket()和select(),它们设置/获取套接字的选项,控制/检测套接字的工作状态。
其中select()函数在必要时可能阻塞。
只使用了上述函数的BerkeleySockets源程序基本上可以不加修改地移植到WindowsSockets环境中来。
但是,移植过来的程序有一个最大的问题是“阻塞”。
在BerkeleySockets中,套接字默认的工作模式是操作处于阻塞方式,一个阻塞操作可能阻塞整个Windows环境。
在非抢先Windows环境,强烈推荐程序员使用非阻塞(异步)操作,也就是说,推荐使用WindowsSockets提供的异步选择函数代替可能阻塞的select()函数,并且用网络事件消息来驱动可能阻塞的网络连接函数(accept()和connect())和数据传输函数,这样设计的程序能更好地工作。
5.1.2数据库函数
WindowsSockets定义了如表6.2所示的“数据库”函数:
表6.2WindowsSockets1.1版本定义的“数据库”函数
gethostbyaddr()*
通过网络地址获取主机名字和地址等信息。
gethostbyname()*
通过主机名字获取主机名字和地址等信息。
gethostname()
获取本地主机名。
getprotobyname()*
通过协议名获取协议名和协议号等信息。
getprotobynumber()*
通过协议号获取协议名和协议号等信息。
getservbyname()*
通过服务名获取服务的名字和端口等信息。
getservbyport()*
通过端口获取服务的名字和端口等信息。
*=在某些条件下此例程可能阻塞。
提供这类函数是为了获取网络特定的信息,在最初的Berkeley版本中,它们是作为在文本数据库文件中寻找信息的机构。
在WindowsSockets实现中,可能使用了不依赖于本地数据库文件的方法(如域名服务),但是对应用程序来说请求这些信息的格式是一致的,并且对应用程序来说是透明的。
调用这些例程所获得的信息存放在由WindowsSockets实现分配的一个结构中,函数返回此结构的地址。
因此,应用程序可以通过此结构指针获取所需要的信息,但它决不能试图修改此结构,更不能释放结构的任一部分。
另外,对一个线程来说,WindowsSockets实现只分配了结构的一个备份,任何WindowsSocketsAPI调用都可能修改此结构。
也就是说,结构指针指向的数据只在此线程的下一次WindowsSocketsAPI调用之前才是正确的,应用程序应该在发布任何其它WindowsSocketsAPI调用之前将任何需要的信息拷贝出来。
数据库函数除了gethostname()之外都是阻塞的,WindowsSockets提供它们是为了BerkeleySockets网络程序的可移植性。
在设计实现WindowsSockets应用程序时,推荐使用WindowsSockets提供的数据库函数的异步版本(见下节)。
6.2标准Socket函数
6.2.1accept()
语法:
SOCKETWSAAPI
accept(
INSOCKETs,
OUTstructsockaddrFAR*addr,
OUTintFAR*addrlen
);
此函数用于从套接字上接收一个连接。
它提取挂在套接字s上的连接队列中的第一个连接,创建一个和s有相同属性(包括使用函数WSAAsyncSelect()或WSAEventSelect()注册的异步事件,但不包括监听套接字的套接字组ID)的新数据套接字,并返回一个指向新套接字的句柄。
如果连接队列上没有等待的连接,并且套接字没有标志为非阻塞,那么accept()阻塞调用直到出现一个连接。
如果套接字标志为非阻塞,并且队列上没有等待的连接,那么accept()返回错误WSAEWOULDBLOCK。
新创建的数据套接字不能用来接收更多的连接,它只能用于数据传输;原来的套接字仍然打开,处于监听连接状态。
参数
描述
s
这是一个套接字描述符,该套接字在用作accept()函数的参数前必须先调用过listen()函数,此时它正处于监听连接的状态。
addr
一个可选的指向缓冲区的指针,用来接收连接实体的地址,在通讯层使用。
addr的确切格式由套接字创建时建立的地址族决定。
addrlen
一个可选的指向整数的指针,它调用时含有地址addr指向的空间的大小,返回时含有返回的地址的确切长度(字节数)。
返回值:
如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。
否则返回值INVALID_SOCKET,错误码可通过调用WSAGetLastError()函数得到。
错误码:
WSANOTINITIALISED
未初始化WindowsSocketsDLL,在使用此函数之前必须有一次成功的WSAStartup()函数调用。
WSAENETDOWN
WindowsSockets实现检测到网络系统已经失败。
WSAEFAULT
参数addrlen太小(小于结构sockaddr的大小),或参数addr不是用户地址空间的合法部分。
WSAEINTR
此(阻塞)调用已被WSACancelBlockingCall()函数取消。
WSAEINPROGRESS
一个阻塞的WindowsSockets操作正在进行。
WSAEINVAL
在accept()调用之前没有执行过listen()。
WSAEMFILE
accept()队列入口空,但没有文件描述符可用。
即打开的文件描述符过多。
WSAENOBUFS
无缓冲区空间可用。
WSAENOTSOCK
描述符s不是套接字描述符。
WSAEOPNOTSUPP
s指向的套接字不是一种支持面向连接服务类型的套接字。
WSAEWOULDBLOCK
套接字标志为非阻塞,但现在没有接收到连接。
注释:
该调用只能和基于连接的套接字类型如SOCK_STREAM一起使用。
如果参数addr和/或addrlen等于NULL,那么没有关于接收套接字的远程地址信息返回。
参见:
bind(),connect(),listen(),select(),socket(),WSAAsyncSelect(),WSAAccept()。
6.2.2bind()
语法:
intWSAAPI
bind(
INSOCKETs,
INconststructsockaddrFAR*name,
INintnamelen
);
此函数用于未连接的数据报或流套接字,它将一本地地址与套接字连接,即建立半相关。
当一套接字用socket()创建后,它存在于一名字空间(地址族),但它没有赋予名字。
bind()通过将一本地名字赋予一未命名的套接字,建立起套接字的本地连接(主机地址/端口号)。
参数
描述
s
指示未连接的数据报或流套接字的描述符。
name
赋给套接字的本地地址(名字)。
结构sockaddr定义如下:
structsockaddr{
u_shortsa_family;
charsa_data[14];
};
除sa_family外,其它内容都以网络字节顺序表示。
namelen
地址缓冲区长度。
返回值:
如果没有错误发生,bind()返回0。
否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
错误码:
WSANOTINITIALISED
未初始化WindowsSocketsDLL,在使用此函数之前必须有一次成功的WSAStartup()函数调用。
WSAENETDOWN
WindowsSockets实现检测到网络系统已经失败。
WSAEADDRINUSE
指定的地址已经在使用(参见setsockopt()中的SO_REUSEADDR套接字选项)。
WSAEADDRNOTAVAIL
对于本机器来说,指定的地址是非法地址(WinSock2)。
WSAEFAULT
参数namelen太小(小于结构sockaddr的长度);参数name或namelen不是用户地址空间的合法部分;参数name包含了相关的地址族来说是不正确的地址格式;参数name指向的内存块的前两个字节与套接字描述符s相关的地址族不匹配。
WSAEINPROGRESS
一个阻塞的WindowsSockets操作正在进行。
WSAEAFNOSUPPORT
此套接字不能使用指定地址族中的地址(WinSock1.1)。
WSAEINVAL
此套接字已经捆扎到了一个地址。
WSAENOBUFS
无缓冲区空间可用,连接太多。
WSAENOTSOCK
此描述符不是套接字描述符。
注释:
在Internet地址族中,一个名字有几个部分。
对于SOCK_DGRAM和SOCK_STREAM类型的套接字来说,名字分为三部分:
主机地址,协议号(分别默认设置为UDP和TCP),以及一个标志应用程序的端口号。
在WinSock2中,参数name并不严格地解释为指向“sockaddr”结构的指针,但为了与WindowsSockets的兼容性仍用这种表示。
服务提供者可以把它当作一个指向长度为namelen的内存块而自由处理,在此内存块的前两个字节(对应结构sockaddr定义中的sa_family元素)必须为包含建立套接字的地址族,否则将产生错误WSAEFAULT。
如果应用程序不关心赋予它的地址,则可指定一个等于常数INADDR_ANY的网际地址,和/或等于0的端口。
如果网际地址等于INADDR_ANY,任何合适的网络接口都可用,这就简化了在多宿主机上的应用程序设计。
当一个服务器向几个网络提供服务时,这将变得很重要。
在不指定地址的情况下,服务器可以接收发向其端口的所有UDP数据包和TCP连接请求,而不必关心请求是从哪一个网络接口到达的。
如果端口指定为0,WindowsSockets实现将为应用程序指定一界于1024和5000之间的端口值。
应用程序可在bind()后使用getsockname()来得到赋给它的地址,但要注意的是,对于网际地址等于INADDR_ANY的情况,getsockname()只有当套接字连接后才填入网际地址(Internetaddress),原因是当主机是多地址家族时,几个网际地址都是合法的。
对客户应用程序来说,不鼓励将其绑扎到一个指定的端口,因为这样存在与已经使用了该端口的其它套接字冲突的危险。
由于WindowsSockets只支持AF_INET地址域,因此名字缓冲区的格式只能是sockaddr_in结构。
此结构在winsock.h中定义如下:
structin_addr
{
u_longs_addr;
};
structsockaddr_in
{
u_charsin_len;
u_charsin_family;
u_shortsin_port;
structin_addrsin_addr;
charsin_zero[8];
};
·sin_family字段只能置为AF_INET。
·sin_port字段为应用程序必须连接的端口号。
·sin_addr字段为主机网际地址。
·sin_zero字段未用,留待以后扩充,必须全置为0。
注意,此结构的任何字段均为网络字节顺序。
在WinSock2,支持的地址族得到扩展,因此结构sockaddr不只解释为sockaddr_in,根据不同的地址族,它有不同的确切格式。
参见:
WSACancelBlockingCall(),connect(),listen(),getsockname(),setsockopt(),socket()。
6.2.3closesocket()
语法:
intWSAAPI
closesocket(
INSOCKETs
);
此函数关闭套接字s,并释放分配给该套接字的资源,以后对s的引用都将产生错误WSAENOTSOCK。
如果s涉及一个打开的TCP连接,该连接被释放。
参数
描述
s
待关闭的套接字描述符。
返回值:
如果没有错误发生,closesocket()返回0。
否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
错误码:
WSANOTINITIALISED
未初始化WindowsSocketsDLL,在使用此函数之前必须有一次成功的WSAStartup()函数调用。
WSAENETDOWN
WindowsSockets实现检测到网络系统已经失败。
WSAENOTSOCK
此描述符不是套接字描述符。
WSAEINPROGRESS
一个阻塞的WindowsSockets操作正在进行。
WSAEINTR
此(阻塞)调用已被WSACancelBlockingCall()函数取消。
WSAEWOULDBLOCK
套接字标志为非阻塞并且SO_LINGER设置为非零超时。
注释:
closesocket()函数的语义受套接字选项SO_LINGER和SO_DONTLINGER的影响,具体见下表(默认情况下是允许SO_DONTLINGER):
选项
间隔
关闭类型
等待关闭?
SO_DONTLINGER
不用
Graceful
No
SO_LINGER
零
Hard
No
SO_LINGER
非零
Graceful
Yes
如果SO_LINGER设置(例如,linger结构的l_onoff域非零)并且超时间隔为零(l_linger为零),那么即使队列数据尚未发送或确认,closesocket()函数也不会阻塞。
这称作强制(“hard”或“abortive”)关闭,因为套接字的虚电路立即复位,任何未发送的数据都将丢失,并且在虚电路远程方的任何recv()调用都将以WSAECONNRESET失败。
在这种情况下,套接字不进入TCP状态机的三次握手流程,系统资源被立即释放。
这对于服务器应用程序非正常退出后希望能立即启动很有用,当正常通信中不鼓励使用。
如果SO_LINGER设置超时间隔为非零,closesocket()函数将阻塞,直到剩余的数据都发送完毕或直到超时退出,这称作“雅致”(graceful)关闭。
注意如果套接字设置为非阻塞并且SO_LINGER设置为非零超时,调用closesocket()将失败,错误码为WSAEWOULDBLOCK。
如果SO_DONTLINGER设置在流套接字上(例如,linger结构的l_onoff域为零),closesocket()调用将立即返回。
然而,排队等待传送的任何数据如果可能的话都将在该套接字关闭前发送出去,这也称作“雅致”关闭。
注意在这些情况下,WindowsSockets实现可能会在任意时间内不释放套接字和其他资源,这可能影响希望使用全部可用套接字的应用程序。
如果应用程序要确保连接上的所有数据都被发送或接收到,则应该在调用closesocket()函数之前调用shutdown()函数。
下面给出closesocket()函数的小结:
∙∙ 如果SO_DONTLINGER允许(默认设置),且不会出现错误WSAEWOULDBLOCK──连接在后台“雅致”关闭;
∙∙ 如果SO_LINGER允许并且超时间隔为0,则总是立即返回──连接被重置或终止;
∙∙ 如果SO_LINGER允许并且超时间隔非0:
──对于阻塞套接字,阻塞到所有数据发送完或超时间隔到时;
──对于非阻塞套接字,立即返回并且指示错误WSAEWOULDBLOCK。
参见:
accept(),socket(),ioctisocket(),setsockopt(),WSAAsyncSelect(),WSADuplicateSocket()。
6.2.4connect()
语法:
intWSAAPI
connect(
INSOCKETs,
INconststructsockaddrFAR*name,
INintnamelen
);
此函数用来与对等方建立一个连接。
如果套接字s没有绑扎,则系统赋予本地相关一个唯一值,并且套接字被表示为已绑扎的。
参数
描述
s
用来表示发出连接请求的套接字的描述符。
name
指向一个socketaddress结构的指针,该结构含有对等方的套接字的地址。
namelen
name指向的socketaddress结构的字节数。
返回值:
如果没有错误发生,connect()返回0。
否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
对于阻塞套接字来说,返回值表示连接试图是否成功。
对于非阻塞套接字来说,连接试图不一定马上完成。
当connect()返回SOCKET_ERROR,并且WSAGetLastError()返回WSAEWOULDBLOCK时,应用程序可以:
1.1. 利用select()函数,通过检查套接字是否可写来判断连接请求是否完成。
2.2. 如果应用程序已使用WSAAsyncSelect()函数注册了对连接事件的兴趣,则当连接操作完成时应用程序将收到FD_CONNECT通知(无论成功与否)。
3.3. 如果应用程序已使用WSAEventSelect()函数注册了对连接事件的兴趣,则当连接操作完成时相应的事件对象将设置信号(无论成功与否)。
对于一个非阻塞套接字来说,在连接试图完成之前,任何对该套接字的connect()调用都将以错误码WSAEALREADY失败,在连接成功之后则返回错误码WSAEISCONN。
由于WindowsSockets1.1规范在定义当连接请求正在处理时调用connect()函数返回的错误值有二义性,其返回值对于不同的WindowsSockets实现其值不同,因此不推荐应用程序采用多次调用connect()函数的方式来检测连接是否完成。
如果应用程序员一定要这么做,为了确保程序的可靠运行,他们在处理错误码WSAEALREADY的同时,还必须准备处理WSAEINVAL和WSAEWOULDBLOCK错误码。
如果返回值指出连接试图失败(例如WSAECONNREFUSED,WSAENETUNREACH,WSAETIMEDOUT等),则应用程序可对该套接字再次调用connect()函数。
错误码:
WSANOTINITIALISED
未初始化WindowsSocketsDLL,在使用此函数之前必须有一次成功的WSAStartup()函数调用。
WSAENETDOWN
WindowsSockets实现检测到网络系统已经失败。
WSAEADDRINUSE
套接字的本地地址已被使用,并且该套接字没有使用SO_REUSEADDR来设置允许地址重用。
此错误经常发生在函数bind()调用时,但当bind()函数使用通配地址(包括ADDR_ANY)并且在connect()函数调用时需要“提交”一个指定地址时,此错误能够延迟到connect()函数。
WSAEINTR
阻塞的WinSock1.1调用为WSACancelBlockingCall()函数撤消。
WSAEINPROGRESS
一个阻塞的WindowsSockets1.1操作正在进行。
WSAEALREADY