浅析linux网络编程基础API

linux网络API分为三个方面:
(1)socket地址api。socket最开始的含义是一个IP地址和端口对(ip,port)。它唯一地表示了使用TCP通信的一端。称为socket地址。
(2)socket基础api。socket主要api定义在sys/socket.h这个头文件中,包括创立socket,命名socket,接受连接,读写数据,获取地址信息,检测带外标记,以及读取和设置socket选项。
(3)网络信息api。linux提供了一套网络信息api,已实现主机名和IP地址的转换,以及服务器名称和端口号之间的转换。这些api都定义在netdb.h中。
一,通用socket地址
socket网络编程接口中表示socket地址的是sockaddr,其定义如下:

#include<bits/socket.h>
struct sockaddr
{
    sa_family_t sa_family;
    char sa_data[14];
}

sa_family表示地址族类型。sa_data成员用于存放socket地址值。
协议族及其地址值

由上表可见,14字节的sa_data根本无法容纳多数协议族的地址值,因此linux定义了下面这个新的通用socket结构体:

#include<bits/socket.h>
struct sockaddr_storage
{
    sa_family_t sa_family;
    unsigned long int _ss_align;
    char _ss_padding[128-sizeof(__ss_align)];
}

二,专用socket地址
通用socket一般情况下不太使用linux下的网络开发,因为在设置和获取IP地址和端口需要执行很繁琐的操作,所以linux为各个协议族提供了专门的socket地址结构体:
(1)UNIX本地协议族:

#include<sys/un.h>
struct sockaddr_un
{
    sa_family_t sin_family;   //地址族AF_UNIX
    char sun_path[108];       //文件路径名
}

(2)TCP/IP协议族,用于IPv4

struct sockaddr_in
{
    sa_family_t sin_family;        //地址族:AF_INET
    u_int16_t sin_port;              //端口号,要用网络字节序表示
    struct in_addr sin_addr;     //IPv4地址结构体
}
struct in_addr
{
    u_int32_t s_addr;               //IPv4地址,要用网络字节序表示
}

(3)TCP/IP协议族,用于IPv6

struct sockaddr_in6
{
    sa_family_t sin6_family;    //地址族:AF_INET6
    u_int16_t sin6_port;         //端口号,要用网络字节序表示
    u_int32_t sin6_flowinfo;  //流信息,一般设为0
    struct in6_addr sin6_addr;
    u_int32_t sin6_scope_id; //scope ID
}
struct in_addr
{
    unsigned char sa_addr[16];
}

所以专业socket地址类型的变量在实际使用中都需转化为通用socket地址类型sockaddr(强制转换即可),因为所有的socket编程接口使用的地址参数类型都是sockaddr。

三,IP地址转换函数

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

inet_addr函数将用点分十进制字符串表示的IPv4地址转换为网络字节序整数表示的IPv4地址,它失败时返回INAADDR_NONE。
inet_aton函数完成和inet_addr同样的功能,但是转换的结果存储于参数inp指向地址结构中,成功是返回1,失败返回0;
inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址,但需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ntoa是不可重入的。

char* szValue1=inet_ntoa("1.2.3.4");
char* szValue2=inet_ntoa("10.194.71.60");
printf("address 1: %s\n",szValue1);
printf("address 2: %s\n",szValue2);

运行后结果为:
address1: 10.194.71.60
address2: 10.194.71.60

创建socket:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);

domain参数为协议族,type参数为服务类型,主要有SOCK_STREAM(流服务),和SOCK_UGTAM(数据报服务)。protocol参数通常设为0;
命名socket:

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);

bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出该socket地址的长度。
bind成功时返回0,失败返回-1,并设置errno。
其中两种常见的errno分别是:
EACCES:被绑定的地址是受保护的地址,仅超级用户访问,如将socket绑定到0~1023端口上。
EADDRINUSE:该绑定的地址正在使用中,比如将socket绑定到一个处于TIME_WAIT状态的socket地址。
监听socket:

#include<sys/socket.h>
int listen(int sockfd,int backlog);

sockfd表示被监听的socket,backlog表示内核监听队列最大长度。如果超过backlog,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息。在内核版本2。2之前的linux中,backlog参数是指所有处于连接状态(SYN_RCVD)和完全连接状态(ESTABLISHED)的socket的上限。但自内核版本2.2之后,它只表示处于完全连接状态的上限,处于半连接状态的socket的上限则由/proc/sys/net/ipv4/tcp_max_backlog内核参数定义。backlog参数的典型值是5。
listen成功时返回0,失败则返回-1,并设置errno。
接受连接:

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

sockfd参数是执行过listen系统调用socket。addr参数是用来获取被接受连接的远端socket地址,该socket地址的长度由addrlen参数指出。accept成功时返回一个新的连接socket,该socket唯一的标识了被接收的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通信。accept失败时返回-1并设置errno。
发起连接:

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);

sockfd参数由socket系统调用返回一个socket。serv_addr参数是服务器监听的socket地址,addrlen参数则指定这个地址的长度。connect成功时返回0。一旦成功连接,sockfd就唯一的标识了这个连接,客户端就可以通过读写sockfd来与服务器通信,connect失败时则返回-1并设置errno。
关闭连接:

#include<unistd.h>
int close(int fd);

fd参数是待关闭的socket,但是close系统不是关闭一个连接,而是将引用技术减一,只有当fd的引用计数为0,才真正关闭连接。
如果想要立刻终止连接可以使用shutdown。

#include<sys/socket.h>
int shutdown(int sockfd,int howto);

成功返回0,失败返回-1并设置errno。howto可选值为SHUT_RD:关闭sockfd上读的这一半,SHUT_WR:关闭sockfd上写的这一半,SHUT_RDWR:同时关闭读和写。
数据读写:
对文件的读写操作read和write可以对socket进行读写操作,但是socket编程接口提供了几个专门用于socket数据读写的系统调用。它们增加了对数据读写的控制。其中用于TCP流数据读写的系统调用是:

#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void *buf,size_t len,int flags);
ssize_t send(int sockfd,const void *buf,size_t len,int flags);

recv读取sockfd上的数据,buf和len参数分别指定都缓冲区的位置和大小,flags参数通常设置为0。
recv成功是表示实际读取数据的长度,它可能小于我们期待的长度len。因此我们可能要多次调用recv,才能读取到完整的数据。recv可能返回0,表示通信对方已经关闭连接了,recv出错时返回-1并设置errno。
send往sockfd上写入数据,buf和len参数分别指定写缓冲区的位置和大小。send成功时返回实际写入的数据和长度,失败则返回-1,并设置errno。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值