首页 > 系统相关 >Linux 网络编程常用辅助函数

Linux 网络编程常用辅助函数

时间:2023-08-05 12:23:12浏览次数:33  
标签:__ 辅助 ai res 编程 char int Linux hints

最大地址结构

struct sockaddr_storage; // 足够大,能够支持任何套接字地址结构
从套接字获取信息
 // 获取本地连接的地址
extern int getsockname (int __fd, __SOCKADDR_ARG __addr, socklen_t *__restrict __len) __THROW;
// 获取连接另一侧的地址
extern int getpeername (int __fd, __SOCKADDR_ARG __addr, socklen_t *__restrict __len) __THROW;
字节序转换
 // 网络字节序到本地字节序
extern uint32_t ntohl (uint32_t __netlong);
extern uint16_t ntohs (uint16_t __netshort);
// 本地字节序到网络字节序
extern uint32_t htonl (uint32_t __hostlong);
extern uint16_t htons (uint16_t __hostshort);
字节操作函数
 // 还有类似的 bset bcmp bcpy
extern void *memset (void *__s, int __c, size_t __n);
extern int memcmp (const void *__s1, const void *__s2, size_t __n);
extern void *memccpy (void *__restrict __dest, const void *__restrict __src, int __c, size_t __n);
地址转换函数,字符串与网络字节序间转换地址
 // IPv4
// 废弃 extern in_addr_t inet_addr (const char *__cp) __THROW;
extern int inet_aton (const char *__cp, struct in_addr *__inp) __THROW;
extern char *inet_ntoa (struct in_addr __in) __THROW;

// IPv4 & IPv6
// p: presentation 表达式   n:numeric 数值
extern int inet_pton (int __af, const char *__restrict __cp, void *__restrict __buf) __THROW; // 字符串转二进制
extern const char *inet_ntop (int __af, const void *__restrict __cp, char *__restrict __buf, socklen_t __len ) __THROW; // 二进制转字符串
/*
socket长度可使用已有宏 
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
*/

当 read write 时内核缓存区达到极限,此时操作的字节数可能比请求的小,所以需要再次调用read或write

readn writen
ssize_t readn(int fd, void *buf, size_t count) { // 可使用 recv() 与 MSG_WAITALL 代替
  size_t nleft = count;
  ssize_t nread;
  char *bufp = (char *)buf;
  while (nleft > 0) {
    if ((nread = read(fd, bufp, nleft)) < 0) {
      if (errno == EINTR) { // 系统调用被捕获信号中断
        continue;
      }
      return -1;
    } else if (nread == 0) {
      return count - nleft;
    }
    bufp += nread;
    nleft -= nread;
  }
  return count;
}

ssize_t writen(int fd, const void *buf, size_t count) {
  size_t nleft = count;
  ssize_t nwrite;
  char *bufp = (char *)buf;
  while (nleft > 0) {
    if ((nwrite = write(fd, bufp, nleft)) < 0) {
      if (errno == EINTR) {
        continue;
      }
      return -1;
    } else if (nwrite == 0) {
      continue;
    }
    bufp += nwrite;
    nleft -= nwrite;
  }
  return count;
}
主机名与IP转换
 // 仅支持IPv4,通过主机名查找IP地址
struct hostent *gethostbyname (const char *__name );
// 通过二进制IP地址找到响应主机名
struct hostent *gethostbyaddr (const void *__addr, __socklen_t __len, int __type);


// 查找主机的所有IP和名称信息
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 from name server.  */
#ifdef __USE_MISC
# define	h_addr	h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};
通过名称或端口从 /etc/services 中获取服务
struct servent *getservbyname (const char *__name, const char *__proto);
struct servent *getservbyport (int __port, const char *__proto);

{
    struct servent *s = getservbyname("ftp", "tcp");
    struct servent *s = getservbyport(21, "tcp");
}

// 对服务的描述
struct servent
{
  char *s_name;			/* Official service name.  */
  char **s_aliases;		/* Alias list.  */
  int s_port;			/* Port number.  */
  char *s_proto;		/* Protocol to use.  */
};


