socket编程概要

本文主要记录socket学习概要,主要分析了非阻塞编程

字节序

        大端:高尾端 0x01020304  内存由低到高:01 02 03 04

        小端:低尾端 0x01020304  内存由低到高:04 03 02 01

        优劣势:大端首位可以快速判断正负;小端强制类型转换方便,不用调整内存数据。

        网络字节序:使用大端传输,数据顺序传输,每位数据都当独立数据传输。

转化函数

        1)字节序转换

        uint32_t htonl(uint32_t hostlong);

        uint16_t htons(uint16_t hostshort);

        uint32_t ntohl(uint32_t netlong);

        uint16_t ntohs(uint16_t netshort);

       2) IP转换

        int inet_pton(int af, const char *src, void *dst);  “10.0.0.101” --> 123424

        const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

        这两种支持IPV6

        in_addr_t inet_addr(const char *cp);

        int inet_aton(const char *cp, struct in_addr *inp);

        char *inet_ntoa(struct in_addr in);

        只支持IPV4,并且inet_ntoa返回值保存为静态变量里,连续使用会覆盖前面的值。

套接字选项获取和设置

        int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

        int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

        1) 字段说明

        sockfd: 套接字描述符

        level: 选项协议层。通用套接字 SOL_SOCKET,

                其他协议层,IPPROTO_IP,IPPROTO_TCP和IPPROTO_IPV6。

        optname: 访问/设置类型名称。

        optval: 数据缓存区,存放返回数据,或者要设置的数据。根据不同类型有不同数据结构。

        optlen: 数据缓存区大小。

        2) 返回说明

        成功:return 0

        失败:return -1,并设置errno

        EBADF:sockfd不是有效的文件描述符

        EFAULT:optval指向内存并非有效的进程空间

        EINVAL:在调用setsockopt时,optlen无效

        ENOPROTOOPT:level指定协议层不能识别

        ENOSOCK:sockfd不是套接字描述符

        3) 重用sockfd

        在服务器关闭后,已经和客户端连接的socket,不会立刻关闭,会出现TIME_WAIT一段时间,此时重启服务器会bind失败。

        解决此问题用optname=SO_REUSEADDR, optval = 1(int)

        int optval = 1;

        setsockopt(sockfd, SOL_SOCKET, SO_REUSERADDR, &optval, sizeof(optval));

        3) 禁止TIME_WAIT阶段

        int optval = 0;

        setsockopt(sockfd, SOL_SOCKET, SO_DONTLINGER, &optval, sizeof(optval));

        5)发送超时

        windows: int outtime = 1000; //1秒

        linux: struct timeval outtime = {1, 0};

        setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &outtime, sizeof(outtime));

        6)接收超时

        windows: int outtime = 1000; //1秒

        linux: struct timeval outtime = {1, 0};

        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &outtime, sizeof(outtime));

        7)收发缓冲器设置

        int membuf = 32 * 1024; //32K

        setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &membuf, sizeof(membuf));

        setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &membuf, sizeof(membuf));

        8)设置广播属性

        int optval = 1;

        setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));

        9)设置关闭时等待发送数据发送完毕

        struct linger {

                u_short l_onoff;  // 1 - 允许发送完毕后在关闭(等待时间为0时,立即关闭即使还有数据未发送),0 - 立即关闭(未发送的数据发送后关闭)

                u_short l_linger;  //允许等待时间(秒)

        };

        struct linger m_slinger;

        m_slinger.l_onoff = 1;

        m_slinger.l_linger = 5;

        setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &m_slinger, sizeof(m_slinger));

非阻塞编程

        1)fcntl函数

        int fcntl(int fd, int cmd, ... /* arg */ );

        fd: 套接字描述符

        cmd: F_SETFL/F_GETFL 设置或获取文件属性

        arg: O_NONBLOCK|O_NDELAY

        O_NDELAY:早起版本,有bug,在没有数据和文件末尾,读取都返回0.

        2)阻塞和非阻塞设置

        int flags;

        flags = fcntl(fd, F_GETFL, 0);

        flags |= O_NONBLOCK;

        fcntl(fd, F_SETFL, flags);

        flags &= ~O_NONBLOCK;

        fcntl(fd, F_SETFL, flags);

        socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);

        3)非阻塞模式下 accept

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

        没有新连接时,

        return: -1

        errno: EWOULDBLOCK(11)

        4)非阻塞模式下connect

        int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

        连接成功:

                return 0;

        可能成功:

                return -1;

                errno: EINPROGRESS

        连接失败:

                return -1;

                errno: !=EINPROGRESS

        int my_connect(int sockfd, struct sockaddr *serveraddr)

        {

                int ret = connect(sockfd, sereveraddr, sizeof(serveraddr));

                if (ret == 0)

                        return 0;

                if (errno != EINPROGRESS)

                        return -1;

                fd_set wset;

                FD_ZERO(&wset);

                FD_SET(sockfd, &wset);

                struct timeval timeout;

                timeout.tv_sec = 3;

                timeout.tv_usec = 0;

                //超时、出错、连接出错

                if (select(sockfd + 1, NULL, &wset, NULL, &timeout) == -1)

                        return -1;

                if (!FD_ISSET(sockfd, &wset))  //不可写

                        return -1;

                int error;

                socklen_t len = sizeof(error);

                 //调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0

                if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)

                        return -1;

                if (error != 0)

                        return -1;

                return 0;

        }

        5)非阻塞模式下write

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

        ssize_t write(int fildes, const void *buf, size_t nbyte);

        返回值:

                return == 0; socket关闭,发送失败

                return > 0; 发送成功

                return < 0; 发送失败。EWOULDBLOCK|EAGAIN没有空间写数据,EINTR连接正常,操作中断可继续发送数据。

        int writeNonBlock(int sockfd, char *send_buf, size_t send_len)

        {

                int write_pos = 0;

                int nLeft = send_len;

                while (nLeft > 0) {

                        int nWrite = write(sockfd, send_buf + write_pos, nLeft);

                        if (nWrite == 0)

                                return -1;

                        if (nWrite < 0) {

                                if (errno == EINTR) {

                                        //可使用select检查是否可写

                                        continue;

                                } else if (errno == EWOULDBLOCK || errno == EAGAIN) {

                                        //nWrite = 0;

                                        continue;

                                } else {

                                        return -1;

                                }

                        } else {

                                nLeft -= nWrite;

                                write_pos += nWrite;

                        }

                }

                return 0;

      }

        6)非阻塞模式下read

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

        ssize_t read(int fildes, void *buf, size_t nbyte);

        返回值:

                return == 0; socket关闭,发送失败

                return > 0; 发送成功

                return < 0; 发送失败。EINTR表示操作被中断,可以继续读取;EWOULDBLOCK|EAGAIN,表示本来应该阻塞的立即返回,可以下次继续读取数据

        int readNonBlock(int sockfd, char *buf, size_t buf_len)

        {

                int nread = 0;

                while (1) {

                        nread = read(sockfd, buf, buf_len);

                        if (nread > 0)

                                return 0;

                        if (nread == 0)

                                return -1;

                        if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {

                                //检测是否可读

                                continue;

                        }

                        return -1;

                }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值