Socket 相关函数

TCP与UDP的区别
    1. 基于连接与无连接
    2. 对系统资源的要求(TCP较多,UDP少)
    3. UDP程序结构较简单
    4. 流模式与数据报模式
           TCP保证数据正确性,UDP可能丢包
           TCP保证数据顺序,UDP不保证

 

部分满足以下几点要求时,应该采用UDP 面向数据报方式
       1. 网络数据大多为短消息
       2. 拥有大量Client
       3. 对数据安全性无特殊要求
       4. 网络负担非常重,但对响应速度要求高
例子:ICQ、ping

如何能以访问文件流(stream I/O)的方式进行网络数据传输?
以文件流方式访问socket,必须分为读写两个stream,无法通过一个stream同时完成读写功能。
可以使用以下程序完成操作:(设sockfd是已连接的连接描述符)
    FILE *cin, *cout;
    cin = fdopen(sockfd, "r");
    setbuf(cin, (char *)0);
    cout = fdopen(sockfd, "w");
    setbuf(cout, (char *)0);
    则可以通过fgets, fread, fscanf等函数对文件流cin进行读操作(从socket中读)。
    可以通过fputs, fwrite, fprintf等函数对文件流cout进行写操作(写入socket)。
需要注意的是,最好在写操作之后加一句fflush(cout),使写入的数据尽快发送。
在断开socket连接前,需要先执行fclose(cin); fclose(cout); 再执行close(sockfd)

 

转换函数

IP地址转换函数:
    unsigned long inet_addr (const char *cp);
    inet_addr将一个点分十进制IP地址字符串转换成32位数字表示的IP地址(网络字节顺序)。
    char* inet_ntoa (struct in_addr in);
    inet_ntoa将一个32位数字表示的IP地址转换成点分十进制IP地址字符串。
    这两个函数互为反函数

字节顺序转换
    htons()--"Host to Network Short"
    htonl()--"Host to Network Long"
    ntohs()--"Network to Host Short"
    ntohl()--"Network to Host Long"

网络连接函数

初始化sock连接符:
int socket(int domain, int type, int protocol);
    函数返回socket描述符,返回-1表示出错
    domain参数只能取AF_INET, protocol参数一般取0
    应用示例:
        TCP方式:sockfd = socket(AF_INET,SOCK_STREAM,0);
        UDP方式:sockfd =socket(AF_INET, SOCK_DGRAM,0);

绑定端口:
int bind(int sockfd, struct sockaddr *sa, int addrlen);
    函数返回-1表示出错,最常见的错误是该端口已经被其他程序绑定。
    需要注意的一点:在Linux系统中,1024以下的端口只有拥有root权限的程序才能绑定。
     
连接网络(用于TCP方式):
int connect(int sockfd, struct sockaddr *servaddr, int addrlen);
    函数返回-1表示出错,可能是连接超时或无法访问。返回0表示连接成功,可以通过sockfd传输数据了。

监听端口(用于TCP方式):
int listen(int sockfd, int queue_length);
    需要在此前调用bind()函数将sockfd绑定到一个端口上,否则由系统指定一个随机的端口。
    接收队列:一个新的Client的连接请求先被放在接收队列中,直到Server程序调用accept函数接受连接请求。
    第二个参数queue_length,指的就是接收队列的长度 也就是在Server程序调用accept函数之前最大允许的连接请求数,多余的连接请求将被拒绝。
 
