网络 IPC 套接字socket

APUE书中所有实例源码下载地址:http://www.apuebook.com

apue学习笔记(第十六章 网络IPC:套接字):https://www.cnblogs.com/runnyu/p/4648678.html

一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字:http://www.cnblogs.com/0xcafebabe/p/4478824.html

linux下网络编程:http://blog.csdn.net/freeking101/article/details/71449111


根据 APUE (第十六章 网络IPC:套接字) 整理,如有疑问,直接看 APUE。。。



1. 套接字描述符




socket 函数


使用 man socket 查看具体使用

NAMEsocket - create an endpoint for communicationSYNOPSIS#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int socket(int domain, int type, int protocol);DESCRIPTIONsocket()  creates  an  endpoint  for communication and returns a file descriptor that refers to that endpoint.The file descriptor returned by a successful call will be the lowest-numbered file  descriptor  not  currentlyopen for the process.








2. 地址


字节序


IP地址+端口 可以唯一确定一个socket进程。运行在同一计算机上进程之间相互通信时。一般不用考虑字节的顺序(字节序分为大端序和小端序。小端序:低地址存放数据低位,高地址存放数据高位,可以记忆为:小弟弟(低低),小高高。虽说有点黄但好记,大端序和小端序存储相反)。字节序是一个处理器架构特性,用于指示像整数这样的大数据类型的内部字节顺序。不同架构的处理器可能是大端也可能是小端,有些处理器可以配置成大端或者小端。字节序不一样,数据就不能正常处理。

大端格式:低地址存放高位数据,高地址存放低位数据。
小端格式:低地址存放低位数据,高地址存放高位数据。


如图所示,假设要存放的数据是 0x30313233,那么 33 是低位,30 是高位,在大端存储格式中,30 存放在低位,33 存放在高位;而在小端存储格式中,33 存放在低位,30 存放在高位。这个东西有什么作用呢?它其实就是我们使用的网络设备(计算机、平板电脑、智能手机等等)在内存当中存储数据的格式。



处理器字节序和网络序转换函数


既然通讯双方的设备存储数据的格式不同,那么一端发送过去的数据,另一端是无法正确解析的,这该怎么办?没关系,其实系统准备了一组函数可以实现字节序转换,我们可以像使用公式一样使用它们。




地址格式





二进制地址和点分十进制字符串地址转换






地址内部结构信息查询



相关函数(可以使用 man 命令查看函数的详细使用帮助):

get network host entry (得到给定计算机的主机信息)gethostbyname, gethostbyaddr, sethostent, gethostent, endhostent, h_errno, herror, hstrerror, gethostbyaddr_r,gethostbyname2, gethostbyname2_r, gethostbyname_r, gethostent_r get network entry (得到网络和网络号相关信息)getnetent, getnetbyname, getnetbyaddr, setnetent, endnetentget protocol entry (得到协议和协议号相关信息)getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent get service entry (得到服务相关信息。服务是由地址的端口号部分表示的)getservent, getservbyname, getservbyport, setservent, endserventnetwork address and service translation	(网络地址和服务映射)getaddrinfo, freeaddrinfo, gai_strerror


示例 使用getaddrinfo 打印主机和服务信息

使用到的数据结构

struct addrinfo {  int              ai_flags;  int              ai_family;  int              ai_socktype;  int              ai_protocol;  size_t           ai_addrlen;  struct sockaddr *ai_addr;  char            *ai_canonname;  struct addrinfo *ai_next;  
};  struct sockaddr_in {  sa_family_t       sin_family; /* Address family       */  __be16        sin_port;   /* Port number          */  struct in_addr    sin_addr;   /* Internet address     */  /* Pad to size of `struct sockaddr'. */  unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -  sizeof(unsigned short int) - sizeof(struct in_addr)];  
};  struct in_addr {  __be32  s_addr;  
}; 

