Linux socket

    技术2022-05-13  13

    linux套接字头文件:

    #include <sys/socket.h>

     

    创建套接字:

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

     

    domain:

    AF_INET  IPv4

    AF_INET6 IPv6

    AF_UNIX Unix域

    AF_UNSPEC 未指定

     

    type:

    SOCK_DGRAM 长度固定,无连接,不可靠传递 默认协议是UDP

    SOCK_RAW ip协议的数据报接口

    SOCK_SEQPACKET 长度固定,有连接,可靠的传递

    SOCK_STREAM 有序,可靠,双向的连接字节流 默认协议是TCP

     

    protocol:

    一般为0,表示按给定的域和套接字类型选择默认协议

     

    调用socket和open类似,均可以使用输入/输出文件描述符(但并非所有,例如lseek),不再使用时候用close关闭

     

    可以使用shutdown禁止套接字上的输入输出

    int shutdown(int sockfd,int how);

     

    how:

    SHUT_RD 关闭读端,无法从套接字上读取数据

    SHUT_WR 关闭写端,无法从套接字上发送数据

    SHUT_RDWR 无法读和写

     

    关闭套接字:

    close(int sockfd);

     

     

    字节排序:

    字节排序有两种,大端和小端,在32位机器上,大端排序为:n,n+1,n+2,n+3,小端排序为:n+3,n+2,n+1,n,但是不管字节排序如何,数字最高位总是在左边,最小位总是在右边,比如0x04030301,数字最高位必定是4,最低位必定是1

     

    #include <arpa/inet.h>

     

    uint32_t htonl(uint32_t hostint32); 返回值:网络字节序表示的32位整数

    uint16_t htons(uint16_t hostint16); 返回值:网络字节序表示的16位整数

    uint32_t ntohl(uint32_t netint32);  返回值:主机字节序表示的32位整数

    uint16_t ntohs(uint16_t netint16);  返回值:主机字节序表示的16位整数

     

    记忆方法:

    h=host,n=network,l=long(表示4个字节),s=short(表示两个字节);

    注意:

    有些系统将其定义在<netinet/in.h>

    地址格式:

    为使不同格式地址能够被传入套接字函数,地址被强制转换成sockaddr表示:

    struct sockaddr

    {

    sa_family_t sa_family;

    char sa_data[];

    ......

    };

     

    套接字可以自由添加额外成员并自定义sa_data的大小,在linxu中,被定义为:

    struct sockaddr

    {

    sa_family_t sa_family;

    char sa_data[14];

    };

     

    FreeBSD:

    struct sockaddr

    {

    unsigned char sa_len;

    sa_family_t sa_family;

    char sa_data[14];

    };

     

    #include <netinet/in.h>

     

    IPv4中(AF_INET)中,套接字地址结构为:

    struct in_addr

    {

    in_addr_t s_addr;         in_addr_t为uint32_t

    };

     

    struct sockaddr_in

    {

    sa_family_t sin_family;

    in_port_t sin_port;   in_port_t为uint16_t

    struct in_addr  sin_addr;

    };

    uint32_t和uint16_t定义在<stdint.h>

     

    IPv6(AF_INET6):

    struct in6_addr

    {

    uint8_t s6_addr[16];

    };

     

    struct sockaddr_in6

    {

    sa_family_t sin6_family;

    in_port_t sin6_port;

    uint32_t sin6_flowinfo;

    struct in6_addr sin6_addr;

    uint32_t sin6_scope_id;

    };

     

    以上为必须定义,可以自由添加额外的字段:

    struct sockaddr_in

    {

    sa_family_t sin_family;

    in_port_t sin_port;

    struct in_addr sin_addr;

    unsigned char sin_zero[8];  //扩充字段,必须全部置为0

    };

     

    地址格式化:

    #include <arpa/inet.h>

     

    const char *inet_ntop(int doman,const void *restrict addr,char *restrict str,socklen_t size); 如果成功,返回地址字符串,否则返回NULL;

    将网络字节序的二进制地址转换成文本字符串

    size指定网络文本字符串缓冲区大小,INET4_ADDRSTRLEN和INET6定义足够大用来存放文本字符串

     

    int inet_pton(int domain,const char *restrict str,void *rstrict addr); 如果成功返回1,无效返回0,出错返回-1

    将文本字符串转换成网络字节序的二进制

    如果domain是(AF_INET)IPv4,缓冲区addr有足够大存放32位地址,对于IPv6(AF_INET6),则需要足够大的空间存放128地址

     

    以上两个函数均支持IPv4和IPv6,inet_addr和inet_ntoa仅支持IPv4

     

    in_addr_t inet_addr(const char *cp);

    char *inet_ntoa(struct in_addr in);

     

    地址查询:

    #include <netdb.h>

     

    struct hostent *gethostent(void); 成功返回指针,失败NULL

    打开数据文件,如果没有打开,会打开它

    void sethostent(int stayopen);

    如果没有打开,回绕

    void endhostend(void);

    关闭数据文件

     

    struct hostent

    {

    char *h_name;

    char **h_aliases;

    int  h_addrtype;

    int  h_lenght;

    char **h_addr_list;

    };

    注意:返回的地址采用网络字节顺序

    还有两个函数gethostbyname,gethostbyaddr,不过被认为是过时的,将会有替代函数

     

    struct netent *getnetbyaddr(uint32_t net,int type);

    struct netent *getnetbyname(const char *name);

    struct netent *getnetent(void);

    三个函数若成功返回指针,失败NULL

     

    void setnetent(int stayopen);

    void entnetent(void);

     

    struct netent

    {

    char *n_name;

    char **n_aliases;

    int  n_addrtype;

    uint32_t n_net;

    .....

    };

    按照网络字节返回,n_addrtype是一个地址足常量(例如AF_INET);

     

    可以将协议号采用以下函数映射:

    struct protoent *getprotobyname(const char *name);

    struct protoent *getprotobynumber(int proto);

    struct protoent *getprotoent(void);

     

    void setprotoent(int stayopen);

    void endprotoent(void);

     

    struct protoent

    {

    char *p_name;

    char **p_aliases;

    int  p_proto;

    .....

    };

     

    getservebyname可以将一个服务名字映射到一个端口号,getservbyport反之,也可以采用getservent顺序扫描服务数据库;

     

    struct servent *getservbyname(const char *name,const char *proto);

    struct servent *getservbyport(int port,const char *proto);

    struct servent *getservent(void);

     

    void setservent(int stayopen);

    void endservent(void);

     

    struct servent

    {

    char *s_name;

    char **s_aliases;

    int  s_port;

    char *s_proto;

    .....

    };

     

    这几个函数代替gethostbyname和gethostbyaddr

    #include <sys/socket.h>

    #include <netdb.h>

     

    int getaddrinfo(const char *restrict host,const char *restrict service,const struct addrinfo *restrict hint,struct addrinfo **restrict res);

    成功返回0,出错返回非0

    需要提供主机名字,服务名字,或者两者都提供,如果仅仅提供一个名字,另外一个必须使空指针,主机名字可以使一个节点名或者十进制计法表示的主机地址.

    void freeaddrinfo(struct addrinfo *ai);

     

    getaddrinfo返回一个addrinfo的链表,用freeaddrinfo释放.

     

    struct addrinfo

    {

    int ai_flags;

    int ai_family;

    int ai_socktype;

    int ai_protocol;

    socklen_t ai_addrlen;

    struct sockaddr *ai_addr;

    char *ai_canonname;

    struct addrinfo *ai_next;

    .....

    };

     

    ai_flags:

    AI_ADDRCONFIG 查询配置的地址类型(IPv4 or IPv6)

    AI_ALL 查找IPv4 and IPv6 地址(仅用于AI_V4MAPPED)

    AI_CANONNAME 需要一个规范名

    AI_NUMERICHOST 数字格式返回主机地址

    AI_NUMERICSERV 以端口号返回服务

    AI_PASSIVE 套接字地址用于监听绑定

    AI_V4MAPPED 如果没有IPv6,返回映射到IPv4的地址

     

    PS:

    如果getaddrinfo失败,不能用perror or strerror生成错误消息,替代调用gai_strerror将错误码转换错误消息

    const char *gai_strerror(int error);

     

     

    int getnameinfo(const struct sockaddr *restrict addr,socklen_t alen,char *restrict host,socklen_t hostlen,char *restrict service,socklen_t servlen,unsigned servlen,unsigned int flags);

    将地址转换成主机名或者服务名

     

    flags:

    NI_DGRAM 服务基于数字报而非流

    NI_NAMEREQD 如果找不到主机,作为一个错误对待

    NI_NOFQDN 对于本地主机,仅返回完全限定域名的节点名字部分

    NI_NUMERICHOST 数字形式返回主机地址

    NI_NUMERICSERV  数字形式返回服务地址 

    代码 // 示例代码: #include < netdb.h > #include < arpa / inet.h > #include < sys / socket.h > #include < netinet / in .h > #include < stdlib.h > #include < stdio.h > #include < string .h > void print_family( struct addrinfo * aip){ printf( " family " ); switch (aip -> ai_family) { case AF_INET: printf( " inet " ); break ; case AF_INET6: printf( " inet6 " ); break ; case AF_UNIX: printf( " unix " ); break ; case AF_UNSPEC: printf( " unspecified " ); break ; default : printf( " unknown " ); }} void print_type( struct addrinfo * aip){ printf( " type " ); switch (aip -> ai_socktype) { case SOCK_STREAM: printf( " stream " ); break ; case SOCK_DGRAM: printf( " datagram " ); break ; case SOCK_SEQPACKET: printf( " seqpacket " ); break ; case SOCK_RAW: printf( " raw " ); break ; default : printf( " unknown %d " ,aip -> ai_socktype); }} void print_protocol( struct addrinfo * aip){ printf( " protocol " ); switch (aip -> ai_protocol) { case 0 : printf( " default " ); break ; case IPPROTO_TCP: printf( " TCP " ); break ; case IPPROTO_UDP: printf( " UDP " ); break ; case IPPROTO_RAW: printf( " raw " ); break ; default : printf( " unknown %d " ,aip -> ai_protocol); }} void print_flags( struct addrinfo * aip){ printf( " flags " ); if (aip -> ai_flags == 0 ) { printf( " 0 " ); } else { if (aip -> ai_flags & AI_PASSIVE) printf( " passive " ); if (aip -> ai_flags & AI_CANONNAME) printf( " canon " ); if (aip -> ai_flags & AI_NUMERICHOST) printf( " numhost " ); if (aip -> ai_flags & AI_NUMERICSERV) printf( " numserv " ); }} int main( int argc, char * argv[]){ struct addrinfo * ailist, * aip; struct addrinfo hint; struct sockaddr_in * sinp; const char * addr; int err; char abuf[INET_ADDRSTRLEN]; if (argc != 3 ) { printf( " %s nodename service " ,argv[ 0 ]); return 1 ; } hint.ai_flags = AI_CANONNAME; hint.ai_family = 0 ; hint.ai_socktype = 0 ; hint.ai_protocol = 0 ; hint.ai_addrlen = 0 ; hint.ai_canonname = NULL; hint.ai_addr = NULL; hint.ai_next = NULL; if ((err = getaddrinfo(argv[ 1 ],argv[ 2 ], & hint, & ailist)) != 0 ) { printf( " error:%s/n " ,gai_strerror(err)); return 1 ; } for (aip = ailist;aip != NULL;aip = aip -> ai_next) { print_flags(aip); print_family(aip); print_type(aip); print_protocol(aip); printf( " /n/thost %s/n " ,aip -> ai_canonname ? aip -> ai_canonname: " - " ); if (aip -> ai_family == AF_INET) { sinp = ( struct sockaddr_in * )aip -> ai_addr; addr = inet_ntop(AF_INET, & sinp -> sin_addr,abuf,INET_ADDRSTRLEN); printf( " address %s/n " ,addr ? addr: " unknown " ); printf( " port %d " ,ntohs(sinp -> sin_port)); } printf( " /n " ); } return 0 ;} 转自 http://www.cnblogs.com/linyilong3/archive/2010/10/09/1846259.html

    最新回复(0)