Socket编程(3)-Socket API

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:获取套接字选项参数(查手册即可)
Socket API调用基本流程
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容