hint是用于过滤地址的模版。只选取ai_flags,ai_type,ai_family,ai_protocol,用来指定如何处理地址和名字。
addrgetinfo()取得输入主机和服务的 addrinfo解构,按照hint的过滤作用保存在ailist中,依次打印flag,type等,在通过定义struct sockaddr_in snip来保存AF_INET family 这一簇地址格式的 aip->ai_addr;使用inet_ntop()函数把得到的地址信息解析出来,放到addr指针域,打印出来。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.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("unkown ");}
}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 ");}
}int main()
{struct addrinfo *ailist,*aip;struct addrinfo hint;struct sockaddr_in *sinp;const char *addr;int err;char abuf[INET_ADDRSTRLEN];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(getaddrinfo("localhost",NULL,&hint,&ailist) != 0){printf("getaddrinfo error: %s",gai_strerror(err));exit(-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",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 ",addr?addr:"unknown");printf(" port %d ",ntohs(sinp->sin_port));}printf("\n");}exit(0);
}

下面是程序运行结果:



套接字与地址绑定


bind 函数

可以使用 bind 函数,将地址绑定到一个套接字。(man bind)

NAMEbind - bind a name to a socketSYNOPSIS#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);



getsockname 函数和getpeername 函数

可以调用 getsockname 函数来得到绑定到套接字上的地址。(man getsockname):

NAMEgetsockname - get socket nameSYNOPSIS#include <sys/socket.h>int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);DESCRIPTIONgetsockname()  returns  the  current  address to which the socket sockfd is bound, in the buffer pointed to byaddr.  The addrlen argument should be initialized to indicate the amount of space (in  bytes)  pointed  to  byaddr.  On return it contains the actual size of the socket address.The  returned  address  is  truncated if the buffer provided is too small; in this case, addrlen will return avalue greater than was supplied to the call.RETURN VALUEOn success, zero is returned.  On error, -1 is returned, and errno is set appropriately.


如果套接字已经和对等方连接,可以调用getpeername函数得到对方的地址。(man getpeername)。和 getsockname 用法相同:

NAMEgetpeername - get name of connected peer socketSYNOPSIS#include <sys/socket.h>int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);DESCRIPTIONgetpeername()  returns  the  address  of  the peer connected to the socket sockfd, in the buffer pointed to byaddr.  The addrlen argument should be initialized to indicate the amount of space  pointed  to  by  addr.   Onreturn  it contains the actual size of the name returned (in bytes).  The name is truncated if the buffer pro‐vided is too small.The returned address is truncated if the buffer provided is too small; in this case,  addrlen  will  return  avalue greater than was supplied to the call.



3. 建立连接




connect 函数

使用 man connect 查看具体使用方法:

NAMEconnect - initiate a connection on a socketSYNOPSIS#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);



下面代码显示了一种如何处理瞬时 connect 错误的方法,这在一个负载很重的服务器上很有可能发生。

#include "apue.h"
#include <sys/socket.h>#define MAXSLEEP 128int connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
{int numsec;/** Try to connect with exponential backoff.*/for (numsec = 1; numsec <= MAXSLEEP; numsec <<= 1) {if (connect(sockfd, addr, alen) == 0) {/** Connection accepted.*/return(0);}/** Delay before trying again.*/if (numsec <= MAXSLEEP/2)sleep(numsec);}return(-1);
}

示例代码:

#include "apue.h"
#include <sys/socket.h>#define MAXSLEEP 128int connect_retry(int domain, int type, int protocol, const struct sockaddr *addr, socklen_t alen)
{int numsec, fd;/** Try to connect with exponential backoff.*/for (numsec = 1; numsec <= MAXSLEEP; numsec <<= 1) {if ((fd = socket(domain, type, protocol)) < 0)return(-1);if (connect(fd, addr, alen) == 0) {/** Connection accepted.*/return(fd);}close(fd);/** Delay before trying again.*/if (numsec <= MAXSLEEP/2)sleep(numsec);}return(-1);
}



listen 函数


服务器调用listen函数来宣告它愿意接受连接请求

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


参数backlog提供了一个提示,提示系统改进程所要入队的未完成连接请求数量。一旦队列满了,系统就会拒绝多余的连接请求。

一旦服务器调用了listen,所用的套接字就恩能够接受连接请求。使用accept函数获得连接请求并建立连接。

#include <sys/socket.h>
int accept(int fd,struct sockaddr *restrict addr,socklen_t *restrict len); 

函数返回的是一个连接到调用connect的客户端的套接字描述符


服务器初始化套接字:

