网络协议(四)Socket编程之Unix

来源:互联网 发布:决战武林进阶数据地煞 编辑:程序博客网 时间:2024/06/05 15:47
Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。
1)Unix域套接字往往比通信两端位于同一主机的TCP套接字要快;
2)Unix域套接字可用于在同一主机上的不同进程之间传递描述符;
3)Unix域套接较新的实现把客户端的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。

Unix域中用于标识客户和服务端的协议地址是普通文件系统中的路径名。

<sys/un.h>struct sockaddr_un {sa_family_t sun_family;     char        sum_path[104]; /* null-terminated pathname */}
#include "../myunp.h"int main(int argc, char **argv){int sockfd;socklen_t len;struct sockaddr_un addr1, addr2;if (argc != 2) {printf("usage: unix bind <pathname>");return -1;}if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {printf("socket error: %s\n", strerror(errno));return -1;} unlink(argv[1]);  /* OK if this fails */memset(&addr1, 0, sizeof(addr1));addr1.sun_family = AF_LOCAL;strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path) - 1);if (bind(sockfd, (struct sockaddr *)&addr1, sizeof(addr1)) < 0) {printf("bind error: %s\n" strerror(errno));return -1;}len = sizeof(addr2);getsockname(sockfd, (struct sockaddr *)&addr2, &len);printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);return 0;}
(1)UDP
#include "../myunp.h"void UdpClient(int sockfd, const SA *pservaddr, socklen_t servlen){int n = 0;char sendline[MAXLINE] = {0};char recvline[MAXLINE + 1] = {0};socklen_t len = 0;struct sockaddr *preplyaddr = NULL;while (fgets(sendline, MAXLINE, stdin) != NULL) {if (sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen) < 0) {printf("sendto error: %s\n", strerror(errno));return;}len = servlen;if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, preplyaddr, &len)) < 0) {printf("recvfrom error: %s\n", strerror(errno));return;}recvline[n] = 0; /* null terminate */fputs(recvline, stdout);}}int main(int argc, char **argv){int sockfd;struct sockaddr_un servaddr;struct sockaddr_un cliaddr;if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {printf("sockfd error: %s\n", strerror(errno));return -1;}memset(&cliaddr, '\0', sizeof(cliaddr));cliaddr.sun_family = AF_LOCAL;strcpy(cliaddr.sun_path, tmpnam(NULL));/* bind an address for us */if (bind(sockfd, (SA *)&cliaddr, sizeof(cliaddr)) < 0) {printf("bind error: %s\n", strerror(errno));return -1;}memset(&servaddr, '\0', sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXDG_PATH);UdpClient(sockfd, (SA *)&servaddr, sizeof(servaddr));return 0;}
#include "../myunp.h"void UdpEcho(int sockfd, SA *pcliaddr, socklen_t clilen){int n = 0;socklen_t len = 0;char msg[MAXLINE] = {0};for ( ; ; ) {len = clilen;if ((n = recvfrom(sockfd, msg, MAXLINE, 0, pcliaddr, &len)) < 0) {printf("recvfrom error: %s\n", strerror(errno));return ;} if (sendto(sockfd, msg, n, 0, pcliaddr, len) < 0) {printf("sendto error: %s\n", strerror(errno));return ;}}}int main(int argc, int **argv){int sockfd;struct sockaddr_un servaddr;struct sockaddr_un cliaddr;if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {printf("socked error: %s\n", strerror(errno));return -1;}unlink(UNIXDG_PATH);memset(&servaddr, '\0', sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXDG_PATH);if (bind(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0) {printf("bind error: %s\n", strerror(errno));return -1;}UdpEcho(sockfd, (SA *)&cliaddr, sizeof(cliaddr));}
(2)TCP

