【linux服务器编程学习】4、linux网络编程技术API

linux网络编程技术API

包括socket地址API、socket基础API,网络信息API

网络字节序和主机字节序

也叫大端和小端。

  • 大端字节序:一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
  • 小端字节序:一个整数的高位字节存储在内存的高地址处,低位字节存储在内存的低地址处。

判断字节序的代码

bool IsBigEndian()
{
    union
    {
        unsigned short a ;//2个字节
        char b ;//高地址
    } c;

    c.a =0x0102 ;

    if(c.b ==1)//如果高地址存放低字节
        return true ;
    else
        return false ;
}


linux提供的字节序转换函数

#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

通用socket地址

#include<socket.h>
struct sockaddr{
	sa_family_t sa_family;
	char sa_data[14];
}
  • sa_family 是地址类型的变量,地址族类型通常与协议族类型对应
  • sa_data 用于存放socket地址值

专用socket地址

struct sockaddr_in{
	sa_family_t sin_family;//地址族
	u_int16_t sin_port;//端口号
	struct in_addr sin_addr;//ipv4地址结构体
};

struct in_addr{//ipv4 地址结构体
	u_int32_t s_addr;
};


IP地址转换函数

#include<arpa/inet.h>
in_addr_t inet_addr(const char* strptr);
//点分十进制字符串转ipv4地址,失败返回INADDR_NONE

int inet_aton(const char* cp,struct in_addr* inp);
//同inet_addr,结果存在inp中,成功1失败0

char* inet_ntoa(struct in_addr in);
//ipv4地址转点分十进制字符串,结果具有不可重入性

#include<arpa/inet.h>
int inet_pton(int af,const const char* src,void* dst);
//字符串转地址结构体,可选ipv4/6
//af指定地址族,AF_INET或AF_INET6,
//src是要转换的字符串
//dst是转换结果

const char* inet_ntop(int af,const void * src,char dst,socklen_t cnt);
//地址结构体转字符串,可选ipv4/6
//af指定地址族
//src是要转换的地址结构体
//dst存储转换结果
//cnt指定目标存储单元的大小

创建socket

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

参数:
domain:协议族PF_INET 或PF_INET6
type:指定服务类型,TCP是SOCK_STREAM,UDP是SOCK_DGRAM
protocol:一般设置为0
返回:
成功返回一个sock文件描述符,失败返回-1并设置errno

命名socket

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

bind将所指的socket地址分配给未命名的sockfd文件描述符,addrken指定了socket地址的长度。
成功返回0,失败返回-1,并设置errno。

监听

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

给指定的sockfd创建监听队列,队列长度为backlog
成功返回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地址,addrlen是他的长度的指针。
返回一个新的连接的socket,该socket唯一标识了被接受的这个连接。
失败返回-1并设置errno

发起连接

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

sockfd是要用来连接的socket文件描述符
serv_addr是要连接的服务器地址,紧跟着他的长度
成功返回0,失败返回-1并设置errno。

关闭连接

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

可以用关闭普通文件描述符的方式关闭socket文件描述符

值得注意的是,close实际上是将该文件描述符的引用计数-1,当引用计数为0时才真正关闭,因此在多进程中,父子进程中都要执行close。

对于socket可以用专用的方法,立即终止连接

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

sockfd是要关闭的socket文件描述符,howto决定了shutdown的行为。
howto可选值:
SHUT_RD :关闭sockfd的读端,不再执行读操作
SHUT_WR:关闭socket的写端,不再执行写操作
SHUT_RDWR:同时关闭读写端

数据读写

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

recv用于在指定的socket文件描述符上读取数据,数据读取后保存在长度为len的buf里,flag通常设置为0,recv成功返回读取的长度,可能小于len,因此可能要多次调用recv才能独到完整的数据。失败返回-1并设置errno。

send用于发送数据到指定的socket文件描述符上,要发送的数据保存在长度为len的buf里,flag通常设置为0。成功返回实际写入的长度,失败返回-1并设置errno。

UDP的读写
#include<sys/types.h>
#include<sys/socket.h>
ssize_t recvfrom(int sockfd,void* buf,size_t len,int flags,struct sockaddr* src_addr,socklen_t* addrlen);
ssize_t sendto(int sockfd,void* buf,,size_t len,int flags,struct sockaddr* dest_addr,socklen_t addrlen);

recvfrom读取sockfd上的数据,buf和len分别指定读缓冲区位置和大小,还要指定src_addr发送端的地址和addrlen发送端地址长度的指针。

sendto往sockfd上写入数据,buf和len参数分别指定写缓冲区位置和大小,还要指定dest接收端的地址和addrlen接收端地址长度。

通用数据读写

socket编程接口提供了一对通用的数据读写,可用于TCP和UDP

