套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本单元。根据网络通信的特征,套接字可分为两类:流套接字和数据报套接字。
流套接字提供双向的、有序的、无重复并且无记录边界的数据流服务,它使用于处理大量数据局。
数据报套接字支持双向的数据流,但并不保证数据传输的可靠性、有序性和无重复性。
下面介绍流套接字的编程模型,流套接字的服务进程和客户进程在通信前必须创建各自的套接字并建立连接,然后才对相应的套接字进行“读”、“写”操作,实现数据的传输。具体编程步骤如下:
1、服务器进程创建套接字
SOCKET socket( int af, int type, int protocol );
其中,参数af用于指定网络地址类型,一般取AF_INET,表示该套接字在Internet域中,进行通信。参数type用于知道套接字的类型,若取SOCK_STREAM表示创建的套接字是流套接字,而取SOCK_DGRAM创建数字报套接字。参数protocol用于指定网络协议,一般取0,表示默认为TCP/IP协议。若套接字创建成功则该函数返回创建的套接字句柄SOCKET,否则产生INVALID_SOCKET错误。
2、将本地地址绑定到所创建的套接字上以在网络上表姐该套接字。这个过程通过调用bind函数来完成。
int bind( SOCKET s, const struct sockaddr* name, int namlen );
其中,第一个参数s标识一个未捆绑套接字的句柄,它用于等待客户机的连接。参数name是赋予套接字的地址,它由struct sockaddr结构表示。
3、将套接字置入监听模式并准备接受连接请求。
监听函数如下:int listen ( SOCKET s, int backlog );
其中,参数s同上,backlog参数用于指定正在等待连接的最大队列长度。如无错误发生,listen函数返回0,失败则返回SOCKET_ERROR错误。
进入监听状态后,通过调用accept函数使套接字做好接受客户连接的准备。accept函数如下:
SOCKET accept( SOCKET s, struct sockaddr* addr, int* addrlen );
其中,参数s同上,addr是一个有效的SOCKADDR_IN结构的地址,而addrlen是sockaddr_in结果的长度。accept函数返回后,addr参数变量中会包含发出连接请求的那个客户机的IP地址信息,而addrlen参数则指出该结构的长度,并返回一个新的套接字描述符,它对应于已经接受的那个客户机连接。
4、客户进程调用socket函数创建客户端套接字。
5、客户向服务进程发出连接请求。通过调用connect函数可以建立一个端的连接。
int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );
s同上,name是针对TCP的套接字地址结构,它标识服务进程IP地址信息,namelen则用来标识name参数的长度。
6、当连接请求到来后,被阻塞服务进程的accpet函数如3中所述生成一个新的套接字与客户套接字建立连接,并向客户返回接受信号。
7、一旦客户机的套接字接受到来自服务器的接受信号,则表示客户机与服务器已实现连接,则可以进行数据传输了。send函数原型如下:
int send( SOCKET s, const char* buf, int len, int flags );
s是已建立连接的套接字描述字,参数buf是字符缓冲区,包含即将发送的数据,参数len用于指定即将发送的缓冲区内的字符数。flags可取的值有:0、MSG_DONTROUTE或MSG_OOB或这些标志的按位或运算。
recv函数的原型为:
int recv( SOCKET s, char* buf, int len, int flags );
s是准备接收数据的套接字,buf是即将收到数据的字符缓冲区,而len则是准备接受的字节数或buf缓冲的长度。flags参数可以是0、MSG_PEEK或MSG_OOB或这些标志的按位“或”运算。
8、关闭套接字。一旦任务完成,就必须关掉连接以释放套接字占用的所有资源。shutdown()函数的原型如下:
int shutdown( SOCKET s, int how );
其中,how参数用于描述禁止哪些操作,它可取的值有:SD_RECEIVE、SD_SEND或SD_BOTH。如果是sd_receive,就表示不允许再调用接收函数,如果选择SD_SEND,表示不允许再调用发送函数,sd_both,则表示取消连接两端的收发操作。如果没有错误发生,shutdown()返回0,否则返回socket_error错误。
closesocket()函数的原型为:
int closesocket(SOCKET s );s是要关闭的套接字描述字,再利用套接字执行调用就会失败。