#include "apue.h"
#include <errno.h>
#include <sys/socket.h>int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
{int fd;int err = 0;if ((fd = socket(addr->sa_family, type, 0)) < 0)return(-1);if (bind(fd, addr, alen) < 0)goto errout;if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {if (listen(fd, qlen) < 0)goto errout;}return(fd);errout:err = errno;close(fd);errno = err;return(-1);
}




4. 数据传输




发送数据的三个函数(man send)和 接收收据的三个函数(man recv)

NAMEsend, sendto, sendmsg - send a message on a socketSYNOPSIS#include <sys/types.h>#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);---------------------------------------------------------------------------------------------------------
NAMErecv, recvfrom, recvmsg - receive a message from a socketSYNOPSIS#include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);


发送数据的函数


send 函数

使用 man send 查看具体使用方法。最简单的是 send 函数,它和 write 很像,但是可以指定标志来改变处理传输数据的方式。

使用 send 函数时,套接字必须已经连接,例如 使用 TCP 协议 的发送数据。参数 buf 和 len 与 write 函数中含义一致。

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

下面总结了 send 函数中套接字 flags 可能的取值



sendto 函数

函数sendto和send很类似,区别在于sendto可以在无连接的套接字上指定一个目标地址。例如 使用 UDP 协议的发送数据。

#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);



sendmsg 函数

#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

POSIX.1定义了msghdr结构,它至少有以下成员:

struct msghdr {void         *msg_name;       /* optional address */socklen_t     msg_namelen;    /* size of address */struct iovec *msg_iov;        /* scatter/gather array */size_t        msg_iovlen;     /* # elements in msg_iov */void         *msg_control;    /* ancillary data, see below */size_t        msg_controllen; /* ancillary data buffer len */int           msg_flags;      /* flags on received message */
};


接收数据的函数


recv 函数

函数recv和read相似,但是recv可以指定标志来控制如何接收数据

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




recvfrom 函数

#include <sys/socket.h>
ssize_t recvfrom(int sockfd,void *restrict buf,size_t len,int flagsstruct sockaddr *restrict addr,socklen_t *restrict addrlen);



recvmsg 函数

#include <sys/socket.h>
ssize_t recvmsg(int sockfd,struct msghdr *msg,int flags);




客户机/服务器模式



在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式*(client/server),即客户像服务其提出请求,服务器接受到请求后,提供相应的服务。

服务器:

(1)首先服务器方要先启动,打开一个通信通道并告知本机,它愿意在某一地址和端口上接收客户请求

(2)等待客户请求到达该端口

(3)接收服务请求,处理该客户请求,服务完成后,关闭此新进程与客户的通信链路,并终止

(4)返回第二步,等待另一个客户请求

(5)关闭服务器

客户方:

(1)打开一个通信通道,并连接到服务器所在的主机特定的端口

(2)向服务器发送请求,等待并接收应答,继续提出请求

(3)请求结束后关闭通信信道并终止



基于TCP(面向连接)的socket编程



服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。


示例程序

程序显示一个客户端命令,该命令用于与服务器通信以获取系统命令 uptime 的输出,该服务称为“remote uptime”(简称 ruptime)

客户端:

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>#define BUFLEN		128extern int connect_retry(int, int, int, const struct sockaddr *, socklen_t);void print_uptime(int sockfd)
{int		n;char	buf[BUFLEN];while ((n = recv(sockfd, buf, BUFLEN, 0)) > 0)write(STDOUT_FILENO, buf, n);if (n < 0)err_sys("recv error");
}int main(int argc, char *argv[])
{struct addrinfo	*ailist, *aip;struct addrinfo	hint;int				sockfd, err;if (argc != 2)err_quit("usage: ruptime hostname");memset(&hint, 0, sizeof(hint));hint.ai_socktype = SOCK_STREAM;hint.ai_canonname = NULL;hint.ai_addr = NULL;hint.ai_next = NULL;if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)err_quit("getaddrinfo error: %s", gai_strerror(err));for (aip = ailist; aip != NULL; aip = aip->ai_next) {if ((sockfd = connect_retry(aip->ai_family, SOCK_STREAM, 0, aip->ai_addr, aip->ai_addrlen)) < 0) {err = errno;} else {print_uptime(sockfd);exit(0);}}err_exit(err, "can't connect to %s", argv[1]);
}


