Linux应用开发之网络套接字编程(实例篇)

news2025/6/14 5:32:54

服务端与客户端单连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void * thread_write(void *argv)
{
    unsigned char * buf_sock_write;
    buf_sock_write = (unsigned char *)malloc(30);
    int client_fd = *((int *)argv);

    //以ctrl+D为结束信号,结束本端的写线程
    while (read(STDIN_FILENO,buf_sock_write,30))
    {
        send(client_fd,buf_sock_write,30,0);
        memset(buf_sock_write,0,30);
    }
    
    printf("服务端收到关闭信号\n");
    //关闭WR时,对端的recv会收到一个0字节的数据包,从而结束对端读线程,不同时关闭读端是为了接收对端的关闭信号
    //本端关闭写端,但是仍然可以接收数据包
    shutdown(client_fd,SHUT_WR);

    free(buf_sock_write);
}

void * thread_read(void *argv)
{
    unsigned char * buf_sock_read;
    buf_sock_read = (unsigned char *)malloc(30);
    int client_fd = *((int *)argv);

    //对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
    while (recv(client_fd,buf_sock_read,30,0))
    {
        printf("%s",buf_sock_read);
        memset(buf_sock_read,0,30);
    }

    printf("客户端断开连接\n");
    
    free(buf_sock_read);
}

int main(int argc, char const *argv[])
{
    int server_fd,tmp,client_fd;
    //创建服务端,客户端ip地址结构体
    struct sockaddr_in server,client;
    memset(&server,0,sizeof(server));
    memset(&client,0,sizeof(client));

    //1,创建服务端套接字并返回文件描述符,家族选择AF_INET(IPV4),使用TCP就选择SOCK_STREAM,UDP就选择SOCK_DGRAM
    server_fd = socket(AF_INET,SOCK_STREAM,0);

    //2,设置服务端的ip和端口号
    server.sin_family = AF_INET;
    inet_pton(AF_INET,"0.0.0.0",&server.sin_addr);
    server.sin_port = htons(8888);

    //3,将套接字绑定地址,我们使用的是IPV4专属的struct sockaddr_in结构体,需要强转为专用的struct sockaddr *
    tmp = bind(server_fd,(struct sockaddr *)&server,sizeof(server));
    handle_error("bind",tmp);

    //4,服务端进入监听模式,随时准备接受连接
    tmp = listen(server_fd,128);
    handle_error("listen",tmp);

    //5,服务端接受客户端的连接,同时会获取到客户端套接字的文件描述符和ip地址等信息,通过文件描述符和客户端进行通信
    socklen_t client_addr_len;
    client_fd = accept(server_fd,(struct sockaddr *)&client,&client_addr_len);
    printf("客户端连接成功\n");

    pthread_t thread_id_write;
    pthread_t thread_id_read;

    pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
    pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);

    pthread_join(thread_id_write,NULL);
    pthread_join(thread_id_read,NULL);

    close(server_fd);
    close(client_fd);

    return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void * thread_write(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_write = (unsigned char *)malloc(30);

    //客户端同样以ctrl+D为关闭信号,关闭写端
    while (read(STDIN_FILENO,buf_sock_write,30))
    {
        send(client_fd,buf_sock_write,30,0);
        memset(buf_sock_write,0,30);
    }

    printf("客户端收到关闭信号\n");
    //关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
    //本端关闭写端,但是仍然可以接收数据包
    shutdown(client_fd,SHUT_WR);

    free(buf_sock_write);
}

void * thread_read(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_read = (unsigned char *)malloc(30);

    //对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
    while (recv(client_fd,buf_sock_read,30,0))
    {
        printf("%s",buf_sock_read);
        memset(buf_sock_read,0,30);
    }

    printf("服务端关闭连接\n");
    
    free(buf_sock_read);
}

