Linux嵌入式操作系统-实验八 进程间通信——Socket(2)

本文详细介绍了如何使用Socket通信创建一个简单的聊天室系统,涉及服务器端的接收与广播、客户端的连接与消息交互,以及在线人数的统计。实验展示了进程间通信在多用户环境中的应用。
摘要由CSDN通过智能技术生成

一、实验目的

1、了解采用Socket通信的原理。

2、掌握Socket的创建及使用方法。

二、实验内容

      1、创建一个服务器端和若干个客户端。

    服务器端可实现包括:接收并区分来自客户端的数据,并将用户输入的内容群发至所有在线客户端(类似qq群聊形式);此外服务器可主动向在线客户端发送数据(类似qq通知信息);并可以统计在线人数等。

    客户端可实现包括:输入文字并且向服务器发送消息、接收来自服务器端的消息,发送和接收来自另一客户端的消息(类似qq私聊形式);用户控制客户端退出。

    2、在服务器端和客户端基于socket实现其通信过程。

3、 服务器与客户端的具体内容可根据实际工作情况发挥,体现原创精神即可。

需要:1)简述程序功能;2)具体的实现源代码及注释;3)实现的过程和结果截图。

三、实验具体分工

  

组内人员分别独立完成实验

  

四、实验步骤、源程序及结果截图

1、创建一个服务器端

源程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdbool.h>
#define NAME_SIZE               (20)//名字的最大字节数
#define CLIENT_COUNT    (50)//最大人数
#define BUF_SIZE                (4096)//接收消息的最大字节数


typedef struct Client

{

    int fd;

    struct sockaddr_in addr;

    char name[NAME_SIZE];

} Client;

Client clients[CLIENT_COUNT];//成员集合,存放进来聊天的人的描述符

int get_num(void)//统计当前人数

{

    int k = 0;

    for (int i = 0; i < CLIENT_COUNT; i++)

    {

        if (0 != clients[i].fd)

        {

            k++;

        }

    }

    return k;

}

Client* get_not_used(void)//遍历成员 查找当前人数是否满

{

    for (int i = 0; i < CLIENT_COUNT; i++)

    {

        if (0 == clients[i].fd)

        {

            return &clients[i];//找到空位置,没满,返回

        }

    }

return NULL;//满了

}

void send_all(Client* client, char* buf) // 群发消息

{

    char send_buf[BUF_SIZE + NAME_SIZE + 3]; // 姓名长度加上冒号、空格和消息内容

    sprintf(send_buf, "%s: %s", client->name, buf);

    for (int i = 0; i < CLIENT_COUNT; i++)

    {

        if (0 != clients[i].fd && client->fd != clients[i].fd)

        {

            write(clients[i].fd, send_buf, strlen(send_buf) + 1);

        }

    }

}

void send_one(Client* client, char* buf, char* toname) // 私聊

{

    char send_buf[BUF_SIZE + NAME_SIZE + 3]; // 姓名长度加上冒号、空格和消息内容

    sprintf(send_buf, "%s: %s(私聊消息)", client->name, buf);

    for (int i = 0; i < CLIENT_COUNT; i++)

    {

        if (0 == strcmp(clients[i].name, toname))

        {

            send(clients[i].fd, send_buf, strlen(send_buf) + 1,0);

        }

    }

}

void* server(void* arg)