服务端:

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>#define BUFLEN    128
#define QLEN 10#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endifextern int initserver(int, const struct sockaddr *, socklen_t, int);void serve(int sockfd)
{int        clfd;FILE    *fp;char    buf[BUFLEN];set_cloexec(sockfd);for (;;) {if ((clfd = accept(sockfd, NULL, NULL)) < 0) {syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));exit(1);}set_cloexec(clfd);if ((fp = popen("/usr/bin/uptime", "r")) == NULL) {sprintf(buf, "error: %s\n", strerror(errno));send(clfd, buf, strlen(buf), 0);} else {while (fgets(buf, BUFLEN, fp) != NULL)send(clfd, buf, strlen(buf), 0);pclose(fp);}close(clfd);}
}int main(int argc, char *argv[])
{struct addrinfo    *ailist, *aip;struct addrinfo    hint;int                sockfd, err, n;char            *host;if (argc != 1) err_quit("usage: ruptimed");if ((n = sysconf(_SC_HOST_NAME_MAX)) < 0) n = HOST_NAME_MAX;    /* best guess */if ((host = malloc(n)) == NULL) err_sys("malloc error");if (gethostname(host, n) < 0) err_sys("gethostname error");daemonize("ruptimed");memset(&hint, 0, sizeof(hint));hint.ai_flags = AI_CANONNAME;hint.ai_socktype = SOCK_STREAM;hint.ai_canonname = NULL;hint.ai_addr = NULL;hint.ai_next = NULL;if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));exit(1);}for (aip = ailist; aip != NULL; aip = aip->ai_next) {if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0) {serve(sockfd);exit(0);}}exit(1);
}


另一个版本的服务器程序

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/wait.h>#define QLEN 10#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endifextern int initserver(int, const struct sockaddr *, socklen_t, int);void serve(int sockfd)
{int        clfd, status;pid_t    pid;set_cloexec(sockfd);for (;;) {if ((clfd = accept(sockfd, NULL, NULL)) < 0) {syslog(LOG_ERR, "ruptimed: accept error: %s",strerror(errno));exit(1);}if ((pid = fork()) < 0) {syslog(LOG_ERR, "ruptimed: fork error: %s",strerror(errno));exit(1);} else if (pid == 0) {    /* child *//** The parent called daemonize ({Prog daemoninit}), so* STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO* are already open to /dev/null.  Thus, the call to* close doesn't need to be protected by checks that* clfd isn't already equal to one of these values.*/if (dup2(clfd, STDOUT_FILENO) != STDOUT_FILENO ||dup2(clfd, STDERR_FILENO) != STDERR_FILENO) {syslog(LOG_ERR, "ruptimed: unexpected error");exit(1);}close(clfd);execl("/usr/bin/uptime", "uptime", (char *)0);syslog(LOG_ERR, "ruptimed: unexpected return from exec: %s",strerror(errno));} else {        /* parent */close(clfd);waitpid(pid, &status, 0);}}
}int main(int argc, char *argv[])
{struct addrinfo    *ailist, *aip;struct addrinfo    hint;int                sockfd, err, n;char            *host;if (argc != 1)err_quit("usage: ruptimed");if ((n = sysconf(_SC_HOST_NAME_MAX)) < 0)n = HOST_NAME_MAX;    /* best guess */if ((host = malloc(n)) == NULL)err_sys("malloc error");if (gethostname(host, n) < 0)err_sys("gethostname error");daemonize("ruptimed");memset(&hint, 0, sizeof(hint));hint.ai_flags = AI_CANONNAME;hint.ai_socktype = SOCK_STREAM;hint.ai_canonname = NULL;hint.ai_addr = NULL;hint.ai_next = NULL;if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",gai_strerror(err));exit(1);}for (aip = ailist; aip != NULL; aip = aip->ai_next) {if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr,aip->ai_addrlen, QLEN)) >= 0) {serve(sockfd);exit(0);}}exit(1);
}





基于UDP(面向无连接)的socket编程



服务器先创建socket,将socket绑定(bind)一个本地地址和端口上,等待数据传输(recvfrom)。这个时候如果有个客户端创建socket,并且向服务器发送数据(sendto),服务器就建立了连接,实现了数据的通信,连接结束后关闭连接。


