本地套接字使用 struct sockaddr_un ;
bind的时候仍然需要强转
创建套接字需要使用 AF_UNIX ,AF_UNIX, AF_LOCAL
创建套接字的type可以选择TCP,也可以选择UDP,但是如果选择TCP,那么必须按照TCP的流程进行通信,如果选择UDP,必须按照UDP的流程进行通信。
一, client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netinet/udp.h>
#define SERV_NAME "sock.c"
#define SERV_NAMES "sock.s"
int main(int argc, char *argv[]) {
int listenfd;
struct sockaddr_un servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
char buf[256] = { 0 };
int ret, i = 0;
//创建socket
listenfd = socket(AF_UNIX, SOCK_DGRAM, 0);
//绑定ip
unlink(SERV_NAME);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_NAME);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind error");
return -1;
}
bzero(&cliaddr, sizeof(cliaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_NAMES);
while (1) {
//发送数据
sprintf(buf, "陈丽 %d\n", i++);
sendto(listenfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
//接收数据
memset(buf, 0x00, sizeof(buf));
int ret = recvfrom(listenfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
if (ret > 0) {
printf("client 接收消息:%s\n", buf);
}
sleep(1);
}
close(listenfd);
return 0;
}
二,server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#define SERV_PORT 8888
#define SERV_NAME "sock.s"
int main(int argc, char *argv[])
{
int listenfd;
struct sockaddr_un servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
char buf[256] = { 0 };
int i, ret;
//创建socket
listenfd = socket(AF_UNIX, SOCK_DGRAM, 0);
//绑定本地套接字
unlink(SERV_NAME); //删除绑定套接字
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, SERV_NAME);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind error");
return -1;
}
while (1) {
//接收数据
ret = recvfrom(listenfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
if (ret > 0) {
printf("server :%s\n", cliaddr.sun_path);
sendto(listenfd, buf, strlen(buf), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
}
}
close(listenfd);
return 0;
}
三 , getsockname 函数
man 查看
SYNOPSIS
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
DESCRIPTION
getsockname() returns the current address to which the socket sockfd is bound, in the buffer pointed to by addr. The addrlen argument should be ini‐
tialized to indicate the amount of space (in bytes) pointed to by addr. 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 a value greater than was supplied to the call.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
ERRORS
EBADF The argument sockfd is not a valid descriptor.
EFAULT The addr argument points to memory not in a valid part of the process address space.
EINVAL addrlen is invalid (e.g., is negative).
ENOBUFS
Insufficient resources were available in the system to perform the operation.
ENOTSOCK
The file descriptor sockfd does not refer to a socket.
得到已经连接sockIP长度
//gcc -o getsocket getsocketname.c
//得到socket连接 IP地址长度
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <errno.h>
#include <fcntl.h> /* for nonblocking */
#include <sys/un.h> /* for Unix domain sockets */
#include <unistd.h> /* unlink */
// cmd -> ./getsocket chenli
// screen -> bound name = chenli, returned len = 9
int main(int argc, char *argv[])
{
int sockfd;
socklen_t len;
struct sockaddr_un addr1, addr2;
if (argc != 2)
{
printf("usage: unixbind <pathname>");
}
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
unlink(argv[1]); /* OK if this fails */
bzero(&addr1, sizeof(addr1));
addr1.sun_family = AF_LOCAL;
strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
bind(sockfd, (struct sockaddr *) &addr1, SUN_LEN(&addr1));
len = sizeof(addr2);
getsockname(sockfd, (struct sockaddr *) &addr2, &len);
printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
//exit(0);
return 0;
}
四, sockepair 父子进程通信
socketpair 是父子进程通信的一种:nginx使用master与worker进程之间的通信就是基于socketpair通信的
- 其实socketpair是基于pipe的通信的全双工的
- 父子进程之间的 “描述符传递”
- 流程- open, pipe,mkfifio, socket, accept
1, msghdr结构体
不幸,对于4.3BSD以及在其基础上构造的SunOS和Ultrix,以及从4.3BSD Reno开始的后续版本我们必须提供不同的实现。
为了交换文件描述符,调用sendmsg(2)和recvmsg(2)函数。这两个函数的参数中都
有一个指向msghdr的指针,该结构包含了所有关于要发送和接收消息的信息。该结
构定义在〈sys/socket.h〉 头文件中,在BSD4.3之下,其样式是:
strcut msghdr {
caddr_t msg_name; 可选的地址
int msg_namelen; 地址长度
struct iovec msg_iov; 散布/聚集数组
int msg_iovlen; 在msg_iov数组中的元素数
caddr_t msg_accrights; 存取权发送/接收到
int msg-accrightslen; 存取权缓存的长度
}
从4.3BSD Reno开始,更改了msghdr结构的定义。在以前版本中被称之为"存取权"
的最后两个元素改称为"辅助数据"。另外,在该结构结束处增加了一个新成员msg
_flags。
strcut msghdr {
caddr_t msg_name; 可选的地址
int msg_namelen; 地址长度
struct iovec msg_iov; 散布/聚集数组
int msg_iovlen; 在msg_iov数组中的元素数
caddr_t msg_control; 辅助数据
int msg-controllen; 辅助数据的长度
int msg_flags; 接收到消息的标志
}
我的内核查看
killer@localhost:~/socket$ uname -r
4.4.0-131-generic
实例程序
// gcc -o socketpair socketpair.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <errno.h>
#include <fcntl.h> /* for nonblocking */
#include <sys/un.h> /* for Unix domain sockets */
#include <unistd.h> /* unlink */
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#define BUFFSIZE 8192 /* buffer size for reads and writes */
/* define if struct msghdr contains the msg_control member */
#define HAVE_MSGHDR_MSG_CONTROL 1
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_CONTROL
union {
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);
#else
int newfd;
msg.msg_accrights = (caddr_t) &newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.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_CONTROL
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_RIGHTS)
err_quit("control type != SCM_RIGHTS");
*recvfd = *((int *) CMSG_DATA(cmptr));
} else
*recvfd = -1; /* descriptor was not passed */
#else
/* *INDENT-OFF* */
if (msg.msg_accrightslen == sizeof(int))
*recvfd = newfd;
else
*recvfd = -1; /* descriptor was not passed */
/* *INDENT-ON* */
#endif
return(n);
}
/* end read_fd */
ssize_t Read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
ssize_t n;
if ( (n = read_fd(fd, ptr, nbytes, recvfd)) < 0)
printf("read_fd error");
return(n);
}
int my_open(const char *pathname, int mode)
{
int fd, sockfd[2], status;
pid_t childpid;
char c, argsockfd[10], argmode[10];
socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
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");
}
/* 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 terminate");
}
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);
}
int main(int argc, char **argv)
{
int fd, n;
char buff[BUFFSIZE];
if (argc != 2)
{
printf("usage: mycat <pathname>");
}
//O_RDONLY 只读取
if ( (fd = my_open(argv[1], O_RDONLY)) < 0)
{
printf("cannot open %s", argv[1]);
}
while ( (n = read(fd, buff, BUFFSIZE)) > 0)
{
write(STDOUT_FILENO, buff, n);
}
//exit(0);
return 0;
}