{

    Client* client = arg;

    // 准备缓冲区

    char buf[BUF_SIZE];

    char bufone[BUF_SIZE];

    // 接收姓名

    read(client->fd, client->name, NAME_SIZE);

    // 拼接欢迎信息

sprintf(buf, "欢迎来自%s的%s!", inet_ntoa(client->addr.sin_addr), client->name);

    // 告诉所有客户端有人进入

    send_all(client, buf);

    printf("当前在线人数为:%d\n", get_num());

    // 返回欢迎信息

    send(client->fd, "欢迎你进入聊天室!", 31,0);

    for (;;)

    {

        char buf1[BUF_SIZE];

        int ret_size = read(client->fd, buf1, BUF_SIZE);

        if (0 >= ret_size)

        {

            sprintf(buf1, "%s掉线!", client->name);

            close(client->fd);

            client->fd = 0;

            send_all(client, buf1);

        }

        else if (0 == strcmp("退出聊天", buf1))

        {

            sprintf(buf1, "%s退出聊天室!", client->name);

            close(client->fd);

            client->fd = 0;

            printf("当前在线人数为:%d\n", get_num());

            send_all(client, buf1);

        }

        else if (0 == strcmp("私聊", buf1)) //给某人单独发消息

        {

            char toname[NAME_SIZE];

            char bufone[BUF_SIZE];

            read(client->fd, toname, NAME_SIZE);

            read(client->fd, bufone, BUF_SIZE);

            send_one(client, bufone, toname);

        }

        else

        {

            char send_buf[BUF_SIZE + 3]; // 姓名长度加上冒号、空格和消息内容

            sprintf(send_buf, "%s\n", buf1); // 使用sprintf格式化消息

            send_all(client, send_buf);

send_all(client, send_buf);

        }

    }

}

void* input_listener(void* arg)

{

    for (;;)

    {

        char input[5];

        fgets(input, 5, stdin);

        if (strstr(input, "send") != NULL)

        {

            printf("请输入要发送的消息:\n");

            char message[BUF_SIZE];

            fgets(message, BUF_SIZE, stdin);

            for (int i = 0; i < CLIENT_COUNT; i++)

            {

                if (0 != clients[i].fd)

                {

                    send(clients[i].fd, message, strlen(message) + 1,0);

                }

            }

        }

    }

}

int main()

{

    // 创建socket

    int svr_fd = socket(AF_INET, SOCK_STREAM, 0);

    if (0 > svr_fd)

    {

        perror("socket");

        return -1;

    }

    // 准备地址

    struct sockaddr_in addr = {};

    addr.sin_family = AF_INET;

    addr.sin_port = htons(6789);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    socklen_t addrlen = sizeof(addr);

    // 绑定

 if (bind(svr_fd, (struct sockaddr*)&addr, addrlen))

    {

        perror("bind");

        return -1;

    }

    // 监听

    if (listen(svr_fd, 10))

    {

        perror("listen");

        return -1;

    }

     // 创建输入监听线程

    pthread_t input_tid;

    pthread_create(&input_tid, NULL, input_listener, NULL);

    // 等待

    for (;;)

    {

        Client* client = get_not_used();//把新进来的人装进空位置

        if (NULL == client)

        {

            printf("客户端已满!\n");

            sleep(10);

            continue;

        }

        client->fd = accept(svr_fd, (struct sockaddr*)&client->addr, &addrlen);//连接

        if (0 > client->fd)

        {

            perror("accept");

            return 0;

        }

        // 创建服务线程

        pthread_t tid;

        pthread_create(&tid, NULL, server, client);



    }

}

2.创建客户端程序:

源代码:

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



#define NAME_SIZE 20 // 名字的最大字节数

#define CLIENT_COUNT 50 // 最大人数

#define BUF_SIZE 4096 // 接收消息的最大字节数



void* run(void* arg) {

    int cli_fd = *(int*)arg;

    char buf[BUF_SIZE];

    for (;;) {

        int ret_size = read(cli_fd, buf, sizeof(buf));

        if (ret_size <= 0) {

            printf("210753程闯提醒您:您的网络异常,请检查网络设置!\n");

            close(cli_fd);

            exit(1);

        } else {

            printf("\r%s\n>>", buf);

            fflush(stdout);

        }

    }

}