示例程序

客户端:

下面程序采用数据报套接字接口的uptime客户端命令版本

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>#define BUFLEN        128
#define TIMEOUT        20void sigalrm(int signo)
{
}void print_uptime(int sockfd, struct addrinfo *aip)
{int        n;char    buf[BUFLEN];buf[0] = 0;if (sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)err_sys("sendto error");alarm(TIMEOUT);if ((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0) {if (errno != EINTR)alarm(0);err_sys("recv error");}alarm(0);write(STDOUT_FILENO, buf, n);
}int main(int argc, char *argv[])
{struct addrinfo        *ailist, *aip;struct addrinfo        hint;int                    sockfd, err;struct sigaction    sa;if (argc != 2)err_quit("usage: ruptime hostname");sa.sa_handler = sigalrm;sa.sa_flags = 0;sigemptyset(&sa.sa_mask);if (sigaction(SIGALRM, &sa, NULL) < 0)err_sys("sigaction error");memset(&hint, 0, sizeof(hint));hint.ai_socktype = SOCK_DGRAM;hint.ai_canonname = NULL;hint.ai_addr = NULL;hint.ai_next = NULL;if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)err_quit("getaddrinfo error: %s", gai_strerror(err));for (aip = ailist; aip != NULL; aip = aip->ai_next) {if ((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0)) < 0) {err = errno;} else {print_uptime(sockfd, aip);exit(0);}}fprintf(stderr, "can't contact %s: %s\n", argv[1], strerror(err));exit(1);
}


服务器程序:

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>#define BUFLEN        128
#define MAXADDRLEN    256#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endifextern int initserver(int, const struct sockaddr *, socklen_t, int);void
serve(int sockfd)
{int                n;socklen_t        alen;FILE            *fp;char            buf[BUFLEN];char            abuf[MAXADDRLEN];struct sockaddr    *addr = (struct sockaddr *)abuf;set_cloexec(sockfd);for (;;) {alen = MAXADDRLEN;if ((n = recvfrom(sockfd, buf, BUFLEN, 0, addr, &alen)) < 0) {syslog(LOG_ERR, "ruptimed: recvfrom error: %s",strerror(errno));exit(1);}if ((fp = popen("/usr/bin/uptime", "r")) == NULL) {sprintf(buf, "error: %s\n", strerror(errno));sendto(sockfd, buf, strlen(buf), 0, addr, alen);} else {if (fgets(buf, BUFLEN, fp) != NULL)sendto(sockfd, buf, strlen(buf), 0, addr, alen);pclose(fp);}}
}int
main(int argc, char *argv[])
{struct addrinfo    *ailist, *aip;struct addrinfo    hint;int                sockfd, err, n;char            *host;if (argc != 1)err_quit("usage: ruptimed");if ((n = sysconf(_SC_HOST_NAME_MAX)) < 0)n = HOST_NAME_MAX;    /* best guess */if ((host = malloc(n)) == NULL)err_sys("malloc error");if (gethostname(host, n) < 0)err_sys("gethostname error");daemonize("ruptimed");memset(&hint, 0, sizeof(hint));hint.ai_flags = AI_CANONNAME;hint.ai_socktype = SOCK_DGRAM;hint.ai_canonname = NULL;hint.ai_addr = NULL;hint.ai_next = NULL;if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",gai_strerror(err));exit(1);}for (aip = ailist; aip != NULL; aip = aip->ai_next) {if ((sockfd = initserver(SOCK_DGRAM, aip->ai_addr,aip->ai_addrlen, 0)) >= 0) {serve(sockfd);exit(0);}}exit(1);
}




5. 套接字选项


套接字机制提供了设置跟查询套接字选项的接口。可以获取或设置以下3种选项

1.通用选项,工作在所有套接字类型上

2.在套接字层次管理的选项,但是依赖于下层协议的支持

3.特定于某协议的选项,每个协议独有的

可以使用setsockopt函数来设置套接字选项

#include <sys/socket.h>
int setsockopt(int sockfd,int level,int option,const void *val,socklen_t len);

level标识了选项应用的协议:

