Socket API概述
- 标识通信端点(对外):IP地址+端口号
- 操作系统/进程如何管理套接字(对内):套接字描述符(创建了一个套接字,怎么找到并调用它,内部通过套接字描述符,外部通过IP地址+端口号,也就是通过IP地址和端口号即可定位外部的一个套接字)
Socket抽象
- 类似于文件的抽象
- 当应用进程创建套接字时,操作系统分配一个数据结构存储该套接字相关信息,返回套接字描述符
- 地址结构
struct sockaddr_in
{
u_char sin_len;
u_char sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}
Socket API函数
WSAStartup函数
- 使用Socket的应用程序在使用Socket之前必须首先调用WSAStartup函数
- 两个参数:
• 第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本,十六进制整数,例如0x102表示2.1版
• 第二个参数返回实际的WinSock的版本信息,指向WSADATA结构的指针
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
WSACleanup函数
- 应用程序在完成对请求的Socket库的使用, 最后要调用WSACleanup函数
- 解除与Socket库的绑定
- 释放Socket库所占用的系统资源
int WSACleanup (void);
Socket函数
- 创建套接字
- 操作系统返回套接字描述符(sd)
- 第一个参数(协议族): protofamily = PF_INET(TCP/IP)
- 第二个参数(套接字类型):
如下图所示,type参数有三种选择,SOCK_STREAM对应TCP类型,SOCKET_DGRAM对应UDP类型,SOCK_RAW直接对应网络层,当使用此方式时要求权限非常高,如果是Linux系统,则要求有root权限
- 第三个参数(协议号):0为默认
sd = socket(protofamily, type, proto);
struct protoent *p;
p = getprotobyname("tcp");
SOCKET sd = socket(PF_INET, SOCK_STREAM, p->p_proto);
Closesocket函数
- 关闭一个描述符为sd的套接字
- 如果多个进程共享一个套接字,调用closesocket将套接字引用计数减1,减至0才关闭
- 一个进程中的多线程对一个套接字的使用无计数
- 如果进程中的一个线程调用closesocket将一个套接字关闭,该进程中的其他线程也将不能访问该套接字
- 返回值:
• 0:成功
• SOCKET_ERROR:失败
int closesocket(SOCKET sd);
bind函数
- 绑定套接字的本地端点地址:IP地址+端口号
- 参数:
• 套接字描述符(sd)
• 端点地址(localaddr):结构sockaddr_in
• 地址结构的长度
- 客户程序一般不必调用bind函数
- 当遇见如下图所示情形,即一个主机/服务器连接了两个网络,也就有了两个IP地址,此时采用地址通配符的方式,地址通配符:INADDR_ANY
int bind(sd, localaddr, addrlen);
listen函数
- 置服务器端的流套接字处于监听状态(监听状态是指网络服务端程序所处的一种状态,在该状态下,服务端程序等待客户端的请求)
• 仅服务器端调用
• 仅用于面向连接的流套接字
- 设置连接请求队列大小(queuesize)(客户端请求队列,当有来自客户端的请求时,放入队列,但不响应请求,响应请求时另一个函数)
- 返回值:
• 0:成功
• SOCKET_ERROR:失败
int listen(sd, queuesize);
connect函数
- 客户程序调用connect函数来使客户套接字(sd)与特定计算机的特定端口(saddr)的套接字(服务)进行连接(用于TCP建立连接)
- 用于客户端
- 可用于TCP客户端也可以用于UDP客户端
• TCP客户端:建立TCP连接
• UDP客户端:指定服务器端点地址(UDP是一种无连接的方式,所以这个函数并不能有效建立UDP的连接)
accept函数
- 服务程序调用accept函数从处于监听状态的流套接字sd的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道
• 仅用于TCP套接字
• 仅用于服务器
- 利用新创建的套接字(newsock)与客户通信
newsock = accept(sd, caddr, caddrlen);
send, sendto函数
- send函数用于TCP套接字(客户与服务器)或调用了connect函数的UDP客户端套接字(send函数用于发送数据,但函数并未指定发送的端口地址,这是因为TCP已经建立了连接,不需要指定,而调用了connect函数的UDP客户端,由于指定了端口,所以函数也不需要指定发送端口)
- sendto函数用于UDP服务器端套接字与未调用connect函数的UDP客户端套接字
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
recv, recvfrom函数
- recv函数从TCP连接的另一端接收数据,或者从调用了connect函数的UDP客户端套接字接收服务器发来的数据(receive函数用于接受数据,但函数并未指定发送的端口地址,这是因为TCP已经建立了连接,不需要指定,而调用了connect函数的UDP客户端,由于指定了端口,所以函数也不需要指定发送端口)
- recvfrom函数用于从UDP服务器端套接字与未调用connect函数的UDP客户端套接字接收对端数据
recv(sd,*buffer,len,flags);
recvfrom(sd,*buf,len,flags,senderaddr