int main() {

    // 创建socket

    int cli_fd = socket(AF_INET, SOCK_STREAM, 0);

    if (cli_fd < 0) {

        perror("socket");

        return -1;

    }



    // 准备地址

    struct sockaddr_in addr;

    addr.sin_family = AF_INET;

 addr.sin_port = htons(6789); // 随意,保证客户端和服务端一样就行

    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 这里要修改成自己的ip



    // 连接

    if (connect(cli_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {

        perror("connect");

        return -1;

    }



    char buf[BUF_SIZE] = {};

    printf("210753程闯提醒您,请输入您的昵称:");

    fgets(buf, sizeof(buf), stdin);

    buf[strcspn(buf, "\n")] = '\0'; // 去除输入中的换行符

    write(cli_fd, buf, strlen(buf) + 1);



    // 创建接收线程

    pthread_t tid;

    pthread_create(&tid, NULL, run, &cli_fd);



    // 写数据并发送

    for (;;) {

        printf(">>");

        fflush(stdout);

        fgets(buf, sizeof(buf), stdin);

        buf[strcspn(buf, "\n")] = '\0'; // 去除输入中的换行符



        int ret_size = write(cli_fd, buf, strlen(buf) + 1);

        if (ret_size <= 0) {

            printf("210753程闯提醒您,您已掉线,请检查网络!\n");

            close(cli_fd);

            return 1;

        } else if (strcmp("退出聊天", buf) == 0) {

            printf("您已经退出聊天室!\n");

            close(cli_fd);

            return 0;

   } else if (strcmp("私聊", buf) == 0) {

            printf("请输入您要私聊的对象昵称:");

            char toname[NAME_SIZE];

                   fgets(toname, sizeof(toname), stdin);

            toname[strcspn(toname, "\n")] = '\0'; // 去除输入中的换行符

            write(cli_fd, toname, strlen(toname) + 1);



            printf("请输入您要发送到消息:");

            char bufone[BUF_SIZE];

            fgets(bufone, sizeof(bufone), stdin);

            bufone[strcspn(bufone, "\n")] = '\0'; // 去除输入中的换行符

            write(cli_fd, bufone, strlen(bufone) + 1);

       }

    }

}



主要功能实现:

客户可以在聊天室内发送消息,服务端将客户发送的消息广播到聊天室,凡是处于该聊天室的客户都可以接收到。

客户与客户之间可以进行私聊,只要客户输入需要私聊的对象名称,就可以进行私聊,除此之外的其他客户是看不到消息的。

用户可以自己控制是否继续聊天,输入退出聊天即可退出聊天室,并且聊天室内的其他用户都可以看到你退出的消息。

服务端可以以广播的形式发送消息,通知在该聊天室的所有用户

并且谁发送的信息都是有标记的,如果没有标记,只有提示符的话那就是服务端发送到广播消息。

接收并区分来自客户端的数据,并将用户输入的内容群发至所有在线客户端(类似qq群聊形式);此外服务器可主动向在线客户端发送数据(类似qq通知信息);并可以统计在线人数。

五、实验问题总结

        这个实验是我自己独立完成的,因为不同主机之间的通信仍然是进程间通信,所以用一台电脑,也可以实现的。Ip地址设置为本地ip:127.0.0.1,端口号设置一个没有被占用的就可以了。

        在本次实验中,探索了使用Socket进行进程间通信的方法。Socket是一种网络通信协议,可以在不同的主机之间传输数据。本次实验的目标是通过Socket实现两个进程之间的通信,并验证其可行性和效果。

        一个作为服务器端,负责监听来自客户端的连接请求;另一个作为客户端,负责向服务器端发送请求并接收响应。

        在服务器端创建了一个socket对象,并指定了IP地址和端口号。然后,我们通过调用bind()函数将socket对象绑定到指定的IP地址和端口上。接着,我们调用listen()函数开始监听来自客户端的连接请求。

        在客户端创建了一个socket对象,并指定了服务器端的IP地址和端口号。然后,我们调用connect()函数与服务器端建立连接。

        一旦连接建立成功,客户端可以向服务器端发送请求,服务器端则可以接收并处理这些请求。在本次实验中,我们简单地实现了服务器端向客户端发送一条欢迎消息,并接收客户端发送的字符串,并将其转换为大写形式返回给客户端。

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值