/// 支持 IPv4 IPv6
int getaddrinfo (const char *__restrict __name, // 主机名 或 ip
			const char *__restrict __service,   // 服务名 或 十进制端口号数串
			const struct addrinfo *__restrict __req,  // 指向某个 addrinfo 结构的指针,填入期望返回值的暗示,可为空
			struct addrinfo **__restrict __pai);

const char *gai_strerror (int __ecode); // 返回 getaddrinfo 错误值对应的错误字符串

void freeaddrinfo (struct addrinfo *__ai); // 释放函数中分配的空间,参数为 getaddrinfo 的返回值

// 返回值是一个指向 addrinfo 链表的指针
struct addrinfo
{
  int ai_flags;			/* Input flags.  */
  int ai_family;		/* Protocol family for socket.  */
  int ai_socktype;		/* Socket type.  */
  int ai_protocol;		/* Protocol for socket.  */
  socklen_t ai_addrlen;		/* Length of socket address.  */
  struct sockaddr *ai_addr;	/* Socket address for socket.  */
  char *ai_canonname;		/* Canonical name for service location.  */
  struct addrinfo *ai_next;	/* Pointer to next in list.  */
};

/// 通过端口协议获取服务名称
int getnameinfo (const struct sockaddr *__restrict __sa,
			socklen_t __salen, char *__restrict __host,
			socklen_t __hostlen, char *__restrict __serv,
			socklen_t __servlen, int __flags);

gethostbyname gethostbyaddr getservbyname getservbyport inet_ntoa 是不可重入的,他们都返回指向同一个静态结构的指针,但有已 _r 结尾的支持可重入的版本

UNP 中对 getaddrinfo 的封装
 // 通过服务名查找服务的可用地址
int tcp_connect(const char* host, const char* serv) {
  int sockfd, n;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
    return -1;
  }
  ressave = res;

  do {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0) {
      continue;
    }
    // 查找到一个可用的地址,就退出循环
    if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
      break;
    }
    close(sockfd);
  } while ((res = res->ai_next) != nullptr);

  if (res == nullptr) {
    return -1;
  }

  freeaddrinfo(ressave);
  return sockfd;
}

int tcp_listen(const char* host, const char* serv, socklen_t* addrlenp) {
  int listenfd, n;
  const int on = 1;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(hints));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
    return -1;
  }
  ressave = res;

  do {
    listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (listenfd < 0) {  // socket失败,继续下一个
      continue;
    }
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) {  // bind成功,退出
      break;
    }
    close(listenfd);
  } while ((res = res->ai_next) != nullptr);

  if (res == nullptr) {  // 遍历完所有的地址,都没有成功
    return -1;
  }

  if (listen(listenfd, 5) < 0) {
    close(listenfd);
    return -1;
  }

  if (addrlenp) {
    *addrlenp = res->ai_addrlen;
  }

  freeaddrinfo(ressave);
  return listenfd;
}

// 创建未连接的UDP套接字
int udp_client(const char* host, const char* serv, struct sockaddr** saptr,
               socklen_t* lenp) {
  int sockfd, n;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;

  if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
    return -1;
  }
  ressave = res;

  do {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0) {
      continue;
    }
    break;
  } while ((res = res->ai_next) != nullptr);

  if (res == nullptr) {
    return -1;
  }

  *saptr = (struct sockaddr*)malloc(res->ai_addrlen);
  memcpy(*saptr, res->ai_addr, res->ai_addrlen);
  *lenp = res->ai_addrlen;

  freeaddrinfo(ressave);
  return sockfd;
}

// 创建已连接的UDP套接字
int udp_connect(const char* host, const char* serv) {
  int sockfd, n;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;

  if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
    return -1;
  }
  ressave = res;

  do {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0) {
      continue;
    }
    if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
      break;
    }
    close(sockfd);
  } while ((res = res->ai_next) != nullptr);

  if (res == nullptr) {
    return -1;
  }

  freeaddrinfo(ressave);
  return sockfd;
}

int udp_server(const char* host, const char* serv, socklen_t* addrlenp) {
  int sockfd, n;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(hints));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;

  if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
    return -1;
  }
  ressave = res;

  do {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0) {
      continue;
    }
    if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
      break;
    }
    close(sockfd);
  } while ((res = res->ai_next) != nullptr);

  if (res == nullptr) {
    return -1;
  }

  if (addrlenp) {
    *addrlenp = res->ai_addrlen;
  }

  freeaddrinfo(ressave);
  return sockfd;
}

 