#include "../myunp.h"#include <sys/un.h>void StrCli(FILE *fp, int sockfd){char sendline[MAXLINE] = {0};char recvline[MAXLINE] = {0};while (fgets(sendline, MAXLINE, fp) != NULL) {writen(sockfd, sendline, strlen(sendline));if (readline(sockfd, recvline, MAXLINE) == 0) {printf("StrCli: server terminated prematurely\n");return;}fputs(recvline, stdout);}}int main(int argc, char **argv){int sockfd;struct sockaddr_un servaddr;if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {printf("socket error\n");return -1;}memset(&servaddr, 0, sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXSTR_PATH);if ((connect(sockfd, (SA *)&servaddr, sizeof(servaddr))) < 0) {printf("connect error: %s\n", strerror(errno));return -1;}StrCli(stdin, sockfd); /* do it all */return 0;}
#include "../myunp.h"#include <sys/un.h>void sig_child(int signo){pid_t pid;int stat;while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {printf("child %d terminated\n", pid);}return;}void StrEcho(int sockfd){ssize_t n = 0;char buf[MAXLINE] = {0};for ( ; ; ) {while ((n = read(sockfd, buf, MAXLINE)) > 0) {if (writen(sockfd, buf, n) != n) {printf("writen error: %s\n", strerror(errno));return ;}}if (n < 0 && errno == EINTR) {continue;} else if (n < 0) {printf("read error: %s\n", strerror(errno));return ;}break;}}int main(){int listenfd;int connfd;int optval = 1;pid_t childpid;socklen_t clilen;struct sockaddr_un cliaddr;struct sockaddr_un servaddr;if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {printf("socket error: %s\n", strerror(errno));return -1;}/* Eliminates "Address already in use" error from bind */if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&optval, sizeof(optval)) < 0) {printf("setsockopt error\n");return -1;}unlink(UNIXSTR_PATH);memset(&servaddr, '\0', sizeof(servaddr));servaddr.sun_family = AF_LOCAL;strcpy(servaddr.sun_path, UNIXSTR_PATH);if (bind(listenfd, (SA *)&servaddr, sizeof(servaddr)) < 0) {printf("bind error: %s\n", strerror(errno));return -1;}if (listen(listenfd, LISTENQ) < 0) {printf("listen error: %s\n", strerror(errno));return -1;}signal(SIGCHLD, sig_child);for ( ; ; ) {clilen = sizeof(cliaddr);if ((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {if (errno == EINTR) {continue;      /* back to for */} else {printf("accept error: %s\n", strerror(errno));return -1;}}if ((childpid = fork()) == 0) { /* child process */close(listenfd); /* close listening socket */StrEcho(connfd); /* process the request */exit(0);}close(connfd);       /* parent closes connected socket */}}
可以使用Unix域在进程间传递描述符。
传递一个描述符并不是传递一个描述符号,而是涉及在接受进程中创建一个新的描述符,而这个新的描述符和发送进程中飞行(in flight)前的那个描述符指向内核中相同的文件表项。
通过执行另一个程序来打开文件的优势在于,另一个程序可以是一个setuid到root的程序,能够打开我们通常没有打开权限的文件。该程序能够把通常的Unxi权限概念(用户、用户组和其他用户)扩展到它想要的任何形式的访问检查。
#include "../myunp.h"int my_open(const char *, int);ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd);int main(int argc, char **argv){int fd, n;char buff[BUFFSIZE] = { 0 };if (argc != 2) {printf("usage: mycat <pathname>\n");return -1;}if ((fd = my_open(argv[1], O_RDONLY)) < 0) {printf("cannot open %s\n", argv[1]);return -1;}while ((n = read(fd, buff, BUFFSIZE)) > 0) {write(STDOUT_FILENO, buff, n);}exit(0);}int my_open(const char *pathname, int mode){int fd, status;int sockfd[2];pid_t childpid;char c;char argsockfd[10] = { 0 };char argmode[10] = { 0 };if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0) {printf("socketpair error: %s\n", strerror(errno));return -1;}if ((childpid = fork()) == 0) {  /* child process */close(sockfd[0]);snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);snprintf(argmode, sizeof(argmode), "%d", mode);execl("./openfile", "openfile", argsockfd, pathname, argmode, (char *)NULL);printf("execl error: %s\n", strerror(errno));return -1;}/* parent process - wait for the child to terminate */close(sockfd[1]); /* close the end we don't use */waitpid(childpid, &status, 0);if (WIFEXITED(status) == 0) {printf("child did not ternimate");}if ((status = WEXITSTATUS(status)) == 0) {read_fd(sockfd[0], &c, 1, &fd);} else {errno = status; /* set errno value from child's status */fd = -1;}close(sockfd[0]);return fd;}ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd){struct msghdr msg;struct iovec iov[1];ssize_t n;#ifdef HAVE_MSGHDR_MSG_CONTROLunion {struct cmsghdr cm;char control[CMSG_SPACE(sizeof(int))];} control_un;struct cmsghdr *cmptr;msg.msg_control = control_un.control;msg.msg_controllen = sizeof(control_un.control);#elseint newfd;msg.msg_accrights = (caddr_t)&newfd;msg.msg_accrightslen = sizeof(int);#endifmsg.msg_name = NULL;msg.msg_namelen = 0;iov[0].iov_base = ptr;iov[0].iov_len = nbytes;msg.msg_iov = iov;msg.msg_iovlen = 1;if ((n = recvmsg(fd, &msg, 0)) <= 0) {return n;}#ifdef HAVE_MSGHDR_MSG_CONTROLif ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {if (cmptr->cmsg_level != SOL_SOCKET) {printf("control level != SOL_SOCKET\n");return -1;}if (cmptr->cmsg_type != SCM_RIGHTS) {printf("control type != SCM_RIGHTS\n");return -1;}*recvfd = *((int *)CMSG_DATA(cmptr));} else {*recvfd = -1; /* descriptor was not passed*/}#elseif (msg.msg_accrightslen == sizeof(int)) {*recvfd = newfd;} else {*recvfd = -1; /* descriptor was not passed */}#endifreturn n;}
#include "../myunp.h"ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd);int main(int argc, char **argv){int fd;if (argc != 4) {printf("openfile <sockfd#> <filename> <mode>");return -1;}if ((fd = open(argv[2], atoi(argv[3]))) < 0) {exit((errno > 0) ? errno : 255);} if (write_fd(atoi(argv[1]), "", 1, fd) < 0) {exit((errno > 0) ? errno : 255);}exit(0);}ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd){struct msghdr msg;struct iovec iov[1];#ifdef HAVE_MSGHDR_MSG_CONTROLunion {struct cmsghdr cm;char control[CMSG_SPACE(sizeof(int))];} control_un;struct cmsghdr *cmptr;msg.msg_control = control_un.control;msg.msg_controllen = sizeof(control_un.control);cmptr = CMSG_FIRSTHDR(&msg);cmptr->cmsg_len = CMSG_LEN(sizeof(int));cmptr->cmsg_level = SOL_SOCKET;cmptr->cmsg_type = SCM_RIGHTS;*((int *)CMSG_DATA(cmptr)) = sendfd;#elsemsg.msg_accrights = (caddr_t)&sendfd;msg.msg_accrightslen = sizeof(int);#endifmsg.msg_name = NULL;msg.msg_namelen = 0;iov[0].iov_base = ptr;iov[0].iov_len = nbytes;msg.msg_iov = iov;msg.msg_iovlen = 1;return (sendmsg(fd, &msg, 0));}

1 0
原创粉丝点击