Linux下基于C语言的即时通讯软件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、基本功能
  • 二、两种实现方式
    • 1.通过数组实现
    • 2.通过结构体实现
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!


提示:以下是本篇文章正文内容,下面案例可供参考

一、基本功能

登录、群聊

二、两种实现方式

1.通过数组实现

代码如下(示例):
头文件:echo_server.h

#ifndef _ECHO_SERVER_H
#define _ECHO_SERVER_H

typedef struct buff
{
    int num; /*已连接数量*/
    int fd[128];
    int port[128];
} ReceiveBuf;
ReceiveBuf receive;

typedef struct login
{
    int type; /* 定义数据类型 */
    char name_buf[256];
    char message_buf[256];
} Login;
Login log_in;
#endif

服务器端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "echo_server.h"
#define NUM 1024
#define SERVER_PORT 4349
#define MESSAGE 2
#define LOGIN 1
//客户端之间的数据通信--使用数组

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int perror_exit(const char *des)
{

    fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
    // close(sock);
    exit(1);
}
void *communication(void *client_sock_num);

int main()
{
    //新建socket的文件描述符
    int sock;
    int bind_rec = 0;
    int listen_rec = 0;
    // socket地址
    struct sockaddr_in server_addr;
    //第一步socket函数,参数IPV4、tcp协议、默认协议
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        perror_exit("create socket");
    }
    //清空结构体
    bzero(&server_addr, sizeof(server_addr));
    //结构体传参,协议、ip地址、端口
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);
    int on = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    //绑定socket和addr
    bind_rec = bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (bind_rec == -1)
    {
        perror_exit("bind");
    }
    //声明socket处于监听状态
    listen(sock, 1024);
    if (listen_rec == -1)
    {
        perror_exit("listen");
    }
    //服务器信息搭建好,可以等待连接并读取数据
    printf("等待客户端连接!\n");

    while (1)
    {
        //声明客户端结构体
        struct sockaddr_in client;
        //声明接收连接返回值变量
        int client_sock;
        //声明读函数返回值
        int accept_num = 0;
        int count = 0;
        ssize_t recv_ret;

        //声明接收ip缓存区数组
        char client_ip[64];
        //声明读取数据缓存区
        socklen_t client_addr_len;
        client_addr_len = sizeof(client);

        memset(&receive, 0, sizeof(receive));

        while (1)
        {

            client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
            if (client_sock > 0)
            {
                printf("客户端已经连接!\n");
                printf("client ip:%s\t port:%d\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));
                receive.fd[receive.num] = client_sock;
                receive.port[receive.num] = ntohs(client.sin_port);
                printf("num:%d\n", receive.num);

                // recv_ret = recv(client_sock, client_name, sizeof(client_name) - 2, NULL);

                pthread_mutex_init(&mutex, NULL); //创建互斥锁

                pthread_t ReadThread; //
                pthread_create(&ReadThread, NULL, communication, (void *)&receive.fd[receive.num]);

                receive.num++;
                count++;
                printf("count:%d\n", count);
            }
            if (client_sock == -1)
            {
                perror_exit("accept");
            }
        }

        close(sock);
        return 0;
    }
}

/* 群发消息给其他人 */
void broadcast_msg(int fd, char *data, int len)
{
    int ret = 0;

    for (size_t i = 0; i < receive.num; i++)
    {
        /* 如果是自己 不要返回重复消息给自己 */
        if (fd == receive.fd[i])
        {
            continue;
        }

        /* code */
        ret = send(receive.fd[i], data, len, 0);
        if (ret < 0)
        {
            perror("send error");
        }

        puts("broadcase_msg success!");
    }
}

void *communication(void *p_client_sock)
{
    ssize_t recv_ret;
    int *client_sock = (int *)p_client_sock;
    char rec_buf[256];

    char rec_name_buf[256];
    int i;

    while (1)
    {
        recv_ret = recv(*client_sock, rec_buf, sizeof(rec_buf) - 1, NULL);
        if (recv_ret > 0)
        {
            // printf("%s Line %d:\n", __FILE__, __LINE__);

            // printf("接收到第一个元素:%d\n", rec_buf[0]);

            switch (rec_buf[0])
            {
            case 1:

                memcpy(rec_name_buf, rec_buf + 1, 15);
                rec_name_buf[15] = '\0';

                break;
            case 2:

                memcpy(rec_name_buf + 16, rec_buf + 16, 15);
                rec_name_buf[31] = '\0';

                printf("服务器收到信息%s %s\n", &rec_name_buf[0], &rec_name_buf[16]);

                pthread_mutex_lock(&mutex);
                broadcast_msg(*client_sock, rec_name_buf, sizeof(rec_name_buf));
                pthread_mutex_unlock(&mutex);
                break;
            default:
                break;
            }
            // sleep(1);
        }
    }
}

客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include "echo_server.h"

