C语言基础-网络相关函数
一、相关函数
函数 | 描述 |
---|---|
int socket(int family, int type, int protocol); |
位于:<sys/socket.h>; family:AF_INET(ipv4) | AF_INET6(ipv6) | AF_UNIX (本地)|AF_LOCAL(本地) | AF_NETLINK(设备驱动)| AF_PACKET(原始套接字)... type:SOCK_STREAM(流式套接字-对应TCP)|SOCK_DGRAM(数据报套接字-对应UDP)|SOCK_RAW(原始套接字) protocol:协议(TCP,UDP都填0) 在原始套接字的时候 填对应编码 |
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); |
位于:<sys/socket.h>; sockfd:通过socket拿到的描述符 myaddr:sockaddr结构体变量 实际是 sockaddr_in结构体强转为sockaddr addrlen:结构体变量长度 |
int listen(int sockfd, int backlog); |
位于:<sys/socket.h>; sockfd:通过socket拿到的描述符 backlog:同时允许几路客户端和服务区正在连接的过程 一般填5 |
int accept(int sockfd, struct sockaddr *cliaddr, socklne_t *addrlen); |
位于:<sys/socket.h>; sockfd:通过socket拿到的描述符 cliaddr:客户端的结构体存储位置 |
int connect(int sockfd,const struct sockaddr *servaddr, socklen_t addrlen); |
位于:<sys/socket.h>; sockfd:通过socket拿到的描述符 servaddr:sockaddr结构体 与bind类似 addrlen:结构体长度 |
int clost(int sockfd); | 位于:<sys/socket.h>; 功能:关闭套接字,终止连接; 返回:成功返回0,出错返回-1。 |
ssize_t send(int fd, const void *buf, size_t len, int flags) |
位于:<sys/socket.h>; send与write类似多了一个flags参数 flag: 填0与write功能一样 填MSG_DONTWAIT 立即返回不会阻塞 填MSG_OOB 用于发送TCP类型的带外数据 |
ssize_t recv(int fd, void *buf, size_t len, int flags) |
位于:<sys/socket.h>; recv与read类似多了一个flags参数 flag: 填0与read功能一样 填MSG_DONTWAIT 立即返回不会阻塞 填MSG_OOB 用于发送TCP类型的带外数据 填MSG_PEEK 读完数据后不移除 |
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *adddrlen); | 位于:<sys/socket.h>; 功能:返回与某个套接字关联的本地协议地址; 返回:成功返回实际接受字节数,出错返回-1。 |
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *adddrlen); | 位于:<sys/socket.h>; 功能:返回某个套接字关联的外地协议地址; 返回:成功返回0,出错返回-1。 |
int shutdown(int sockfd, int howto); | 位于:<sys/socket.h>; 功能:关闭套接字; 参数:sockfd为套接字标识;howto可能为: SHUT_RD关闭连接的输入流,SHUT_WR关闭连接的输出流,SHUT_RDWR输入输出都关闭; 返回:成功返回0,出错返回-1。 |
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); |
位于:<sys/socket.h>; sockfd :为套接字标识 level:指定控制套接字的层级,可以取三种值:SOL_SOCKET通用套接字选项(应用层),IPPROTO_IP ip选项(网络层),IPPROTO_TCP tcp选项(传输层) optname:见下表 optval:对应optname的数据类型地址 例如 快速重用 int b_reuse =1; setsockopt=(fd,SOL_SOCKET,SO_REUSERADDR,&b_reuse,sizeof(int)) optlen:对应optval的长度 |
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); |
位于:<sys/socket.h>; sockfd:为套接字标识 level:指定控制套接字的层级,可以取三种值:SOL_SOCKET通用套接字选项(应用层),IPPROTO_IP ip选项(网络层),IPPROTO_TCP tcp选项(传输层) optname:见下表 optval:对应optname的数据类型地址 例如 快速重用 int b_reuse =1; setsockopt=(fd,SOL_SOCKET,SO_REUSERADDR,&b_reuse,sizeof(int)) optlen:对应optval的长度 |
int fcntl(int fd, int cmd, …); | 位于:<fcntl.h>; 功能:执行各种描述符控制操作; 返回:成功返回0,出错返回-1。 |
optname
保活策略
#include <sys/socket.h> #include <netinet/tcp.h> int keepAlive = 1; // 设置KeepAlive int keepIdle = 5;// 开始首次keepalive探测前的TCP空闲时间 int keepInterval = 5; // 两次keepalive探测间的时间间隔 int keepCount = 3; // 判定断开前keepalive探测次数 void SetKeepAlive(int sockfd, int attr_on, socklen_t idle_time, socklen_t interval, socklen_t cnt) { setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char *) &attr_on, sizeof(attr_on)); setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (const char *) &idle_time, sizeof(idle_time)); setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (const char *) &interval, sizeof(interval)); setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (const char *) &cnt, sizeof(cnt)); }
Demo:创建一个socket套接字 server部分
#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <zconf.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> int main(int argc, char *argv[]) { int fd = -1; // 句柄 int newfd = -1; char buf[64]; // 读写缓冲区 int res = -1; struct sockaddr_in sin; // bind需要用到的结构体 // 1.创建socket套接字 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 0; } // 初始化bind需要用到的 sockaddr_in 结构体 bzero(&sin, sizeof(sin)); // 清空内容 sin.sin_family = AF_INET; // sin_family 与创建套接字的family一致 sin.sin_port = htons(8080); // 端口号 需要传网络段 所以需要转化 if (inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr) < 0) { // inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); perror("inet_pton"); return 0; } // 2.绑定bind if ((bind(fd, (struct sockaddr *) &sin, sizeof(sin))) < 0) { perror("bind"); return 0; } // 3.listen 监听 if (listen(fd, 5) < 0) { perror("listen"); return 0; } // 4.阻塞 if ((newfd = accept(fd, NULL, NULL)) < 0) { perror("accept"); return 0; } // 5.读写 bzero(buf, sizeof(buf)); // 循环读 while (read(newfd, buf, 32) > 0) { puts(buf); } // 关闭套接字 close(newfd); }
Dem:客户端
#include <stdio.h> #include <unistd.h> #include <arpa/inet.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> // 客户端 int main(int argc, char *argv[]) { int fd = -1; // 句柄 char buf[64]; // 读写缓冲区 int res = -1; struct sockaddr_in sin; // bind需要用到的结构体 // 1.创建socket套接字 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 0; } // 初始化connect需要用到的 sockaddr_in 结构体 bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; // sin_family 与创建套接字的family一致 sin.sin_port = htons(8080); // 端口号 需要传网络段 所以需要转化 if (inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr) < 0) { // inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); perror("inet_pton"); return 0; } // 2.连接 if ((connect(fd, (struct sockaddr *) &sin, sizeof(sin))) < 0) { perror("connect"); return 0; } // 3.写 while (1) { fgets(buf, 64, stdin); write(fd, buf, 63); } // 4.关闭套接字 close(fd); }
1、创建服务器套接字 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); 返回套接字 2、构建服务器地址结构 struct sockaddr_in serveraddr; #include <strings.h> bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP serveraddr.sin_port = htons(SERVER_PORT);//端口 3、绑定地址 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 4、构建组播属性结构 struct ip_mreqn group; #include <arpa/inet.h> inet_pton(AF_INET,GROUP,&group.imr_multiaddr);//设置组播地址 net_pton(AF_INET,"0.0.0.0",&group.imr_address);//设置本地地址 group.imr_ifindex=if_nametoindex("ent0");//设置网卡接口 5、设置组播权限和属性 setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&group, sizeof(group));//设置组播权限及选项 6、设置客户端组播地址 struct sockaddr_in cliaddr; bzero(&cliaddr,sizeof(cliaddr)); cliaddr.sin_family=AF_INET; inet_pton(AF_INET,GROUP,&cliaddr.sin_addr.s_addr); cliaddr.sin_port=htons(CLIENT_PORT); 7、发送数据 sendto(sockfd,buf,strlen(buf),0,(structsockaddr*)&cliaddr, sizeof(cliaddr));//往组播地址发送信息,返回数据大小
1、创建客户端套接字 2、构建客户端地址结构 3、绑定地址 4、构建组播结构 5、设置组播权限和属性 6、接收数据 #include <sys/types.h> #include <sys/socket.h> len=recvfrom(confd,buf,sizeof(buf),0,NULL,0);//接收数据
二、UDP相关函数
函数 | 描述 |
---|---|
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); |
位于:<sys/socket.h>; 前面几个参数与recv一致 from:sockaddr结构体填充发送发的ip和端口 addrlen:地址长度的地址 |
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen); |
位于:<sys/socket.h>; 前面几个参数跟上述的send参数一致 to:sockaddr的结构体填充对方的ip和端口信息 addrlen:地址长度 |
Demo:服务端
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> #include <arpa/inet.h> // server int main(int argc, char *argv[]) { int fd = -1; // 句柄 char buf[64]; // 读写缓冲区 struct sockaddr_in sin; // bind需要用到的结构体 struct sockaddr_in cli; // 获取客户端链接后的有一些信息 socklen_t len = sizeof(cli); // 1.创建socket套接字 这个地方使用UDP SOCK_DGRAM if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 0; } // 初始化bind需要用到的 sockaddr_in 结构体 bzero(&sin, sizeof(sin)); // 清空内容 sin.sin_family = AF_INET; // sin_family 与创建套接字的family一致 sin.sin_port = htons(8080); // 端口号 需要传网络段 所以需要转化 sin.sin_addr.s_addr = htonl(INADDR_ANY); // 不需要绑定主机ip 任何都可以访问 // if (inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr) < 0) { // inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); // perror("inet_pton"); // return 0; // } // 2.绑定bind if ((bind(fd, (struct sockaddr *) &sin, sizeof(sin))) < 0) { perror("bind"); return 0; } // 3.udp读写 while (1) { bzero(buf, 64); // 接受消息 需要强制转换(struct sockaddr*) // 最后一个len一定要传地址 if (recvfrom(fd, buf, 63, 0, (struct sockaddr *) &cli, &len) < 0) { perror("recvfrom"); return 0; } char ipv4[16]; if (!inet_ntop(AF_INET,(void *)&cli.sin_addr,ipv4,sizeof(cli))){ perror("inet_ntop"); return 0; } printf("%s:%d : %s\n",ipv4,ntohs(cli.sin_port),buf); } // 关闭套接字 close(fd); }
Demo:客户端
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> #include <arpa/inet.h> // client int main(int argc, char *argv[]) { int fd = -1; // 句柄 char buf[64]; // 读写缓冲区 struct sockaddr_in sin; // bind需要用到的结构体 int addrlen = sizeof(sin); // 1.创建socket套接字 这个地方使用UDP SOCK_DGRAM if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 0; } // 初始化bind需要用到的 sockaddr_in 结构体 bzero(&sin, sizeof(sin)); // 清空内容 sin.sin_family = AF_INET; // sin_family 与创建套接字的family一致 sin.sin_port = htons(8080); // 端口号 需要传网络段 所以需要转化 // sin.sin_addr.s_addr = htonl(INADDR_ANY); // 不需要绑定主机ip 任何都可以访问 if (inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr.s_addr) < 0) { // inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); perror("inet_pton"); return 0; } // 2.udp读写 while (1) { // 写消息 bzero(buf, 64); if (fgets(buf, 64, stdin) < 0) { perror("fgets"); return 0; } if (sendto(fd, buf, 63, 0, (struct sockaddr *) &sin, addrlen) < 0) { perror("recvfrom"); continue; } } // 关闭套接字 close(fd); }
三、SCTP相关函数
函数 | 描述 |
---|---|
int sctp_bindx(int sockfd, const struct sockaddr *addrs, int addrcnt, int flags); | 位于:<netinet/sctp.h>; 功能:允许SCTP套接字绑定一个特定地址子集; 返回:成功返回0,出错返回-1。 |
int sctp_connectx(int sockfd, const struct sockaddr *addrs, int addrcnt); | 位于:<netinet/sctp.h>; 功能:连接到一个多宿对端机; 返回:成功返回0,出错返回-1。 |
int sctp_getpaddrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs); | 位于:<netinet/sctp.h>; 功能:获取对端所有地址; 返回:成功返回0,出错返回错-1。 |
int sctp_freepaddrs(struct sockaddr *addrs); | 位于:<netinet/sctp.h>; 功能:释放由sctp_getpaddrs函数分配的资源。 |
int sctp_getladdrs(int sockfd, sctp_assoc_t id, struct sockaddr **addrs); | 位于:<netinet/sctp.h>; 功能:获取属于某个关联的本地地址; 返回:成功返回存放在addrs中的本端地址数,出错返回错-1。 |
void sctp_freeladdrs( struct sockaddr *addrs); | 位于:<netinet/sctp.h>; 功能:释放由sctp_getladdrs函数分配的地址。 |
void sctp_sendmsg(int sockfd, const void *msg, size_t msgsz, const struc sockaddr *to, socklen_t tolen, uint32_t ppid, uint flags, uint16_t stream, uint32_t timetolive, uint32_t context); | 位于:<netinet/sctp.h>; 功能:简化发送方式; 返回:成功返回所有写字节数,出错返回-1。 |
ssize_t sctp_recvmsg(int sockfd, const void *msg, size_t msgsz, const struc sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags); | 位于:<netinet/sctp.h>; 功能:简化获取数据过程; 返回:成功返回所有读字节数,出错返回-1。 |
int sctp_opt_info(int sockfd, sctp_assoc_t assoc_id, int opt, void *arg, socklen_t *siz); | 位于:<netinet/sctp.h>; 功能:把参数重新包装到合适的getsockopt调用中的库函数; 返回:成功返回0,出错返回-1。 |
int sctp_peeloff(int sockfd, sctp_assoc_t id); | 位于:<netinet/sctp.h>; 功能:从一个一到多式套接字中抽取一个关联,构成单独一个一到一式套接字; 返回:成功返回一个新的套接字描述符,出错返回错-1。 |
四、IP与地址相关函数
函数 | 描述 |
---|---|
struct hostent *gethostbyname(const char *hostname); | 位于:<netdb.h>; 功能:通过名称获取host; 返回:成功返回一个非空指针,出错返回-1。 |
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family); | 位于:<netdb.h>; 功能:通过IP地址获取主机名; 返回:成功返回非空指针,出错返回-1。 |
struct servent *getservbyname(const char *servname, const char *protoname); | 位于:<netdb.h>; 功能:通过给定名字与协议查找服务; 返回:成功返回非空指针,出错返回-1。 |
struct servent *getservbyport(int port, const char *protoname); | 位于:<netdb.h>; 功能:根据给定端口号与协议查询服务; 返回:成功返回非空指针,出错返回错1。 |
struct servent *getaddrinfo(const char *hostname, const *service, const struct addrinfo *hints, struct addrinfo **result); | 位于:<netdb.h>; 功能:处理名字到地址以及服务到端口这两种转换,返回一个sockaddr结构; 返回:成功返回0,出错返回-1。 |
const char *gai_strerror(int error); | 位于:<netdb.h>; 功能:返回一个指向对应的出错信息串的指针。 |
void freeaddrinfo(struct addrinfo *ai); | 位于:<netdb.h>; 功能:清除getaddrinfo函数生成的内存空间。 |
int getnameinfo(struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); | 位于:<netdb.h>; 功能:返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串; 返回:成功返回0,出错返回NULL。 |
struct hostent *gethostbyname_r(const char *hostname, struct hostent *result, char *buf, int buflen, int *h_errnop); | 位于:<netdb.h>; 功能:; 返回:成功返回非空指针,出错返回NULL。 |
struct hostent *gethostbyaddr_r(const char *addr, struct int len, int type, struct hostent *result, char *buf, int buflen, int *h_errnop); | 位于:<netdb.h>; 功能:; 返回:成功返回非空指针,出错返回NULL且设置error_num。 |
struct hostent *gethostbyname2(const chat *name, int af); | 位于:<netdb.h>; 功能:; 返回:成功返回非空指针,出错返回错-1。 |
struct hostent *getipnodebyname(const chat *name, int af, int flags, int *error_num); | 位于:<netdb.h>; 功能:; 返回:成功返回非空指针,出错返回NULL且设置error_num。 |
五、字节序转换函数
函数 | 作用 |
主机字节序到网络字节序 | |
u_long htonl(u_long hostlong); |
位于<arpa/inet.h> htonl ()用来将参数指定的32 位hostlong 转换成网络字符顺序.即大端模式(big-endian) |
u_short htons(u_short short); |
位于<arpa/inet.h> htons ()用来将参数指定的16 位short 转换成网络字符顺序.即大端模式(big-endian)
|
网络字节序到主机字节序 | |
u_long ntohl(u_long hostlong); |
位于<arpa/inet.h> 本函数将一个32位数由网络字节顺序转换为主机字节顺序。 |
u_short ntohs(u_short short); |
位于<arpa/inet.h> 本函数将一个16位数由网络字节顺序转换为主机字节顺序。 |
六、IP地址的转换
函数 | 作用 |
int inet_aton(const char *, struct in_addr *); |
位于<arpa/inet.h> 功能是将一个字符串IP地址转换为一个32位的网络序列IP地址。如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零 |
in_addr_t inet_addr(const char *); |
位于<arpa/inet.h> inet_addr()的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)。 仅适用于ipv4 出错时返回-1 不能用户255.255.255.255的转换 |
char *inet_ntoa(struct in_addr); |
位于<arpa/inet.h> 将一个IP转换成一个互联网标准点分格式的字符串。 |
int inet_pton(int af, const char *src, void *dst); |
位于<arpa/inet.h> inet_pton是一个IP地址转换函数,可以在将IP地址在点分十进制和“二进制整数”之间转换 而且inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。 af:地址协议族(AF_INET或AF_INET6) src:是一个指针(填写点分形式的IP地址) dst:转换的结果给到dst |
结构体
in_addr
struct in_addr { in_addr_t s_addr; // 网络序 addr 32位 };
sockaddr_in
struct sockaddr_in { __uint8_t sin_len; sa_family_t sin_family; // 两个字节 in_port_t sin_port; // 端口号 网络序 struct in_addr sin_addr; // in_addr 四个字节 char sin_zero[8]; // 必须置0 };
七、网络信息检索函数
函数 | 作用 |
int gethostname(char *name, size_t namelen); |
位于<unistd.h> 获取主机名 |
int getpeername(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); |
位于<sys/socket.h> 获得与套接口相连的远程协议地址 |
int getsockname(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
|
位于<sys/socket.h> 根据主机名获取主机信息 |
struct hostent * gethostbyname(const char *name); |
位于<netdb.h> 成功返回结构体指针,失败返回NULL name:可以填写域名或点分形式的ip值 |
void endhostent(void); |
位于<netdb.h> 如果不需要gethostbyname返回的结构体指针 执行此函数销毁变量释放空间 |
void herror(const char *string); |
位于<netdb.h> 如果出错使用此方法输出 |
struct protoent * getprotobyname(const char *name); |
位于<netdb.h> 根据协议好取得主机协议信息 |
struct protoent * getprotobynumber(int proto); |
位于<netdb.h> 根据协议号取得主机协议信息 |
struct servent * getservbyname(const char *name, const char *proto); |
位于<netdb.h> 根据服务名取得相关服务信息 |
struct servent * getservbyport(int port, const char *proto); |
位于<netdb.h> 根据端口号取得相关服务信息 |
#include <stdio.h> #include <netdb.h> int main(int argc, char *argv[]) { char domain[] = "www.hao123.com"; struct hostent *hs = NULL; // 域名解析 if ((hs = gethostbyname(domain)) == NULL){ herror("gethostbyname"); return 0; } // 输出地址 printf("---->%d",*(uint32_t *)hs->h_addr_list); // 回收 endhostent(); }