int main(int argc, char const *argv[])
{
    int client_fd,tmp;
    struct sockaddr_in server_addr,client_addr;
    memset(&server_addr,0,sizeof(server_addr));
    memset(&client_addr,0,sizeof(client_addr));

    //1,创建套接字
    client_fd = socket(AF_INET,SOCK_STREAM,0);
    handle_error("socket",client_fd);

    //2,设置服务端的ip地址和端口号
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配

    //3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号
    inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用
    tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));
    handle_error("bind",tmp);

    //4,连接服务端
    tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("connect",tmp);
    printf("成功连接到服务端\n");

    pthread_t thread_id_write;
    pthread_t thread_id_read;

    pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
    pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);

    pthread_join(thread_id_write,NULL);
    pthread_join(thread_id_read,NULL);

    close(client_fd);

    return 0;
}

过程及结果解释

        客户端和服务端代码均使用多线程,分别为读写线程,均通过终端接收数据并向对端发送数据,接收到对端的数据之后就会打印在终端上。在服务端/客户端关闭本端的写功能之后,都可以继续接收对端的数据,但是无法发送数据。

        在两个终端中分别运行服务端和客户端代码,要先运行服务端代码,不然客户端会连接不到。

         服务端发送一次hello csdn,客户端发送一次hello wrold

         关闭服务端,客户端继续发送数据

        关闭客户端 

 服务端基于多线程的支持多个客户端连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void *thread_recv_send(void *argv)
{
    int client_fd; 
    ssize_t cnt;
    client_fd = *((int *)argv);

    char *buf = (char *)malloc(100);

    while (cnt = recv(client_fd,buf,100,0))
    {
        handle_error("recv",cnt);
        cnt = send(client_fd,buf,cnt,0);
        handle_error("send",cnt);
        printf("客户端%d发来消息,内容为%s",client_fd,buf);
        memset(buf,0,100);
    }
    
    shutdown(client_fd,SHUT_WR);

    printf("有客户端断开连接,文件描述符为%d\n",client_fd);

    free(buf);
    close(client_fd);
}

int main(int argc, char const *argv[])
{
    int server_fd,client_fd,tmp;
    struct sockaddr_in server_addr,client_addr;

    //创建服务端套接字
    server_fd = socket(AF_INET,SOCK_STREAM,0);
    handle_error("socket",server_fd);

    //设置服务端ip地址和端口号
    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
    server_addr.sin_port = htons(8888);

    //绑定服务端套接字
    tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("bind",tmp);

    tmp = listen(server_fd,128);
    handle_error("listen",tmp);

    while (1)
    {
        socklen_t sock_len;
        client_fd = accept(server_fd,(struct sockaddr *)&client_addr,&sock_len);
        handle_error("accept",client_fd);

        printf("有客户端连接,ip地址为%s,端口号为%d,文件描述符为%d\n",
            inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);

        pthread_t thread_id_sock;
        pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);
        pthread_detach(thread_id_sock);
    }

    close(server_fd);
    return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void * thread_write(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_write = (unsigned char *)malloc(30);

    //客户端同样以ctrl+D为关闭信号,关闭写端
    while (read(STDIN_FILENO,buf_sock_write,30))
    {
        send(client_fd,buf_sock_write,30,0);
        memset(buf_sock_write,0,30);
    }

    printf("客户端收到关闭信号\n");
    //关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
    //本端关闭写端,但是仍然可以接收数据包
    shutdown(client_fd,SHUT_WR);

    free(buf_sock_write);
}

void * thread_read(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_read = (unsigned char *)malloc(30);

    //对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
    while (recv(client_fd,buf_sock_read,30,0))
    {
        printf("%s",buf_sock_read);
        memset(buf_sock_read,0,30);
    }

    printf("服务端关闭连接\n");
    
    free(buf_sock_read);
}