标签:__,辅助,ai,res,编程,char,int,Linux,hints
From: https://www.cnblogs.com/zhh567/p/17603818.html

相关文章

  • linux系统 nginx-部署安装
    以CentOS7.9为例下载nginx,当前版本以 nginx-1.19.3为例安装依赖,已安装可跳过yuminstall-ygcc-c++opnesslpcre-develzlibzlib-deve但是有时候会有各种原因报错,你可以选择去解决,也可以换个思路,那么我不从yum源下载了,直接从网上下载所需的库进入/optcd/opt安装pcre库w......
  • 《安富莱嵌入式周报》第319期:声音编程器,开源激光雕刻机,自制600W海尔贝克无刷电机,车用
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 更新视频教程:更新第7期ThreadX视频教程:如何实现RTOS高效的任务管理,抢占式调度,时间片调度和零中断延迟(2023-07-31)https://www.armbbs.cn/forum.php?mod=viewthread&tid......
  • Linux环境编程day03--文件管理
    分别使用标准IO和系统IO写入一百万个整数到文件,测试谁的时间更短?为什么?结论:在同等数据的写入下,使用标准IO要比直接使用系统IO更快原因:标准IO有缓冲区机制,在执行fwrite写文件时,数据不是直接调用系统IO写入磁盘,而是先存放在内存的缓冲区中,直到缓冲区满后才会调用一次系统IO全部写......
  • Linux环境编程day04--信号产生与处理
    信号管理基本概念1、中断当进程接收到消息后中止当前正在进行进程,转而去执行其它任务,等其它任务执行结束后再返回刚刚中止的位置,可以继续往下运行这种执行模式称为中断中断分为硬件中断、软件中断,硬件中断是由硬件设备引发的、软件中断是执行了中断指令引发2、信号信号是一......
  • 【雕爷学编程】Arduino动手做(182)---DRV8833双路电机驱动模块
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞......
  • 我的嵌入式Linux相关文章
    crosscompilerToolchain(交叉编译工具链)的建立构造嵌入式Linux(一):Kernel编译构造嵌入式Linux(二):vmlinux、vmlinuz和bzImage建立Host和Target的MTD工具[摘]嵌入式linux系统的开启Moblin:kernel编译和rpm打包(一):更换kernelMoblin:kernel编译和rpm打包(二):RPM打包X86的bootloader(一):......
  • window下VMWare安装Linux
    一、下载VMWare首先,在浏览器地址栏中输入链接地址https://www.vmware.com/cn.html进入VMWare官网,如图所示。选择导航栏中的“产品”选项,在下拉菜单的右侧选择“WorkstationPro”选项,如图所示。下拉,选择下载试用版这边使用的是windows版本的二、安装VMWare下载VMWare安......
  • linux mint (ubuntu)下安装Rime输入法
    linuxmint(ubuntu)下安装Rime输入法清理fctix输入法框架的其他依赖rm-rf$HOME/.config/fcitx安装Rime输入法sudoaptinstallfcitx-rimelibrime-*修改配置文件,将输入法设置为小鹤双拼mkdir-p$HOME/.config/fcitx/rime&&cd$HOME/.config/fcitx/rimeecho......
  • Linux基础32 nginx多虚拟主机,日志,日志目录模块,访问限制模块
    虚拟主机方式一:基于主机多IP方式基于主机多ip的方式,主机多网卡,多外网ip(一般不使用这种方式)[[email protected]]#catchess.confserver{listen10.0.0.7:80;server_namelocalhost;location/{root/code/chess;indexindex.html;}}[r......
  • NVIDIA 535.86.05 Linux 图形驱动程序改进 Wayland 支持
    NVIDIA公司近日发布了适用于 Linux、FreeBSD和Solaris系统的NVIDIA535.86.05图形驱动程序,作为其生产分支的维护更新,解决了各种错误和问题。NVIDIA535.86.05是在NVIDIA535.54.03发布一个多月之后发布的,它通过解决在使用某些Wayland合成器时关闭连接的显示器时......