Unix Socket - 核心函数

本章描述了编写完整的TCP客户端和服务器所需的核心套接字函数。

下图显示了完整的客户端和服务器交互

The socket Function

要执行网络I/O,进程必须做的第一件事是,调用socket函数,指定所需的通信协议类型和协议族,等等。

#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

这个调用返回一个套接字描述符,您可以在以后的系统调用中使用该描述符,或者在出错时返回-1。

Parameters

family − 它指定协议族,是如下所示的常量之一

AF_INET:IPv4 protocols
AF_INET6: IPv6 protocols
AF_LOCAL: Unix domain protocols
AF_ROUTE: Routing Sockets
AF_KEY: Ket socket

本章不涉及除IPv4以外的其他协议。

type - 它指定您想要的套接字类型。它可以取以下值之一

SOCK_STREAM: Stream socket
SOCK_DGRAM: Datagram socket
SOCK_SEQPACKET: Sequenced packet socket
SOCK_RAW: Raw socket

protocol - 参数应该设置为下面给出的特定协议类型,或者0来为给定的family和type组合选择系统的默认值

IPPROTO_TCP: TCP transport protocol
IPPROTO_UDP: UDP transport protocol
IPPROTO_SCTP: SCTP transport protocol

The connect Function

连接功能用于TCP客户端与TCP服务器建立连接

#include <sys/types.h>
#include <sys/socket.h>

 int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

如果成功连接到服务器,则返回0,否则返回-1。

Parameters

sockfd − 它是套接字函数返回的套接字描述符。

serv_addr − 它是一个指向struct sockaddr的指针,其中包含了目的IP地址和端口。

addrlen − 设置为sizeof(struct sockaddr)。

The bind Function

bind函数为套接字分配一个本地协议地址。在互联网协议中,协议地址是32位IPv4地址或128位IPv6地址,以及16位TCP或UDP端口号的组合。此函数仅由TCP服务器调用。

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr,int addrlen);

如果成功绑定到地址,则返回0,否则返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    my_addr − 它是一个指向struct sockaddr的指针,其中包含本地IP地址和端口。

    addrlen − 设置大小为sizeof(struct sockaddr).

您可以自动输入您的IP地址和端口

端口号的值为0表示系统将随机选择一个端口,IP地址的值为INADDR_ANY表示服务器的IP地址将自动分配。

server.sin_port = 0;
server.sin_addr.s_addr = INADDR_ANY;

注意:所有1024以下的端口都保留。您可以将端口设置为1024以上,65535以下,除非它们被其他程序使用。

The listen Function

listen函数只由TCP服务器调用,它执行两个操作 −

    - listen函数将一个未连接的套接字转换为被动套接字,表明内核应该接受指向该套接字的传入连接请求。

    - 这个函数的第二个参数指定内核应该为这个套接字队列的最大连接数。

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd,int backlog);

如果调用成功,则返回0,否则返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    backlog − 它是允许的连接数。

The accept Function

TCP服务器调用accept函数,从已完成的连接队列的前端返回下一个已完成的连接。呼叫的签名如下 −

#include <sys/types.h>
#include <sys/socket.h>

int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

如果调用成功,则返回一个非负描述符,否则返回-1。返回的描述符被假定为客户端套接字描述符,所有的读写操作都将在这个描述符上完成,以与客户端通信。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    cliaddr −它是一个指向sockaddr结构体的指针,其中包含客户端IP地址和端口。

    addrlen − 设置大小为sizeof(struct sockaddr)。

The send Function

send函数用于通过流套接字或连接的数据报套接字发送数据。如果要在未连接的数据报套接字上发送数据,必须使用sendto()函数。

您可以使用write()系统调用来发送数据。如下

int send(int sockfd, const void *msg, int len, int flags);

这个调用返回发送的字节数,否则它将在出错时返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    msg − 它是一个指针,指向您想要发送的数据。

    len − 它是您想要发送的数据长度(以字节为单位)。

    flags −它被设置为0。

The recv Function

recv函数用于通过流套接字或连接的数据报套接字接收数据。如果想通过未连接的数据报套接字接收数据,必须使用recvfrom()。

可以使用read()系统调用读取数据。这个调用在帮助函数一章中有解释。

int recv(int sockfd, void *buf, int len, unsigned int flags);

这个调用返回读入缓冲区的字节数,否则它将在出错时返回-1。

Parameters

    sockfd −它是套接字函数返回的套接字描述符。

    buf − 它是读取信息的缓冲区。

    len − 它是缓冲区的最大长度。

    flags − 它被设置为0。

The sendto Function