如果选项是通用的套接字层次选项,则level设置为SOL_SOCKET,否则,level设置成控制这个选项的协议编号。对于TCP选项,level是IPPROTO_TCP,对于IP,level是IPPROTO_IP。

下面总结了Single UNIX Specification中定义的通用套接字层次选项


可以使用getsockopt函数来查看选项的当前值

#include <sys/socket.h>
int getsockopt(int sockfd,int level,int option,void *restrict val,socklen_t restrict lenp);

采用地址复用初始化套接字端点:

#include "apue.h"
#include <errno.h>
#include <sys/socket.h>int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
{int fd, err;int reuse = 1;if ((fd = socket(addr->sa_family, type, 0)) < 0)return(-1);if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse,sizeof(int)) < 0)goto errout;if (bind(fd, addr, alen) < 0)goto errout;if (type == SOCK_STREAM || type == SOCK_SEQPACKET)if (listen(fd, qlen) < 0)goto errout;return(fd);errout:err = errno;close(fd);errno = err;return(-1);
}



6. 带外数据


带外数据是一些通信协议所支持的可选特征,允许更加高级的数据比普通数据优先传输。

TCP将带外数据称为紧急数据。TCP仅支持一个字节的紧急数据,但是允许紧急数据在普通数据传递机制数据流之外传输。

为了产生紧急数据,在三个send函数中任何一个指定标志MSG_OOB。如果带MSG_OOB标志传输字节超过一个时,最后一个字节被作为紧急数据字节。

如果安排发生套接字信号,当接收到紧急数据时,那么发送信号SIGURG信号。可以通过调用以下函数安排进程接收套接字的信号:

fcntl(sockfd,F_SETTOWN,pid);

F_GETOWN命令可以用来获取当前套接字所有权

owner=fcntl(sockfd,F_GETOWN,0);

为帮助判断是否已经达到紧急标记,可以使用函数sockatmark

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

当下一个要读取的字节在紧急标志处时,sockatmark返回1。



7. 非阻塞和异步I/O


在基于套接字异步I/O中,当能够从套接字中读取数据,或者套接字写队列中的空间变得可用时,可以安排发送信号SIGIO。通过两个步骤来使用异步I/O:

1) 建立套接字拥有者关系,信号可以被传送到合适的进程。

2) 通知套接字当I/O操作不会阻塞时发信号告知。

可以使用三种方式来完成第一个步骤:

A、 在fcntl使用F_SETOWN命令

B、 在ioctl中作用FIOSETOWN命令

C、 在ioctl中使用SIOCSPGRP命令。

     要完成第二个步骤,有两个选择:

A、 在fcntl中使用F_SETFL命令并且启用文件标志O_ASYNC。

B、 在ioctl中使用FIOASYNC




示例



TCP 示例 客户端 和服务端程序:

服务端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>#define SERV_PORT    8000int main(void)
{int sfd, cfd;struct sockaddr_in serv_addr, clin_addr;   //IP  + 端口号socklen_t clin_addr_len;char buf[4096];int len, i;//AF_INET:ipv4协议  SOCK_STREAM:流式协议  0:流式协议里默认协议TCPsfd = socket(AF_INET, SOCK_STREAM, 0); /* 构造本地进程绑定的地址信息 */bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;   // IPv4地址类型serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //绑定本机所有IPserv_addr.sin_port = htons(SERV_PORT);   //绑定端口号,转换为网络序/* 本地进程绑定,地址信息+socket文件描述符 */bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));/* 设置socket同时能处理多少客户端链接 */listen(sfd, 20);clin_addr_len = sizeof(clin_addr);cfd = accept(sfd, (struct sockaddr *)&clin_addr, &clin_addr_len);while (1) {len = read(cfd, buf, sizeof(buf));//接收客户端发送过来的数据//模拟处理数据,小写转大写for (i = 0; i < len; i++) buf[i] = toupper(buf[i]);write(cfd, buf, len);//把处理后的数据发回客户端}close(cfd); //关闭和客户端链接的socketclose(sfd); //关闭accept监听用的socketreturn 0;
}


