UNIX域套接字的网络编程实现

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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值