#define SERVER_PORT 4349
#define SERVER_IP "192.168.1.8"
#define MESSAGE 2
#define LOGIN 1

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//客户端之间的数据通信--使用数组
void text(int a, int b, int c);
int perror_exit(const char *des)
{

    fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
    // close(sock);
    exit(1);
}
void *client_communication(void *q_server_sock);
int main(int argc, char *argv[])
{
    //创建网络文件描述符
    int sockfd = 0;
    int server_sock = 0;
    int read_ret = 0;
    ssize_t send_ret;
    //创建发送指针
    char *message = NULL;
    char *message1 = NULL;
    //声明服务器ip数组
    char server_ip[128];
    //声明读取数组
    char namebuf[256];

    //声明socket结构体
    socklen_t server_addr_len;
    struct sockaddr_in server_addr;
    ReceiveBuf receive;
    int count = 0;

    //打开网络客户端通讯端口
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror_exit("socket");
        exit(1);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));

    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    server_addr.sin_port = htons(SERVER_PORT);

    server_sock = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if (server_sock < 0)
    {
        perror("connect");
        return 1;
    }

    namebuf[0] = LOGIN;
    printf("欢迎登录杠精艺术交流会聊天室,请输入姓名:\n");
    message = &namebuf[1];
    scanf("%s", message);
    send_ret = send(sockfd, namebuf, sizeof(namebuf), NULL);

    if (send_ret == -1)
    {
        perror_exit("send name");
        exit(1);
    }
    // memset(namebuf, 0, sizeof(namebuf));

    while (1)
    {
        pthread_mutex_init(&mutex, NULL); //创建互斥锁

        pthread_t client_ReadThread; //

        pthread_create(&client_ReadThread, NULL, client_communication, (void *)&sockfd);

        namebuf[0] = MESSAGE;
        message = &namebuf[16];

        scanf("%s", message);
        send_ret = send(sockfd, namebuf, sizeof(namebuf), NULL);

        if (send_ret == -1)
        {
            perror_exit("send");
            exit(1);
        }
    }
    close(sockfd);
    // return 0;
}

void *client_communication(void *q_server_sock)
{
    ssize_t recv_ret;
    int *server_sock = (int *)q_server_sock;
    char rec_buf[256];
    while (1)
    {
        recv_ret = recv(*server_sock, rec_buf, sizeof(rec_buf), 0);
        if (recv_ret > 0)
        {
            printf("%s %s\n", &rec_buf[0], &rec_buf[16]);
        }
        else if (recv_ret == -1)
        {
            perror_exit("recv");
            exit(1);
        }
    }
}

2.通过结构体实现

代码如下(示例):
服务器

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "echo_server.h"
#define NUM 1024
#define SERVER_PORT 4349
#define MESSAGE 2
#define LOGIN 1
//客户端之间的数据通信--使用结构体

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int perror_exit(const char *des)
{

    fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
    // close(sock);
    exit(1);
}
void *communication(void *client_sock_num);

int main()
{
    //新建socket的文件描述符
    int sock;
    int bind_rec = 0;
    int listen_rec = 0;
    // socket地址
    struct sockaddr_in server_addr;
    //第一步socket函数,参数IPV4、tcp协议、默认协议
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        perror_exit("create socket");
    }
    //清空结构体
    bzero(&server_addr, sizeof(server_addr));
    //结构体传参,协议、ip地址、端口
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);
    int on = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    //绑定socket和addr
    bind_rec = bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (bind_rec == -1)
    {
        perror_exit("bind");
    }
    //声明socket处于监听状态
    listen(sock, 1024);
    if (listen_rec == -1)
    {
        perror_exit("listen");
    }
    //服务器信息搭建好,可以等待连接并读取数据
    printf("等待客户端连接!\n");

    while (1)
    {
        //声明客户端结构体
        struct sockaddr_in client;
        //声明接收连接返回值变量
        int client_sock;
        //声明读函数返回值
        int accept_num = 0;
        int count = 0;
        ssize_t recv_ret;

        //声明接收ip缓存区数组
        char client_ip[64];
        //声明读取数据缓存区
        socklen_t client_addr_len;
        client_addr_len = sizeof(client);

        memset(&receive, 0, sizeof(receive));

        while (1)
        {

            client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
            if (client_sock > 0)
            {
                printf("客户端已经连接!\n");
                printf("client ip:%s\t port:%d\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));
                receive.fd[receive.num] = client_sock;
                receive.port[receive.num] = ntohs(client.sin_port);
                printf("num:%d\n", receive.num);

                // recv_ret = recv(client_sock, client_name, sizeof(client_name) - 2, NULL);

                pthread_mutex_init(&mutex, NULL); //创建互斥锁

                pthread_t ReadThread; //
                pthread_create(&ReadThread, NULL, communication, (void *)&receive.fd[receive.num]);

                receive.num++;
                count++;
                printf("count:%d\n", count);
            }
            if (client_sock == -1)
            {
                perror_exit("accept");
            }
        }

        close(sock);
        return 0;
    }
}

/* 群发消息给其他人 */
void broadcast_msg(int fd, char *data, int len)
{
    int ret = 0;

    for (size_t i = 0; i < receive.num; i++)
    {
        /* 如果是自己 不要返回重复消息给自己 */
        if (fd == receive.fd[i])
        {
            continue;
        }

        /* code */
        ret = send(receive.fd[i], data, len, 0);
        if (ret < 0)
        {
            perror("send error");
        }

        puts("broadcase_msg success!");
    }
}

