Socket编程(3)-Socket API
2019-01-16 本文已影响0人
Veahow
1 Socket API函数
WinSock的实现方式是利用Windows的动态链接库实现的,所以如果在Windows环境下进行socket编程,需要事先初始化Windows Sockets API,可以通过调用WSAStartup()完成。在使用结束后释放所使用的Windows Sockets DLL,可以通过调用WSACleanup()来完成。这里的WSA指的是Windows Socket Asynchronous,即Windows异步套接字。
在Linux下可使用man命令查阅相关API函数的详细信息。
1.1 WSAStartup
// #include <WinSock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
- Windows特有,使用Socket的应用程序在使用Socket之前必须先调用WSAStartup函数。
- 两个参数:
-
WORD wVersionRequested
:指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本。十六进制整数,例如0x102表示2.1版本。 -
LPWSADATA lpWSAData
:返回实际的WinSock的版本信息,为指向WSADATA结构的指针。
-
例:使用2.2版本的WinSock的程序代码段
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
1.2 WSACleanup
// #include <WinSock2.h>
int WSACleanup(void);
- Windows特有,应用程序在完成对请求的Socket库的使用,最后要调用WSACleanup函数,可以解除与Socket库的绑定和释放Socket库所占用的系统资源。
1.3 socket
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
int socket(int domain, int type, int protocol);
- 创建套接字时使用,操作系统会返回套接字描述符。最初是面向TCP/IP协议栈的,但最终设计成可以面向其他协议簇。
- 三个参数:
-
int domain
:指定协议簇,主要用TCP/IP协议,因此可指定为PF_INET符号常量,这里AF_INET和PF_INET是一样的,这是历史遗留问题造成的。协议簇主要有AF_INET(IPv4)、AF_INET6(IPv6)、AF_LOCAL(UNIX协议) AF_ROUTE(路由套接字)、AF_KEY(秘钥套接字)。 -
int type
:指定套接字类型,主要有面向TCP的套接字SOCK_STREAM,又称流式套接字;面向UDP的套接字SOCK_DGRAM,又称数据报套接字。TCP:可靠、面向连接、字节流传输、点对点;UDP:不可靠、无连接、数据报传输。 -
int protocol
:指定协议号,0为默认。需要使用别的协议才指定,如果套接字类型不是原始套接字,那么这个参数就为0。
-
- 特殊情况:创建原始套接字SOCK_RAW(操作系统对这类套接字的创建需要有特殊的权限,Windows需要管理员权限,Linux需要root权限),可跨越传输层面向网络层。
例:创建一个面向TCP/IP的流式套接字代码段
struct protoent *p;
p = getprotobyname("tcp");
SOCKET sd = socket(PF_INET, SOCK_STREAM, p->p_proto);
五层模型 | 主要协议 |
---|---|
应用层 | |
传输层 | TCP/UDP |
网络层 | IP/ICMP/IGMP |
数据链路层 | |
物理层 |
1.4 closesocket/close
int closesocket(SOCKET sd); // Windows:#include <WinSock2.h>
int close(int fd); // Linux:#include <unistd.h>
- 关闭一个描述符为sd/fd的套接字。
- 如果多个进程共享一个套接字,调用函数将套接字引用计数减1,减至0才关闭。
- 一个进程中的多线程对一个套接字的使用无计数。如果进程中的一个线程调用函数将一个套接字关闭,该进程中的其他线程也将不能访问该套接字。
- 返回值:0-成功,SOCKET_ERROR(-1)-失败。
1.5 bind
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 绑定套接字的本地端点地址:IP地址+Port端口号。
- 三个参数:
-
int sockfd
:socket函数返回的套接字描述符。 -
const struct sockaddr *addr
:结构为sockaddr_in的端点地址,指向本地IP地址的结构体指针。 -
socklen_t addrlen
:结构长度。
-
- 客户程序一般不必显式调用bind函数,操作系统会帮忙设置。
- 服务器需要调用绑定熟知的端口号,例如80端口。
- IP地址在服务器使用时不应该指定具体的IP地址,因为可能有多个网卡接收数据,较难确认,只需将IP地址赋值为INADDR_ANY地址通配符,即可监听所有地址接收的数据。
struct sockaddr{
unsigned short sa_family; //通信协议类型族AF_xx
char sa_data[14]; //14字节协议地址,包含该socket的IP地址和端口号
};
1.6 listen
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
int listen(int sockfd, int backlog);
- 监听状态函数,置服务器端的流套接字处于监听状态,仅服务器端调用,并且仅用于面向连接的流式套接字。
- 两个参数:
-
int sockfd
:服务器端的流套接字,仅用于面向连接的流式套接字。 -
int backlog
:设置连接请求队列大小,即缓存队列,可以设置连接客户端的最大连接个数,当有多个客户端向服务器请求时,会受到此值的影响,默认值为20。
-
- 返回值:0-成功,SOCKET_ERROR(-1)-失败。
1.7 connect
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
int connect(int sockfd,const struct sockaddr *addr, socklen_t addrlen)
- 客户端程序调用connect函数来使客户套接字与特定计算机的特定端口的套接字连接。
- 仅用于客户端, 可用于TCP客户端,也可用于UDP客户端。
- TCP客户端:建立TCP连接。
- UDP客户端:UDP是无连接的,功能只是指定服务器端点地址,实际是没有连接的,但看着像连接一样。
- 三个参数:
-
int sockfd
:客户端套接字。 -
const struct sockaddr *addr
:服务端套接字地址。 -
socklen_t addrlen
:服务端套接字长度。
-
1.8 accept
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)
- 服务器端程序调用accept函数,从处于监听状态的流套接字的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新得套接字来与客户端套接字创建连接通道。
- 仅用于TCP套接字,仅用于服务器。
- 三个参数:
-
int sockfd
:服务端套接字。 -
const struct sockaddr *addr
:客户端套接字地址。 -
socklen_t *addrlen
:客户端套接字长度地址。
-
- 如果使用主套接字与客户端通信,因为TCP是点对点连接,那么在某一时刻服务器就只能为一个客户端提供服务,而不能够实现并发的TCP服务器。因此创建新套接字与客户端通信,服务器通过多线程、多进程创建新套接字,即可实现并发的TCP服务器。
1.9 send, sendto
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
- send函数用于TCP套接字(客户端与服务器端)或调用了connect函数的UDP客户端套接字。
- sendto函数用于UDP服务器端套接字与未调用connect函数的UDP客户端套接字。
1.10 recv, recvfrom
// Windows:#include <WinSock2.h>
// Linux:#include <sys/types.h> <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
- recv函数从TCP连接的另一端接收数据,或者从调用了connect函数的UDP客户端套接字接收服务器发来的数据。
- recvfrom用于从UDP服务器端套接字与未调用用connect函数的UDP客户端套接字接收对端数据。
1.11 setsockopt, getsockopt
int setsockopt(int sd, int level, int optname, *optval, socklen_t optlen);
int getsockopt(int sd, int level, int optname, *optval, socklen_t *optlen);
- setsockopt()函数用来设置套接字sd的选项参数。
- getsockopt()函数用于获取任意类型、任意状态套接口的选项当前值,并把结果存入optval。
2 Socket API函数小结
- WSAStartup:初始化socket库(仅对于WinSock)
-
WSACleanup:清除/终止socket库的使用(仅对于WinSock)
其他函数一般来说可以在Linux下使用,名称基本一致 - socket:创建套接字
- connect:“连接”远端服务器(仅用于客户端,注意TCP与UDP的区别)
- closesocket/close:释放/关闭套接字
- bind:绑定套接字的本地IP地址和端口号(通常客户端不需要,客户端的端点地址- 操作系统会帮忙设置,不需要自己设置)
- listen:置服务器端TCP套接字为监听模式,并设置队列大小(仅用于服务器端TCP套接字)
- accept:接收/提取一个连接请求,创建新套接字,通过新套接字与客户端进行连接(仅用于服务器端的TCP套接字,也称为阻塞函数)
- recv:接收数据(用于TCP套接字或连接模式的客户端UDP套接字)
- recvfrom:接收数据报(用于非连接模式的UDP套接字)
- send:发送数据(用于TCP套接字或连接模式的客户端UDP套接字)
- sendto:发送数据报(用于非连接模式的UDP套接字)
- setsockopt:设置套接字选项参数(查手册即可)
- getsockopt:获取套接字选项参数(查手册即可)