响应连接请求(用于TCP方式):
int accept(int sockfd,struct sockaddr *addr,int *addrlen);
    accept()函数将响应连接请求,建立连接并产生一个新的socket描述符来描述该连接,该连接用来与特定的Client交换信息。
    函数返回新的连接的socket描述符,错误返回-1
    addr将在函数调用后被填入连接对方的地址信息,如对方的IP、端口等。
    addrlen作为参数表示addr内存区的大小,在函数返回后将被填入返回的addr结构的大小。
    accept缺省是阻塞函数,阻塞直到有连接请求
    应用示例:
        struct sockaddr_in their_addr; /* 用于存储连接对方的地址信息*/
        int sin_size = sizeof(struct sockaddr_in);
        … …(依次调用socket(), bind(), listen()等函数)
        new_fd = accept(sockfd, &their_addr, &sin_size);
        printf(”对方地址: %s/n", inet_ntoa(their_addr.sin_addr));
        … …

关闭socket连接:
    int close(int sockfd);
        关闭连接将中断对该socket的读写操作。
        关闭用于listen()的socket描述符将禁止其他Client的连接请求。
         
部分关闭socket连接:
    int shutdown(int sockfd, int how);
        Shutdown()函数可以单方面的中断连接,即禁止某个方向的信息传递。
        参数how:
        0 - 禁止接收信息
        1 - 禁止发送信息
        2 - 接收和发送都被禁止,与close()函数效果相同

socket轮询选择:
    int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    应用于多路同步I/O模式(将在同步工作模式中详细讲解)
        FD_ZERO(*set) 清空socket集合
        FD_SET(s, *set) 将s加入socket集合
        FD_CLR(s, *set) 从socket集合去掉s
        FD_ISSET(s, *set) 判断s是否在socket集合中
    常数FD_SETSIZE:集合元素的最多个数
 
等待选择机制:
    int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
    是select机制的一个变种,应用于多路同步I/O模式(将在同步工作模式中详细讲解)
    ufds是pollfd结构的数组,数组元素个数为nfds。
         struct pollfd {
               int fd;           /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

接收/发送消息:
TCP方式:
    int send(int s, const void *buf, int len, int flags);
    int recv(int s, void *buf, int len, int flags);
        函数返回实际发送/接收的字节数,返回-1表示出错,需要关闭此连接。
        函数缺省是阻塞函数,直到发送/接收完毕或出错
        注意:如果send函数返回值与参数len不相等,则剩余的未发送信息需要再次发送
UDP方式:
    int sendto(int s, const void *buf, int len, int flags, const struct sockaddr *to, int tolen);
    int recvfrom(int s,void *buf, int len, int flags, struct sockaddr *from, int *fromlen);
        与TCP方式的区别:
            需要指定发送/接收数据的对方(第五个参数to/from)
        函数返回实际发送/接收的字节数,返回-1表示出错。
        函数缺省是阻塞函数,直到发送/接收完毕或出错
        注意:如果send函数返回值与参数len不相等,则剩余的未发送信息需要再次发送

基于消息的方式:
    int sendmsg(int s, const struct msghdr *msg, int flags);
    int recvmsg(int s, struct msghdr *msg, int flags);
        发送/接收一个消息,消息使用如下数据结构:
        struct msghdr {
            void * msg_name; /* optional address */
            socklen_t msg_namelen; /* size of address */
            struct iovec * msg_iov; /* scatter/gather array */
            size_t msg_iovlen; /* # elements in msg_iov */
            void * msg_control; /* ancillary data, see below */
            socklen_t msg_controllen; /* ancillary data buffer len */
            int msg_flags; /* flags on received message */
        };
        这种方式可以使用面向连接和无连接两种方式,灵活性较大,但不太常用,将在后面的程序示例(网络仿真设备)中解释工作流程。
标志位:
    上面这六个发送/接收函数均有一个参数flags,用来指明数据发送/接收的标志,常用的标志主要有:
        MSG_PEEK 对数据接收函数有效,表示读出网络数据后不清除已读的数据
        MSG_WAITALL 对数据接收函数有效,表示一直执行直到buf读满、socket出错或者程序收到信号。
        MSG_DONTWAIT 对数据发送函数有效,表示不阻塞等待数据发送完后返回,而是直接返回。(只对非阻塞socket有效)
        MSG_NOSIGNAL 对发送接收函数有效,表示在对方关闭连接后出错但不发送SIGPIPE信号给程序。
        MSG_OOB 对发送接收都有效,表示读/写带外数据(out-of-band data)

带外数据实例图


获取/设置socket的参数或信息


取得本地主机名:
    int gethostname(char *hostname, size_t size);
    获得主机名存到hostname中。
     
取得本地的信息:
    int getsockname(int sockfd, struct sockaddr *addr, int *addrlen);
    addr存有返回的主机信息。
    示例:
        struct sockaddr_in sa;
        int len = sizeof(sa);
        getpeername(sockfd, (struct sockaddr *)&sa, &len);
        printf("本地IP:%s", inet_ntoa(sa.sin_addr));         
 
取得对方主机的信息:
    int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
    addr存有返回的主机信息。
    示例:
        struct sockaddr_in sa;
        int len = sizeof(sa);
        getpeername(sockfd, (struct sockaddr *)&sa, &len);
        printf("对方IP:%s", inet_ntoa(sa.sin_addr));

获得DNS信息:
    struct hostent *gethostbyname(const char *name);
    struct hostent *gethostbyaddr(const char *addr, int len, int type);
    返回了一个指向struct hostent的指针,struct hostent定义如下:
        struct hostent {
            char *h_name; /* official name of host */
            char **h_aliases; /* alias list */
            int h_addrtype; /* host address type */
            int h_length; /* length of address */
            char **h_addr_list; /* list of addresses */
        };
        #define h_addr h_addr_list[0] /* for backward compatibility */
    对于如何获得DNS信息,将在后面的程序片断中详细讲解。
    DNS操作时的错误处理与普通程序不同,gethostbyname通过设置h_errno代表出错号,对应的错误函数有hstrerror()和herror(),分别对应于strerror()和perror()这

两个普通的错误函数。

获得或改变socket属性
    int getsockopt(int sockfd, int level, int name, char *value, int *optlen);
    int setsockopt(int sockfd, int level, int name, char *value, int *optlen);
        对于socket编程,level一般为常数SOL_SOCKET
        name属性类型,value属性参数,optlen属性内存块的长度
        常用的有:
        SO_RCVTIMEO,SO_SNDTIMEO:获得或设置socket发送/接收的timeout。
        SO_SNDBUF,SO_RCVBUF:获得或设置socket发送/接收的buffer大小。
        SO_BROADCAST:获得或设置socket状况,使之可以广播发送数据报。(只能用于UDP方式)。
        SO_REUSEADDR:设置该socket绑定的端口可以被重用。
        注意:在Linux系统中,如果一个socket绑定了某个端口,该socket正常关闭或程序退出后,在一段时间内该端口依然保持被绑定的状态,其他程序(或者重新启动的原程序)无法绑定该端口。可以通过调用以下语句避免该问题:
            opt = 1;
            len = sizeof(opt);
            setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);   
        

获得或改变socket的I/O属性:
    int ioctl(int sockfd,long cmd,unsigned long* argp);
        cmd属性类型,argp属性的参数。
        常用的有:
        FIONREAD,返回socket缓冲区中未读数据的字节数
        FIONBIO,argp为零时为阻塞模式,非零时为非阻塞模式
        SIOCATMARK ,判断是否有未读的带外数据(仅用于TCP协议),返回true或false   
    int fcntl(int fd, int cmd, long arg);
        F_SETFL,arp为O_NONBLOCK时进入非阻塞模式,为0时进入阻塞模式。
        F_GETFL,获得属性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值