基本套接口编程

1、套接口地址比较:

IPV4:sockaddr_in 包含长度+AF_INET+16端口号+32位IPV4地址+未用(固定长度16字节)

IPV6:sockaddr_in6 包含长度+AF_INET6+16位端口号+32位流标+128位IPV6地址+32位范围ID(固定长度28字节)

Unix:sockaddr_un 包含长度+AF_LOCAL+路径名(可变长度)

datalink:sockaddr_dl  (可变长度)

storage:sockaddr_storage 包含长度+AF_XXX+用户透明段(系统中最大长度)

针对于不同的协议族有不同的套接口结构,但是当作为参数传递给一个套接口函数时,套接口函数要处理所有类型的套接口,所以设计成了一个通用的套接口,一般在传递给套接口参数时要进行套接口的强制转换。

2、通用套接口:(sys/socket.h)

struct sockaadr

{

uint8_t sa_len;

sa_family_t sa_family;

char sa_data[14];

};

强制转换例子如下:struct sockaddr_in serv; bind(sockfd,(struct sockaddr *)&serv,sizeof(serv));

 其中长度sizeof(serv)的类型是sockaddr_len

3、新的通用套接口结构:(<netinet/in.h>)

sockaddr_storage与sockaddr相比有一下两个优点:

(1):如果系统支持的任何套接口地址结构有对齐需要,那么sockaddr_storage能够满足最苛刻的对齐要求;

(2): sokcaddr_storage足够大,能够容纳系统所支持的任何套接口地址结构。

4、字节排序函数

一般网络协议在处理多字节整数时都是使用大端字节序,但是对于不同系统,可能采用大端,也可能采用小端,所以产生了能够将主机字节序和网络字节序进行转换的字节排序函数:

#include<netinet/in.h>
uint16_t htons(uint host16bitvalue);
uint32_t htonl(uint host32bitvalue);

uint16_t ntohs(uint host16bitvalue);
uint16_t ntohl(uint host32bitvalue);

前两个函数返回网络字节序, htons用于端口号,htonl用于IP地址,后两个函数返回的是主机字节序。

5、字节操纵函数

对于类似套接口地址结构某些字段需要设置为0等情况,有一下而已字节的操纵函数:

#include<strings.h>
void bzero(void *dest,size_t nbytes);
void bcopy(const void *src,void *dest,size_t nbytes);
int bcmp(const void *ptr1,const void *ptr2,size_t nbytes);
第一个用用来初始化套接口地址结构时置0
bzero(&serv,sizeof(sev));
 相应的ANSI C也提供了类似的函数:
#include<strings.h>
void *memset(void *dest,int c,size_t len);
void *memcpy(void *dest,const *src,size_t nbytes);
int memcmp(const void *ptr1,const void *ptr2,size_t nbytes);

6、地址转换函数

有时候我们希望把二进制存储的网络字节序的IP地址转换成为我们习惯看到的ASCII码的点分十进制的字符串,则可以使用以下函数:

#include<arpa/inet.h>
int inet_aton(const char* strptr,struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
char *inet_ntoa(struct in_addr inaddr);

两个新的函数inet_pton和inet_ntop也能实现这样的功能。

7、进程到内核传递套接口地址的4个套接口函数:bind 、connect、sendto、sendmsg

内核到进程传递套接口地址结构的5个函数为:accept 、recvfrom 、recvmsg 、getpeername、 getsockname

8、read/readn以及write/writen函数读缓冲区的区别

read和write是读或者写一定字节的数据进入缓冲区,一旦读回的数据小于指定的字节数时,认为到达了文件尾。但是对于读socket则不同,由于读回来的数据很有可能小于指定的字节数,但是这可能是套接口的缓冲区到达了极限,而不是文件尾,所以还应该继续尝试后续读写,直到所有的数据都读写完毕。所以一般使用readn,和writen.

#include"apue.h"
ssize_t readn(int fd,void *ptr,size_t n){
    size_t nleft;
    ssize_t nread;

    nleft=n;
    while(nleft>0){
        if((nread=read(fd,ptr,nleft))<0){//当read返回-1,代表出错了

            if(nleft==n)
                return(-1);/*若第一次read时出错, readn 返回 -1 */
            else
                break;/*若读了一些数据后出错, readn返回已经读的字节数,而不出错返回 */
        }else if(nread==0){//当read返回0时,不再读下去,readn返回已经读的字节数

            break;/* EOF,文件尾 */
        }
        nleft-=nread;//当read返回值>0但不等于n时,继续读

        ptr+=nread;
    }
    return(n-nleft);/* return >= 0 ,返回的始终是已经读取的字节数*/
}

ssize_t writen(int fd,const void *ptr,size_t n){
    size_t nleft;
    ssize_t nwritten;

    nleft=n;
    while(nleft 0){
        if((nwritten=write(fd,ptr,nleft))<0){//当write返回-1,

            if(nleft==n)/*若第一次write时出错, writen 返回 -1 */
                return(-1);
            else
                break;/*若写了一些数据后出错, writen返回已经读的字节数,而不出错返回 */
        }else if(nwritten==0){//当write返回0时,不再写下去,writen返回已经读的字节数

            break;
        }
        nleft-=nwritten;//当write返回值>0但不等于n时,继续写

        ptr+=nwritten;
    }
    return(n-nleft);/* return >= 0 */
}

更多嵌入式linux及编程学习交流的文章,请访问我的个人网站”恩享网” :http://www.enxiang.icoc.cc,期待与您共同进步。

原文地址:https://www.cnblogs.com/LJTbozai/p/3062806.html