sendto函数用于在未连接的数据报套接字上发送数据。签名如下

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

这个调用返回发送的字节数,否则它返回-1错误。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    msg − 它是一个指针,指向您想要发送的数据。

    len − 它是您想要发送的数据长度(以字节为单位)。

    flags − 它被设置为0。

    to − 它是一个指向struct sockaddr的指针,用于发送数据的主机。

    tolen − 它设置为sizeof(struct sockaddr)。

The recvfrom Function

recvfrom函数用于从未连接的数据报套接字接收数据。

int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);

这个调用返回读入缓冲区的字节数,否则返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    buf − 它是读取信息的缓冲区。

    len − 它是缓冲区的最大长度。

    flags − 它被设置为0。

    from − 它是一个指向struct sockaddr的指针,用于读取数据的主机。

    fromlen − 它设置为sizeof(struct sockaddr)。

The close Function

关闭功能用于关闭客户端和服务器之间的通信。其语法为

int close( int sockfd );

如果调用成功,则返回0,否则返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

The shutdown Function

shutdown功能用于优雅地关闭客户端和服务器之间的通信。与close函数相比,这个函数提供了更多的控制。下面给出了shutdown的语法

int shutdown(int sockfd, int how);

如果调用成功,则返回0,否则返回-1。

Parameters

    sockfd − 它是套接字函数返回的套接字描述符。

    how − 下面其中一个

        0 − 表示不允许接收

        1 − 表示不允许发送

        2 − 表示发送和接收都不允许。当how设置为2时,它和close()是一样的。

The select Function

select函数指示指定的文件描述符中哪些可以读,哪些可以写,或者有一个等待的错误条件。

当应用程序调用recv或recvfrom时,它将被阻塞,直到该套接字的数据到达。当传入的数据流为空时,应用程序可以进行其他有用的处理。另一种情况是应用程序从多个套接字接收数据。

在输入队列中没有数据的套接字上调用recv或recvfrom会阻止从其他套接字上立即接收数据。select函数调用通过允许程序轮询所有套接字句柄来解决这个问题,以查看它们是否可用于非阻塞的读写操作。

下面给出select的语法 

int select(int  nfds, fd_set  *readfds, fd_set  *writefds, fd_set *errorfds, struct timeval *timeout);

如果调用成功,则返回0,否则返回-1。

Parameters

    nfds −它指定要测试的文件描述符的范围。select()函数测试范围为0到nfds-1的文件描述符

    readfds − 它指向一个fd_set类型的对象,该对象在输入时指定要检查的文件描述符是否准备读取,在输出时指定哪些文件描述符准备读取。它可以为NULL,表示为空集。

    writefds − 它指向一个fd_set类型的对象,该对象在输入时指定要检查的文件描述符是否准备写入,在输出时指定哪些文件描述符准备写入。它可以为NULL,表示为空集。

    exceptfds − 它指向一个fd_set类型的对象,该对象在输入时指定要检查的文件描述符是否有错误条件等待处理,在输出时指定哪些文件描述符有错误条件等待处理。它可以为NULL,表示为空集。

    timeout − 它指向一个timeval结构体,该结构体指定select调用应该为一个可用的I/O操作轮询描述符多长时间。如果超时值为0,则select将立即返回。如果timeout参数为NULL,则select将阻塞,直到至少有一个文件/套接字句柄为可用的I/O操作准备好为止。否则,select将在超时时间已经过去或至少有一个文件/套接字描述符准备好进行I/O操作之后返回。

select的返回值是文件描述符集中指定的准备好进行I/O的句柄数。如果达到timeout字段指定的时间限制,则选择返回0。以下宏用于操作文件描述符集 

    FD_CLR(fd, &fdset) − 清除文件描述符集fdset中文件描述符fd的位。

    FD_ISSET(fd, &fdset) − 如果文件描述符fd的位设置在fdset所指向的文件描述符集合中,则返回一个非零值,否则返回0。

    FD_SET(fd, &fdset) − 在文件描述符集合fdset中设置文件描述符fd的位。

    FD_ZERO(&fdset) − 初始化文件描述符集fdset,使所有文件描述符的位都为零。

如果fd参数小于0或大于等于FD_SETSIZE,这些宏的行为是未定义的。

Example

fd_set fds;

struct timeval tv;

/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;

/* tv now represents 1.5 seconds */
FD_ZERO(&fds);

/* adds sock to the file descriptor set */
FD_SET(sock, &fds);

/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);

if (FD_ISSET(sock, &fds)) { 
     recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len); 
     /* do something */
}else { 
     /* do something else *0/
}

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

推荐阅读更多精彩内容