int main(int argc, char const *argv[])
{
    int client_fd,tmp;
    struct sockaddr_in server_addr,client_addr;
    memset(&server_addr,0,sizeof(server_addr));
    memset(&client_addr,0,sizeof(client_addr));

    //1,创建套接字
    client_fd = socket(AF_INET,SOCK_STREAM,0);
    handle_error("socket",client_fd);

    //2,设置服务端的ip地址和端口号
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配

    //3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号,如果固定端口号的话
    //多个线程无法共用一个端口,导致无法创建多连接
    /*inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用
    tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));
    handle_error("bind",tmp);*/

    //4,连接服务端
    tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("connect",tmp);
    printf("成功连接到服务端\n");

    pthread_t thread_id_write;
    pthread_t thread_id_read;

    pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
    pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);

    pthread_join(thread_id_write,NULL);
    pthread_join(thread_id_read,NULL);

    close(client_fd);

    return 0;
}

过程及结果解释

        客户端的代码几乎没有变化,只是取消了ip地址、端口号与套接字的绑定,因为客户端会默认设置本机的ip地址,并选择一个空闲端口使用,所以无需进行绑定。相反如果固定了端口号,因为端口号不能被多个进程共用,反而会导致无法创建多连接。

        服务端的接受连接的逻辑与之前相同,在每次接受连接之后都会创建一个新线程并传入服务端的套接字,并将线程设置为detach状态,使其在结束之后自动回收资源,不使用join,从而不会阻挡创立新连接。在新线程中维护与客户端的连接并进行数据传输,将服务端接收到的消息发送回客户端并打印到终端上。当客户端从终端中接收了ctrl+D之后,客户端退出,调用shutdown会向服务端发送挥手信号,此时recv接收到的数据长度为0,服务端也随之退出。

        打开三个终端,在终端1中运行服务端,终端2,3运行客户端

         终端2,3分别发送消息

         终端2,3退出

        可以看到此时客户端已经全部退出,服务端仍然在接收连接

        终端2,3继续运行客户端,与服务端建立连接 

 服务端基于多进程的支持客户端多连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void singal_handle(int sig)
{
    while (waitpid(-1,NULL,WNOHANG))
    {
        
    }
    
}

void recv_send(int client_fd,struct sockaddr_in * client_addr)
{
    ssize_t cnt;
    char *buf = malloc(30);
    while (cnt = recv(client_fd,buf,30,0))
    {
        send(client_fd,buf,cnt,0);
        printf("客户端%d发来消息,内容为%s",ntohs(client_addr->sin_port),buf);
        memset(buf,0,30);
    }

    printf("客户端%d断开连接\n",client_fd);

    shutdown(client_fd,SHUT_WR);
    free(buf);
}

int main(int argc, char const *argv[])
{
    int server_fd,client_fd,tmp;
    struct sockaddr_in server_addr,client_addr;

    server_fd = socket(AF_INET,SOCK_STREAM,0);
    handle_error("socket",server_fd);

    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
    server_addr.sin_port = htons(8888);

    tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("bind",tmp);

    tmp = listen(server_fd,128);
    handle_error("listen",tmp);

    //注册信号处理函数,调用waitpid回收子进程防止成为僵尸进程
    signal(SIGCHLD,singal_handle);

    while (1)
    {
        socklen_t client_len;
        client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
        handle_error("accept",client_fd);

        printf("有客户端建立连接,ip地址为%s,端口号为%d,文件描述符为%d\n",
            inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);

        pid_t pid = fork();
        if(pid == 0)//子进程
        {
            //子进程不需要使用服务端描述符
            close(server_fd);

            recv_send(client_fd,&client_addr);

            close(client_fd);
            exit(EXIT_SUCCESS);
        }
        else if(pid > 0)
        {
            //父进程不需要使用客户端描述符
            close(client_fd);
        }
    }
    


    return 0;
}

客户端代码

        客户端代码和多线程的相同。