客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>#define SERV_PORT    8000
#define SERV_IP     "127.0.0.1"int main(int argc, char *argv[])
{struct sockaddr_in serv_addr;int cfd, len;char buf[1024];cfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); //serv_addr.sin_addr.s_addr = SERV_IP;serv_addr.sin_port = htons(SERV_PORT);connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));while (fgets(buf, sizeof(buf), stdin)) {write(cfd, buf, strlen(buf));len = read(cfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);}close(cfd);return 0;
}






本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496286.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET MVC V2 Preview 1 发布 期望VS有更好的表现

ASP.NET MVC V2 Preview 1官方首页&#xff1a;http://aspnet.codeplex.com/ 在这里可以下载 以下是网友的转载&#xff0c;介绍的还是比较详细的&#xff1a; 预览版是在.NET 3.5 SP1和VS 2008下工作的&#xff0c;可与ASP.NET MVC 1.0并行安装在同一个机器上&#xff08;即&a…

全球最权威的区块链行业报告

来源&#xff1a;腾讯研究院美国加密货币报道媒体CoinDesk近期发布“全球区块链现状报告”&#xff0c;深入研究了快速发展的加密货币行业及其底层技术&#xff0c;该报告覆盖了公共区块链、企业区块链、ICO、投资以及监管等话题&#xff0c;另外还对3000多名投资者的加密货币投…

Google 的 C++ 代码规范

Google 开源项目风格指南 (中文版)&#xff1a;https://zh-google-styleguide.readthedocs.io/en/latest/ 英文版&#xff1a;http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml 中文版&#xff1a;http://zh-google-styleguide.readthedocs.org/en/latest/goog…

NASA投资有远景技术,有望改变未来人类和机器人的勘探任务

来源&#xff1a; 机器人创新生态据NASA官网报道&#xff0c;美国宇航局(NASA)正在投资有远见的技术概念&#xff0c;包括流星撞击探测、太空望远镜群以及细小轨道碎片测绘技术&#xff0c;这些技术将来可能被用于未来的太空探索任务中。美国宇航局已经选出25个还处于早期的技术…

人工智能如何影响社会经济:关于近期文献的一个综述

作者&#xff1a;陈永伟 文章来源&#xff1a;经济学原理 相比于之前的历次技术进步&#xff0c;“人工智能革命”所引发的冲击更为巨大&#xff0c;其对经济学造成的影响也将更为广泛和深远。人工智能技术的突飞猛进&#xff0c;对经济社会的各个领域都产生了重大影响&#…

Spring Data JPA 从入门到精通~Naming命名策略详解及其实践

Naming 命名策略详解及其实践 用 JPA 离不开 Entity 实体&#xff0c;我都知道实体里面有字段映射&#xff0c;而字段映射的方法有两种&#xff1a; 显式命名&#xff1a;在映射配置时&#xff0c;设置的数据库表名、列名等&#xff0c;就是进行显式命名&#xff0c;即通过 C…

激光雷达:从光电技术角度看自动驾驶

来源&#xff1a; 传感器技术激光雷达和与之竞争的传感器技术&#xff08;相机、雷达和超声波&#xff09;加强了对传感器融合的需要&#xff0c;也对认真谨慎地选择光电探测器、光源和MEMS振镜提出了更高的要求。传感器技术、成像、雷达、光探测技术及测距技术&#xff08;激光…

socket的长连接、短连接、半包、粘包与分包

socket的半包&#xff0c;粘包与分包的问题和处理代码&#xff1a;http://blog.csdn.net/qq_16112417/article/details/50392463 知乎关于长连接和短连接&#xff1a;https://www.zhihu.com/search?typecontent&q长连接%20短连接 TCP网络通讯如何解决分包粘包问题&#…

2018年中国人工智能行业研究报告|附下载

来源&#xff1a;网络大数据、艾瑞咨询广义人工智能指通过计算机实现人的头脑思维所产生的效果&#xff0c;是对能够从环境中获取感知并执行行动的智能体的描述和构建;相对狭义的人工智能包括人工智能产业(包含技术、算法、应用等多方面的价值体系)、人工智能技术(包括凡是使用…

Spring Data JPA 从入门到精通~方法的查询策略设置