void *communication(void *p_client_sock)
{
    ssize_t recv_ret;
    int *client_sock = (int *)p_client_sock;
    Login log_in_ser;

    char rec_name_buf[256];
    int i;

    while (1)
    {
        pthread_mutex_lock(&mutex);
        recv_ret = recv(*client_sock, &log_in_ser, sizeof(log_in_ser), NULL);
        if (recv_ret > 0)
        {
            // printf("%s Line %d:\n", __FILE__, __LINE__);

            // printf("接收到第一个元素:%d\n", rec_buf[0]);

            switch (log_in_ser.type)
            {
            case 1:
                break;
            case 2:

                broadcast_msg(*client_sock, &log_in_ser, sizeof(log_in_ser));
                // memcpy(rec_name_buf + 16, rec_buf + 16, 15);
                // receive.log_buf[31] = '\0';

                printf("服务器收到信息%s %s\n", log_in_ser.name_buf, log_in_ser.message_buf);

                break;
            default:
                break;
            }
            // sleep(1);
        }
        pthread_mutex_unlock(&mutex);
    }
}

客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include "echo_server.h"

#define SERVER_PORT 4349
#define SERVER_IP "192.168.1.8"
#define MESSAGE 2
#define LOGIN 1

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//客户端之间的数据通信--使用结构体
//创建结构体,将需要的不同数据类型的参数进行整理,将一段数据根据类型进行拆分,有利于代码实现的方便性
//指针为地址,所有函数的形参中有指针均可以传各种类型的地址
//与数组实现的好处:1、省去位置偏移量的考虑;2、省去客户端、服务器多次创建缓冲区数组;3、逻辑更容易辨识,增加可读性;
void text(int a, int b, int c);
int perror_exit(const char *des)
{

    fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
    // close(sock);
    exit(1);
}
void *client_communication(void *q_server_sock);
int main(int argc, char *argv[])
{
    //创建网络文件描述符
    int sockfd = 0;
    int server_sock = 0;
    int read_ret = 0;
    ssize_t send_ret;
    //创建发送指针
    char *message = NULL;
    char *message1 = NULL;
    //声明服务器ip数组
    char server_ip[128];
    //声明读取数组
    char namebuf[256];

    //声明socket结构体
    socklen_t server_addr_len;
    struct sockaddr_in server_addr;
    ReceiveBuf receive;
    int count = 0;

    //打开网络客户端通讯端口
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror_exit("socket");
        exit(1);
    }
    memset(&server_addr, 0, sizeof(struct sockaddr_in));

    server_addr.sin_family = AF_INET;
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    server_addr.sin_port = htons(SERVER_PORT);

    server_sock = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if (server_sock < 0)
    {
        perror("connect");
        return 1;
    }
    log_in.type = LOGIN;
    // log_in.name_buf[0] = log_in.type;
    printf("欢迎登录杠精艺术交流会聊天室,请输入姓名:\n");
    message = log_in.name_buf;
    scanf("%s", message);
    send_ret = send(sockfd, &log_in, sizeof(log_in), NULL);

    if (send_ret == -1)
    {
        perror_exit("send name");
        exit(1);
    }
    // memset(namebuf, 0, sizeof(namebuf));

    while (1)
    {
        pthread_mutex_init(&mutex, NULL); //创建互斥锁

        pthread_t client_ReadThread; //

        pthread_create(&client_ReadThread, NULL, client_communication, (void *)&sockfd);

        log_in.type = MESSAGE;
        // log_in.message_buf[0] = log_in.type;
        message = log_in.message_buf;

        scanf("%s", message);
        send_ret = send(sockfd, &log_in, sizeof(log_in), NULL);

        if (send_ret == -1)
        {
            perror_exit("send");
            exit(1);
        }
    }
    close(sockfd);
    // return 0;
}

void *client_communication(void *q_server_sock)
{
    ssize_t recv_ret;
    int *server_sock = (int *)q_server_sock;
    Login log_in_client;
    while (1)
    {
        pthread_mutex_lock(&mutex);

        recv_ret = recv(*server_sock, &log_in_client, sizeof(log_in_client), 0);
        if (recv_ret > 0)
        {
            printf("%s %s\n", log_in_client.name_buf, log_in_client.message_buf);
        }
        else if (recv_ret == -1)
        {
            perror_exit("recv");
            exit(1);
        }
        pthread_mutex_unlock(&mutex);
    }
}

使用结构体与数组实现总结:
创建结构体,将需要的不同数据类型的参数进行整理,将一段数据根据类型进行拆分,有利于代码实现的方便性。
指针为地址,所有函数的形参中有指针均可以传各种类型的地址。
使用结构体好处:1、省去位置偏移量的考虑;2、省去客户端、服务器多次创建缓冲区数组;3、逻辑更容易辨识,增加可读性。


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅实现登录及群聊功能,随学习深入增加更多功能。

你可能感兴趣的:(嵌入式,C语言,linux,linux,c语言,服务器)