socket------套接字

socket的基本概念

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

连接过程

根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认

(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。

(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别

至此,我们对Socket进行了直观的描述:抽象出来,Socket实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。


常用函数

创建

函数原型:

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

参数说明:

    domain:协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

    type:指定Socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。

    protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意:

    1.type和protocol不可以随意组合,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。

    2.WindowsSocket下protocol参数中不存在IPPROTO_STCP

返回值:

    如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET(Linux下失败返回-1)。

    套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

绑定

函数原型:

        int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);

参数说明:

        socket:是一个套接字描述符。

                       它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

        address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。

                        一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。

                        这个地址结构根据地址创建socket时的地址协议族的不同而不同,

                        如ipv4对应的是

                        struct sockaddr_in  {

                                sa_family_t    sin_family; /* address family: AF_INET */

                                in_port_t      sin_port;  /* port in network byte order */

                                struct in_addr sin_addr;  /* internet address */

                        };

                        /* Internet address. */

                        struct in_addr  {

                                uint32_t      s_addr;    /* address in network byte order */

                        };

                ipv6对应的是:

                        struct sockaddr_in6  {

                                sa_family_t    sin6_family;  /* AF_INET6 */

                                in_port_t      sin6_port;    /* port number */

                                uint32_t        sin6_flowinfo; /* IPv6 flow information */

                                struct in6_addr sin6_addr;    /* IPv6 address */

                                uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */

                };

                struct in6_addr {

                        unsigned char  s6_addr[16];  /* IPv6 address */

                };

                Unix域对应的是:

                #define UNIX_PATH_MAX    108

                struct sockaddr_un {

                        sa_family_t sun_family;              /* AF_UNIX */

                        char        sun_path[UNIX_PATH_MAX];  /* pathname */

                };

        address_len:确定address缓冲区的长度,对应的是地址的长度。。

返回值:

    如果函数执行成功,返回值为0,否则为SOCKET_ERROR。


接收

函数原型:

        int recv(SOCKET socket, char FAR* buf, int len, int flags);

参数说明:

        socket:一个标识已连接套接口的描述字。

        buf:用于接收数据的缓冲区。

        len:缓冲区长度。

        flags:指定调用方式。取值如下:

                  MSG_PEEK 查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除;

                  MSG_OOB 处理带外数据。

返回值:

        若无错误发生,recv()返回读入的字节数。

        如果连接已中止,返回0。

        否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。


函数原型:

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

参数说明:

        sockfd:标识一个已连接套接口的描述字。

         buf:接收数据缓冲区。

         len:缓冲区长度。

         flags:调用操作方式。是以下一个或者多个标志的组合体,可通过or操作连在一起:

            (1)MSG_DONTWAIT:操作不会被阻塞;

            (2)MSG_ERRQUEUE: 指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用。

            (3)MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。

            (4)MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。

            (5)MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。

            (6)MSG_EOR:指示记录的结束,返回的数据完成一个记录。

            (7)MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。

             /*(MSG_TRUNC使用错误,4才是MSG_TRUNC的正确解释)*/

            (8)MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。

            (9)MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。

            (10)MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。

        from:(可选)指针,指向装有源地址的缓冲区。

        fromlen:(可选)指针,指向from缓冲区长度值。

发送

函数原型:

        int sendto( SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen);

参数说明:

        s:套接字

        buf:待发送数据的缓冲区

        size:缓冲区长度

        flags:调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式

        addr:(可选)指针,指向目的套接字的地址

        tolen:addr所指地址的长度

返回值:

        如果成功,则返回发送的字节数

        失败则返回SOCKET_ERROR。


接收连接请求

函数原型:

        int accept( int fd, struct socketaddr* addr, socklen_t* len);

参数说明:

        fd:套接字描述符。

        addr:返回连接着的地址

        len:接收返回地址的缓冲区长度

返回值:

        成功返回客户端的文件描述符

        失败返回-1。


read()、write()等函数

万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

        read()/write()

        recv()/send()

        readv()/writev()

        recvmsg()/sendmsg()

        recvfrom()/sendto()

它们的声明如下:

        ssize_t read(int fd, void *buf, size_t count);

        ssize_t write(int fd, const void *buf, size_t count);#include

        ssize_t send(int sockfd, const void *buf, size_t len, int flags);

        ssize_t recv(int sockfd, 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);

        ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

        struct sockaddr *src_addr, socklen_t *addrlen);

        ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

        ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。

write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。

close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

int close(int fd);

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

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

推荐阅读更多精彩内容

  • 一 、Socket 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个 Socket。S...
    空白Null阅读 1,668评论 1 9
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,117评论 0 5
  • 关于iOS socket都在这里了 Socket概念理解 Socket是一种进程通信机制,凭借这种机制,客户/服务...
    水止云起阅读 7,278评论 0 13
  • 短短杭城一年里,不知觉间,记忆太多了,不知道是好事还是坏事? 在两难中,渡过了去年的这三百多日,遇到了觉得可以珍惜...
    诗意贝蜀黍阅读 191评论 0 3
  • 2017年新春 恒哥一岁八个月 1、过年期间祝福多,奶奶手机响不停,恒哥在边上一边玩一边说:娘娘,叮咚叮咚...
    欢子fly_楷妈养生合伙人阅读 145评论 0 1