方法的查询策略设置 通过下面的命令来配置方法的查询策略&#xff1a; EnableJpaRepositories(queryLookupStrategy QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND) 其中&#xff0c;QueryLookupStrategy.Key 的值一共就三个&#xff1a; Create&#xff1a;直接根据方法名…

不用地图如何导航?DeepMind提出新型双路径强化学习「智能体」架构

来源&#xff1a;deepmind、arXiv作者&#xff1a;Piotr Mirowski、Matthew Koichi Grimes、Mateusz Malinowski、Karl Moritz Hermann、Keith Anderson、Denis Teplyashin、Karen Simonyan、Koray Kavukcuoglu、Andrew Zisserman、Raia Hadsell「雷克世界」编译&#xff1a;嗯…

C 和 C++ 宏 详解

From&#xff1a;https://www.cnblogs.com/njczy2010/p/5773061.html C中的预编译宏详解&#xff1a;http://www.cppblog.com/bellgrade/archive/2010/03/18/110030.html C语言的宏总结&#xff1a;http://blog.csdn.net/pirlck/article/details/51254590 C 语言中的 宏定义…

Spring Data JPA 从入门到精通~查询方法的创建

查询方法的创建 内部基础架构中有个根据方法名的查询生成器机制&#xff0c;对于在存储库的实体上构建约束查询很有用&#xff0c;该机制方法的前缀 find…By、read…By、query…By、count…By 和 get…By 从所述方法和开始分析它的其余部分&#xff08;实体里面的字段&#x…

人工智能在能源行业的5个应用

作者&#xff1a;CB Insights . 来源&#xff1a;CometLabs摘要&#xff1a;自2012年以来&#xff0c;把人工智能和能源产业放在一起进行报道的新闻开始增多。本文简要描述了人工智能在能源行业的5个应用方向&#xff0c;及对应的案例。能源行业会产生大量的数据。为了将这些数…

VMware 安装 win7、win10、MAC 和网络模式VMnet0、VMnet1、VMnet8解释

VMware虚拟机安装ghost win7系统方法&#xff1a;http://www.xitongcheng.com/jiaocheng/xtazjc_article_15314.html VMWare14 安装Mac OS系统&#xff08;图解&#xff09;&#xff1a;http://blog.csdn.net/u011415782/article/details/78505422 虚拟机&#xff08;VMware …

Spring Data JPA 从入门到精通~关键字列表

注意除了 find 的前缀之外&#xff0c;我们查看 PartTree 的源码&#xff0c;还有如下几种前缀&#xff1a; private static final String QUERY_PATTERN "find|read|get|query|stream"; private static final String COUNT_PATTERN "count"; private s…

当科学遇上众包:9个值得关注的前沿科技算力众包平台

来源&#xff1a; 资本实验室 . 作者&#xff1a;李鑫找到癌症治疗的方法&#xff0c;预测气候的变化&#xff0c;追踪可能与地球相撞的小行星……甚至预测地震&#xff0c;我们每天都面临着各种世界性难题。如果你想参与解决这些难题&#xff0c;公民科学应用将让你发挥作用…

htop 命令详解

htop 官网&#xff1a;http://htop.sourceforge.net/ Linux top 命令的用法详细详解&#xff1a;https://www.cnblogs.com/zhoug2020/p/6336453.html htop 使用详解&#xff1a;https://www.cnblogs.com/programmer-tlh/p/11726016.html 使用 yum 无法直接安装 htop&#xff…

linux主机服务器日志采集,Linux通过Rsyslog搭建集中日志服务器

(一)Rsyslog简介ryslog 是一个快速处理收集系统日志的程序&#xff0c;提供了高性能、安全功能和模块化设计。rsyslog 是syslog 的升级版&#xff0c;它将多种来源输入输出转换结果到目的地。rsyslog是一个开源工具&#xff0c;被广泛用于Linux系统以通过TCP/UDP协议转发或接收…

IDC预测2022年全球智能家居连接设备市场规模将达10亿台!

来源&#xff1a; IDC官网、智慧生活&#xff1b; 物联网资本论编译摘要&#xff1a;2017年&#xff0c;全球智能家居连接设备市场规模达到43310万台&#xff0c;比上一年增长27.6&#xff05;。2022年市场达到9.397亿台&#xff0c;IDC预计复合年增长率&#xff08;CAGR&#…