运行结果及过程解释

        服务端通过fork创建多个进程来维护与客户端的连接,在子进程中执行数据传输,父进程阻塞等待客户端的连接。子进程在客户端断开之后调用exit退出进程,向父进程发送SIGCHLD信号,父进程通过注册信号处理函数调用waitpid来回收每一个子进程,防止出现僵尸进程。

        程序的运行结果和多线程的基本相同。开启三个终端,终端1执行服务端,2,3执行客户端并发送消息,之后关闭客户端

         和多线程不同的是,我们发现两个客户端的文件描述符是相同的,这是因为accept在父进程进行,数据传输的任务在子进程执行,父进程不需要使用客户端的文件描述符,所以已经将其关闭了,进而导致下一次返回的文件描述符相同。

UDP的数据传输

        UDP通讯也使用socket,但是接收和发送的函数与TCP不一样。由于UDP不存在握手这一步骤,所以在绑定地址之后,服务端不需要listen,客户端也不需要connect,服务端同样不需要accept。只要服务端绑定以后,就可以相互发消息了,由于没有握手过程,两端都不能确定对方是否收到消息,这也是UDP协议不如TCP协议可靠的地方。

        UDP的通讯流程相比之下就简单了很多,TCP是通过accept和connect绑定文件描述符进行通信,UDP则是通过目标地址的ip和端口号来通信,使用的是struct sockaddr结构体的信息。在接收对方的数据之后,就可以获得对方的ip地址和端口号等信息,进而向对方回传数据。

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

int main(int argc, char const *argv[])
{
    //UDP通讯过程中只涉及一个套接字就是本端套接字
    int sock_fd,tmp;
    char buf[30];
    struct sockaddr_in server_addr,client_addr;
    socklen_t client_len = sizeof(client_addr);

    sock_fd = socket(AF_INET,SOCK_DGRAM,0);
    handle_error("sock",sock_fd);

    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
    server_addr.sin_port = htons(8888);

    tmp = bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("bind",tmp);

    ssize_t cnt = 0;

    while (cnt = recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,&client_len))
    {
        printf("%s",buf);

        sendto(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,client_len);
        
        memset(buf,0,30);
    }

    printf("客户端退出\n");
    
    return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

int main(int argc, char const *argv[])
{
    int sock_fd,tmp;

    sock_fd = socket(AF_INET,SOCK_DGRAM,0);
    handle_error("socket",sock_fd);

    struct sockaddr_in server_addr;
    socklen_t server_len;

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);

    char buf[30];
    while (read(STDIN_FILENO,buf,30))
    {
        sendto(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,sizeof(server_addr));
        memset(buf,0,30);
        recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,&server_len);
        printf("%s",buf);
        memset(buf,0,30);
    }

    sendto(sock_fd,"",0,0,(struct sockaddr *)&server_addr,sizeof(server_addr));
    
    return 0;
}

        客户端必须先向服务端发送消息,之后服务端才可以获取到客户端的ip地址和端口号。运行结果如下

套接字用于进程间通信

        Socket编程原本是为了网络服务的,后来逐渐发展成一种进程间通信的方式:Unix Domain Socket IPC。它允许在同一台主机上运行的进程之间进行高效的数据传输,无需经过网络协议栈,因此具有低延迟和高性能的特点。通过文件系统中的特殊文件(通常是一个套接字文件),进程可以通过套接字(socket)来进行通信,实现双向的数据传输。

        和网络通信不同的是,UNIX通信域使用的结构体是struct sockaddr_un,结构体的sun_family固定使用AF_UNIX,第二个成员变为了套接字的地址,也就是声明一个使用本地文件的socket,其他进程可以绑定该文件完成进程间通信。在使用完成之后,服务端需要调用unlink清除该套接字文件,否则下次进行绑定时会提示该地址已经被使用。除此之外,服务端和客户端的程序编写流程和TCP并无区别。

        服务端代码

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void *thread_recv_send(void *argv)
{
    int client_fd; 
    ssize_t cnt;
    client_fd = *((int *)argv);

    char *buf = (char *)malloc(100);

    while (cnt = recv(client_fd,buf,100,0))
    {
        handle_error("recv",cnt);
        cnt = send(client_fd,buf,cnt,0);
        handle_error("send",cnt);
        printf("客户端%d发来消息,内容为%s",client_fd,buf);
        memset(buf,0,100);
    }
    
    shutdown(client_fd,SHUT_WR);

    printf("有客户端断开连接,文件描述符为%d\n",client_fd);

    free(buf);
    close(client_fd);
}