#include<sys/socket.h>
ssize_t recvmsg(int sockfd,struct msghdr* msg,int flags);
ssize_t sendmsg(int sockfd,struct msghdr* msg,int flags);
struct msghdr{
	void * msg_name; //socket地址,对于TCP设置为NULL
	socklen_t msg_namelen;//socket地址长度
	struct iovec* msg_iov;//分散的内存块
	int msg_iovlen;//分散内存块数量
	void* msg_control;//指向辅助数据
	socklen_t msg_controllen;//辅助数据大小
	int msg_flags;//复制函数中的flags参数,并在调用过程中更新
}
  • 其中,msg_iov是 iovec结构体类型的一个数组指针

  • iovec里保存了一段内存的起始地址和长度。

  • 对于recvmsg,数据将被读取,存放在msg_iovlen块分散的内存中,由msg_iov这个数组记录这些分散的内存,叫做分散读。

  • 对于sendmsg,这些分散的内存将被一并发送,叫做集中写。

struct iovec{
	void *iov_base;
	size_t iov_len;
};
带外标记

Linux内核检测到TCP紧急标志时,通知应用程序有带外数据要接收。通知的方式一般是IO复用产生的异常事件和SIGURG信号。
应用程序还需要知道带外数据在数据流中的具体位置。

#include<sys/socket.h>
int sockatmark(int sockfd);

这个系统调用用于判断sockfd是否有带外标记,如果是则返回1,否则返回0.

地址信息函数

#include<sys/socket.h>
int getsockname(int sockfd,struct sockaddr* address,socklen_t* address_len);
int getpeername(int sockfd,struct sockaddr* address,socklen_t*address_len);

这两个函数分别是获得本端的socket地址和获得对端的socket地址。
成功返回0,失败-1,并设置errno。

socket选项

#include<sys/socket.h>
int getsockopt(int sockfd,int level,int option_name,void* option_value,socklen_t* restrict option_len)
int setsockopt(int sockfd,int level,int option_name,const void* option_value,socklen_t* option_len)

sockfd是要设置或者获取选项的socket文件描述符
level指定了要操作哪个协议的选项,如IPV4、IPV6、TCP
option_name指定了选项的名字
option_value 选项的值
option_len 选项的长度
请添加图片描述
成功返回0,失败-1,并设置errno。

使用举例:
1、设置地址重用

 setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);

2、设置缓冲区大小

/* 设置TCP发送缓冲区大小 */
  int sendbuf = atoi(argv[3]);
  int len = sizeof sendbuf;
  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuf, len);

  /* 读取socket选项 */
  getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t*)&len);
  printf("发送缓冲区大小是:%d\n", sendbuf);

网络信息API

根据主机名或地址获取主机信息
#include<netdb.h>
struct hostent* gethostbyneme(const char*name);
struct hostent* gethostbyaddr(const void* addr,size_t len,int type);

name指定目标主机的主机名
addr指定目标主机的IP地址,
len指定该地址长度
type指定地址类型:AF_INET/AF_INET6

返回hostent结构体
struct hostent{
	char * h_naem;//主机名
	char** h_aliases;//主机别名列表
	int h_addr_type;//地址类型
	int h_length;//地址长度
	char** h_addr_list;//按网络字节序列出的IP地址列表
根据端口号或服务名获取某个服务的完整信息
 getservbyport
   /* 根据端口号获取某个服务的完整信息 */ 
    struct servent* getservbyport(int port, const char *proto); 
     
    /* 参数说明 */
    // port:目标服务对应的端口号 
    // proto:指定服务类型,给它传递“tcp”表示获取流服务,
    “udp”表示数据报服务,NULL表示所有类型的服务 
    
getservbyname

    /* 根据服务名称获取某个服务的完整信息 */
    struct servent* getservbyname(const char *name, const char *proto);
     
    /* 参数说明 */
    // name:目标服务的名字
    // proto:指定服务类型,给它传递“tcp”表示获取流服务,
    “udp”表示数据报服务,NULL表示所有类型的服务
靠主机名获得IP、靠服务名获得端口
getaddrinfo

    /* 既能通过主机名获得IP地址,也能通过服务名获得端口号 */
    int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);
     
    /* 参数说明 */
    // hostname:可以接收主机名,也可以接收字符串表示的IP地址 
    // service:可以接收服务名称,也可以就收字符串表示的十进制端口号
    // hints:应用程序给getaddrinfo的一个提示,以对getaddrinfo的输出进行更精确的控制
    // result:指向一个链表,该链表用于存储getaddrinfo反馈的结果 
靠地址获得主机名和服务名
 getnameinfo

    /* 能通过socket地址同时获得以字符串表示的主机名和服务名 */
    int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);
     
    /* 参数说明 */ 
    // host:返回的主机名  
    // serv:返回的服务名 
    // flags:控制getnameinfo的行为 

后记:linux网络编程技术API复杂多样,不可死记硬背

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值