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

27 篇文章 5 订阅
15 篇文章 0 订阅

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


前言

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

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


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

一、基本功能

登录、群聊

二、两种实现方式

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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>

#include <stdlib.h>
#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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>

#include <stdlib.h>
#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、逻辑更容易辨识,增加可读性。


总结

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

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于C语言的聊天软件设计需要考虑以下几个方面:用户界面设计、通信协议选择、数据传输方式以及数据加密与安全性。 首先,用户界面设计要简洁易用,方便用户进行登录、注册和聊天等操作。可以使用C语言的图形库如SDL或者基于命令行的界面。通过添加输入框和显示框等组件,用户可以输入聊天内容并实时接收他人发送的消息。 其次,通信协议选择要根据实际需求和服务器的支持情况来确定。常见的选择有TCP/IP协议和UDP协议。TCP/IP协议可确保可靠的数据传输,但速度相对较慢;而UDP协议则具有快速传输的优势,但会有数据丢失的风险。根据具体情况进行选择。 再次,数据传输方式可以基于Socket编程来实现。使用C语言的Socket库函数,通过建立与服务器的连接,实现消息的发送和接收。可以使用多线程来处理服务器和客户端之间的通信,确保并发处理多个客户端的请求。 最后,为了保证聊天数据的安全性,可以采用加密算法对消息进行加密。常用的加密算法有对称加密和非对称加密,如AES和RSA。对称加密速度较快,但需要共享秘钥;而非对称加密则无需共享秘钥,但速度相对较慢。可以根据实际需求选择合适的加密算法。 综上所述,基于C语言的聊天软件设计需要考虑用户界面设计、通信协议选择、数据传输方式以及数据加密与安全性等方面。通过合理的设计和实现,可以开发出功能完善、稳定可靠的聊天软件

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值