用C语言打造简单易用的聊天室——基于客户端/服务器(Client/Server)模型

一、服务器

简单来说就是个简单的Linux C程序,实现了读取一个命名管道的功能。下面是代码解析:

1、头文件

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

这些是头文件,用于提供各种系统调用函数、宏定义和数据类型的声明。

2、宏定义

#define FIFO_PATH "/tmp/mp_fifo"
#define MSG_LEN   50
#define EXIT_MASK "exit"

这里定义了三个宏,分别表示命名管道的路径、消息长度和退出标志。

3、结构体定义

struct msg_inf
{
    long process_id;//进程id
    char msg[MSG_LEN];
};

这里定义了一个结构体,包含进程id和消息。

4、判断有名管道是否存在

if(access(FIFO_PATH,F_OK))
{
    umask(0000);
    mkfifo(FIFO_PATH,0777);
}

这段代码判断命名管道是否存在,如果不存在,则调用mkfifo函数创建它。access函数可以检查文件或目录是否存在,并且可以检查当前用户是否具有读、写或执行权限。mkfifo函数用于创建一个命名管道。

5、打开有名管道

int fifo_fd = open(FIFO_PATH,O_RDWR);
if(fifo_fd == -1)
{
    perror("open");
    return -1;
}

这段代码打开命名管道并返回文件描述符。O_RDWR表示以读写方式打开,perror用于打印出错信息。

6、循环读取信息

struct msg_inf MI;
while(1)
{
    memset(&MI,0,sizeof(MI));
    read(fifo_fd,&MI,sizeof(MI));
    if(strcmp(EXIT_MASK,MI.msg) == 0)
    {
       printf("%ld下线了\n",MI.process_id);
    }
    else
    {
        printf("%ld:%s\n",MI.process_id,MI.msg);
    }
}

进入一个无限循环。循环中第一步是清空MI结构体,然后使用read函数从FIFO中读取消息,并将读取的内容存储到MI结构体中。

接着通过判断MI中的消息内容,来决定是输出下线提示还是打印接收到的消息内容。循环将一直运行,直到程序被手动结束。

7、关闭管道

close(fifo_fd);

下面是完整代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define FIFO_PATH "/tmp/mp_fifo"
#define MSG_LEN   50
#define EXIT_MASK "exit"
struct msg_inf
{
    long process_id;//进程id
    char msg[MSG_LEN];

};

int main()
{
    if(access(FIFO_PATH,F_OK))
    {
        umask(0000);
        mkfifo(FIFO_PATH,0777);
    }

    int fifo_fd = open(FIFO_PATH,O_RDWR);
    if(fifo_fd == -1)
    {
        perror("open");
        return -1;
    }

    //我想弄一个客户端链接服务器的机制 --- 链表保存
    
    struct msg_inf MI;

    while(1)
    {
        memset(&MI,0,sizeof(MI));
        read(fifo_fd,&MI,sizeof(MI));

        
        if(strcmp(EXIT_MASK,MI.msg) == 0)
        {
           printf("%ld下线了\n",MI.process_id);
        }
        else
        {
            printf("%ld:%s\n",MI.process_id,MI.msg);
        }
    }

    close(fifo_fd);

    return 0;
}

二、客户端:

用C语言程序实现了一个基于FIFO(命名管道)的客户端聊天室程序,以下是代码解析:

1、头文件

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

2、宏定义

#define FIFO_PATH "/tmp/mp_fifo"
#define MSG_LEN   50

#define EXIT_MASK "exit"

定义了FIFO文件路径、消息长度、退出消息的字符串。

3、定义结构体

struct msg_inf
{
    long process_id;
    char msg[MSG_LEN];
};

定义了消息结构体,包含进程ID和消息内容。

4、捕获信号的处理函数

void Fun(int num)
{
    struct msg_inf MI;
    memset(&MI,0,sizeof(MI));
    MI.process_id = getpid();
    strcpy(MI.msg,EXIT_MASK);

    write(tmp_fd,&MI,sizeof(MI));
    close(tmp_fd);
    exit(1);
}

这个函数在程序捕获到SIGINT信号(即Ctrl+C)时被调用。它向FIFO中写入一个包含进程ID和退出消息的结构体,告诉服务器客户端已经退出,并关闭FIFO文件描述符

5、主函数