int main(int argc, char const *argv[])
{
    int server_fd,client_fd,tmp;
    struct sockaddr_un server_addr,client_addr;

    //创建套接字
    server_fd = socket(AF_UNIX,SOCK_STREAM,0);
    handle_error("socket",server_fd);

    //设置地址
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path,"test.socket");

    //绑定套接字
    tmp = bind(server_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    handle_error("bind",tmp);

    tmp = listen(server_fd,128);
    handle_error("listen",tmp);

    while (1)
    {
        socklen_t client_len = sizeof(client_addr);
        client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
        handle_error("accept",client_fd);

        printf("有客户端连接,文件描述符为%d\n",client_fd);

        pthread_t thread_id_sock;
        pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);
        pthread_detach(thread_id_sock);
    }
    
    unlink("test.socket");

    return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

#define handle_error(cmd,result)    \
        if(result < 0)              \
        {                           \
            perror(cmd);            \
            exit(EXIT_FAILURE);     \
        }                           \

void * thread_write(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_write = (unsigned char *)malloc(30);

    //客户端同样以ctrl+D为关闭信号,关闭写端
    while (read(STDIN_FILENO,buf_sock_write,30))
    {
        send(client_fd,buf_sock_write,30,0);
        memset(buf_sock_write,0,30);
    }

    printf("客户端收到关闭信号\n");
    //关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号
    //本端关闭写端,但是仍然可以接收数据包
    shutdown(client_fd,SHUT_WR);

    free(buf_sock_write);
}

void * thread_read(void *argv)
{
    int client_fd = *((int *)argv);
    unsigned char * buf_sock_read = (unsigned char *)malloc(30);

    //对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程
    while (recv(client_fd,buf_sock_read,30,0))
    {
        printf("%s",buf_sock_read);
        memset(buf_sock_read,0,30);
    }

    printf("服务端关闭连接\n");
    
    free(buf_sock_read);
}

int main(int argc, char const *argv[])
{
    int client_fd,tmp;
    struct sockaddr_un server_addr,client_addr;
    memset(&server_addr,0,sizeof(server_addr));
    memset(&client_addr,0,sizeof(client_addr));

    //创建套接字
    client_fd = socket(AF_UNIX,SOCK_STREAM,0);
    handle_error("socket",client_fd);

    //设置服务端的地址
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path,"test.socket");

    //连接服务端
    tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
    handle_error("connect",tmp);
    printf("成功连接到服务端\n");

    pthread_t thread_id_write;
    pthread_t thread_id_read;

    pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);
    pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);

    pthread_join(thread_id_write,NULL);
    pthread_join(thread_id_read,NULL);

    close(client_fd);

    return 0;
}

过程解释及运行结果

        服务端仍然采用多线程方式支持多个连接,接收客户端数据之后打印在终端并传回客户端。

         总结一下,套接字用于进程间通信时,类似于TCP协议下的编程顺序,但是套接字地址结构体变为了struct sockaddr_un 其中的family写为AF_UNIX,第二个成员改为了路径,客户端服务端的路径要一致。创建套接字的第二个参数还是sock_stream和TCP一样。服务端的编程顺序是初始化地址,给地址赋值,创建套接字,绑定套接字,进入监听模式,接受连接(会返回客户端的套接字和地址),发送/接收数据,unlink释放套接字。客户端的编程顺序是初始化地址,给地址赋值,创建套接字,连接客户端,发送/接收数据。

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

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

相关文章

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…