UNIX域套接字
socket同样可以用于本地通信 ,即主机内的进程间通信。创建套接字时使用本地协议AF_UNIX(或AF_LOCAL) 。
关于socket函数接口的介绍可参考我的另一篇博客:
链接 Linux网络基础介绍和编程socket函数介绍
UNIX主机内进程间通信的特点:
- 和其他进程间通信方式相比使用方便、效率更高
- 常用于前后台进程通信
UNIX套接字地址结构体
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX 协议族*/
char sun_path[108]; /* Pathname套接字文件的路径 */
};
填充地址结构体例子:
struct sockaddr_un myaddr;
bzero(&myaddr, sizeof(myaddr));
myaddr.sun_family = PF_UNIX;//或者写AF_UNIX
strcpy(myaddr.sun_path, “./mysocket”);
//这里需要关注一下“mysocket”这个文件。网络编程中,绑定的是ip和port,但是这里需要绑定的是socket类型的文件,如果在绑定之前,这个文件已经存在,则绑定会失败,因此,需要在bind之前和close之后,删除这个文件
//remove("./mysocket");
UNIX域套接字分为流式套接字和用户数据报套接字
1.流式套接字
UNIX域(流式)套接字服务器端:
socket–>bind–>listen–>accept–>recv/send–>close;
UNIX域(流式)套接字客户端:
socket–>connect–>send/recv–>close;
//流程和搭建TCP服务器和客户端几乎一样,只是协议族由IPv4换成UNIX协议族,以及地址结构体不同
补充:多线程中的子线程资源回收:线程分离函数
int pthread_detach(pthread_t thread)
线程分离状态:指定该状态,线程主动与主控线程断开关系。
功能:线程结束后(不会产生僵尸线程),其退出状态不由其他线程获取,而直接自己自动释放(自己清理掉PCB的残留资源)。网络、多线程服务器常用。而进程没有这一机制,所以进程重点需要处理僵尸进程
参数:线程id
返回值:成功0,失败错误号
pthread_self()可获取调用该函数的线程(轻量级进程)id。
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/un.h>
#include <pthread.h>
void *send_data(void *arg);
int main(int argc, const char *argv[])
{
remove("./mysocket");//确保套接字文件在创建前不存在
//1.创建套接字
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n",sockfd);
//2. 绑定套接字文件
struct sockaddr_un serveraddr = {0};
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,"./mysocket");
int len = sizeof(serveraddr);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len );
if(ret == -1)
{
perror("bind");
return -1;
}
//3.设置监听套接字
ret = listen(sockfd, 10);
if(ret == -1)
{
perror("listen");
return -1;
}
while(1)//循环服务器,解决客户端关闭导致服务器死循环或者直接退出的情况
{
printf("wait a client....\n");
//4.等待客户端连接, 返回一个新的套接字(connfd:收发数据)
//accept(sockfd, NULL, NULL);
int connfd = accept(sockfd,NULL,NULL);
if(connfd < 0)
{
perror("accept");
return -1;
}
pthread_t tid;
pthread_create(&tid,NULL,send_data,(void*)&connfd);//创建子线程,用于数据发送
pthread_detach(tid);
//收发数据
char buf[64] = {0};
while(1)
{
int m = recv(connfd, buf, 64,0);
if(m < 0)
{
perror("read");
return -1;
}
else if(m == 0) //客户端关闭
{
close(connfd);
break;
}
printf("message:%s\n", buf);
memset(buf, 0, 64); //数组清零
}
}
close(sockfd);
return 0;
}
void *send_data(void *arg)//服务器发送数据--子线程
{
pthread_detach(pthread_self());//在线程结束时自动释放线程资源
int nfd=*(int*)arg;
char buf[1024]={0};
while(1)
{
fgets(buf,1024,stdin);
buf[strlen(buf)-1]='\0';
send(nfd,buf,strlen(buf),0);
if(strcmp(buf,"quit")==0)break;
memset(buf,0,1024);
}
close(nfd);
}
客户端:
/*===============================================
* 文件名称:tcp_unix_cli.c
* 创 建 者:
* 创建日期:2022年08月18日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
void *recv_data(void *arg);
int main(int argc, const char *argv[])
{
//1.创建套接字
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
//2. 连接服务器(IP,PORT)
struct sockaddr_un serveraddr = {0};
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,"./mysocket");
int len = sizeof(serveraddr);
int ret = connect(sockfd, (struct sockaddr *)&serveraddr, len );
if(ret == -1)
{
perror("connect");
return -1;
}
pthread_t tid;
pthread_create(&tid,NULL,recv_data,(void*)&sockfd);//创建子线程,用于数据接收
pthread_detach(tid);
char buf[64] = {0};
while(1)
{
fgets(buf,64,stdin);
buf[strlen(buf)-1]='\0';
send(sockfd, buf, strlen(buf),0);
if(strcmp(buf,"quit")==0)break;
memset(buf, 0, 64); //数组清零
}
close(sockfd);
return 0;
}
void *recv_data(void *arg)
{
pthread_detach(pthread_self());//在线程结束时自动释放线程资源
int sockfd=*(int*)arg;
char buf[1024]={0};
while(1)
{
int n=recv(sockfd,buf,sizeof(buf),0);
if(n<=0)
{
perror("read");
return NULL;
}
printf("%s\n",buf);
memset(buf,0,sizeof(buf));
}
close(sockfd);
}
2.数据报套接字
UNIX域(数据报)套接字服务器端:
socket–>bind–>recvfrom/sendto–>close
UNIX域(数据报)套接字客户端:
socket–>recvfrom/sendto–>close
//流程和搭建UDP服务器和客户端几乎一样,只是协议族由IPv4换成UNIX协议族,以及地址结构体不同
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/un.h>
#include <pthread.h>
void *send_data(void *arg);
int main(int argc, const char *argv[])
{
remove("./mysocket");//确保套接字文件在创建前不存在
//1.创建套接字
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n",sockfd);
//2. 绑定套接字文件
struct sockaddr_un serveraddr = {0};
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,"./mysocket");
int len = sizeof(serveraddr);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len );
if(ret == -1)
{
perror("bind");
return -1;
}
char buf[64] = {0};
while(1)
{
int n = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL );
if(n == -1)
{
perror("recvfrom");
return -1;
}
printf("message:%s\n", buf);
memset(buf, 0, 64); //数组清零
}
close(sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
void *recv_data(void *arg);
int main(int argc, const char *argv[])
{
//1.创建套接字
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
//2. 服务器的地址结构体
struct sockaddr_un serveraddr = {0};
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,"./mysocket");
int len = sizeof(serveraddr);
char buf[64] = {0};
while(1)
{
fgets(buf,64,stdin);
buf[strlen(buf)-1]='\0';
sendto(sockfd, buf, strlen(buf),0,(struct sockaddr*)&serveraddr,len);
if(strcmp(buf,"quit")==0)break;
memset(buf, 0, 64); //数组清零
}
close(sockfd);
return 0;
}