int main()
{
    if(access(FIFO_PATH,F_OK))
    {
        umask(0000);
        mkfifo(FIFO_PATH,0777);
    }

    int fifo_fd = open(FIFO_PATH,O_RDWR);
    if(fifo_fd == -1)
    {
        perror("open");
        return -1;
    }

    tmp_fd = fifo_fd;
    signal(SIGINT,Fun);

    struct msg_inf MI;
    memset(&MI,0,sizeof(MI));
    MI.process_id = getpid();

    while(1)
    {
       memset(MI.msg,0,MSG_LEN);
       scanf("%s",MI.msg);
       write(fifo_fd,&MI,sizeof(MI));

       if(strcmp(EXIT_MASK,MI.msg) == 0)
       {
           printf("退出聊天室成功!\n");
           break;
       }      
    }

    close(fifo_fd);
    return 0;
}

首先通过access函数判断FIFO文件是否存在,如果不存在则使用umask和mkfifo创建它。然后打开FIFO文件并获得它的文件描述符,如果打开失败则输出错误信息并退出程序。

接着将FIFO文件描述符赋值给全局变量tmp_fd,以便在Fun函数中使用。然后注册SIGINT信号,并将Fun函数作为信号处理函数。

接下来定义一个消息结构体MI,将自己的进程ID赋值给它。然后进入循环,从标准输入中读取消息内容,将进程ID和消息内容写入FIFO文件中。

如果读取到的消息内容与退出消息相同,打印退出信息并退出循环。

最后关闭FIFO文件描述符,程序运行结束。

完整代码:

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define FIFO_PATH "/tmp/mp_fifo"
#define MSG_LEN   50

#define EXIT_MASK "exit"
int tmp_fd;

struct msg_inf
{
    long process_id;//进程id
    char msg[MSG_LEN];

};

void Fun(int num)
{
    struct msg_inf MI;
    memset(&MI,0,sizeof(MI));
    MI.process_id = getpid();
    strcpy(MI.msg,EXIT_MASK);

    write(tmp_fd,&MI,sizeof(MI));//告诉服务器我挂了

    close(tmp_fd);

    exit(1);
}

int main()
{
    if(access(FIFO_PATH,F_OK))
    {
        umask(0000);
        mkfifo(FIFO_PATH,0777);
    }

    int fifo_fd = open(FIFO_PATH,O_RDWR);
    if(fifo_fd == -1)
    {
        perror("open");
        return -1;
    }

    tmp_fd = fifo_fd;
    signal(SIGINT,Fun);
    //我想弄一个客户端链接服务器的机制 --- 链表保存
    
    struct msg_inf MI;
    memset(&MI,0,sizeof(MI));
    //先把自己的id赋值进结构体

    MI.process_id = getpid();

    while(1)
    {
       memset(MI.msg,0,MSG_LEN);
       scanf("%s",MI.msg);
       write(fifo_fd,&MI,sizeof(MI));

       if(strcmp(EXIT_MASK,MI.msg) == 0)
       {
           printf("退出聊天室成功!\n");
           break;
       }
      
    }


    close(fifo_fd);

    return 0;
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个简单客户端/服务器通信程序的C代码示例: 服务器端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define PORT 8888 #define MAXLINE 1024 int main(int argc, char *argv[]) { int server_fd, new_socket, valread; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[MAXLINE] = {0}; char *hello = "Hello from server"; // 创建套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字选项,允许地址重用 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定套接字到端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听套接字 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } // 接受新的连接请求 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } // 从客户端读取数据 valread = read(new_socket, buffer, MAXLINE); printf("Client: %s\n", buffer); // 向客户端发送消息 send(new_socket, hello, strlen(hello), 0); printf("Hello message sent\n"); return 0; } ``` 客户端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define PORT 8888 #define MAXLINE 1024 int main(int argc, char *argv[]) { int sock = 0, valread; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[MAXLINE] = {0}; // 创建套接字 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return -1; } memset(&serv_addr, '0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 将IPv4地址从点分十进制转换为二进制格式 if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported \n"); return -1; } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection Failed \n"); return -1; } // 向服务器发送消息 send(sock, hello, strlen(hello), 0); printf("Hello message sent\n"); // 从服务器读取响应 valread = read(sock, buffer, MAXLINE); printf("Server: %s\n", buffer); return 0; } ``` 这个程序中,服务器在本地监听端口8888,客户端连接到服务器后向其发送“Hello from client”消息,服务器接收到消息后回复“Hello from server”消息,客户端接收到服务器的消息并打印在屏幕上。你可以根据需要修改代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值