网络通讯项目源码

人工智能22-4 吴彦组.zip

编译

gcc chat_server.c(服务端) -o server(服务端可执行文件) -pthread

临时升级 系统 gcc 的版本

sudo yum install centos-release-scl
 
sudo yum install devtoolset-9-gcc*
 
scl enable devtoolset-9 bash

服务端源码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <pthread.h>
#include <time.h> // 用于时间函数

// 全局文件指针
FILE *fp;

// 结构体,用于保存用户信息
struct User {
    char ip[INET_ADDRSTRLEN]; // IP地址字符串
    char username[50];        // 用户选择的用户名
    int connfd;               // 连接套接字描述符
};

// 数组,用于存储注册用户(为简单起见,考虑使用动态数据结构以提升可扩展性)
struct User registeredUsers[100]; // 假设最多100个用户

// 注册用户计数
int numRegisteredUsers = 0;

// 写用户信息到文件的函数
void writeUserToFile(struct User user) {
    FILE *file = fopen("registered_users.txt", "a"); // 'a' 追加模式
    if (file == NULL) {
        perror("文件打开");
        return;
    }
    fprintf(file, "IP: %s, 用户名: %s\n", user.ip, user.username);
    fclose(file);
}

// 发送私聊消息的函数
void sendPrivateMessage(char* sender, char* receiver, char* message) {
    // 查找接收者的连接套接字描述符
    int receiverConnfd = -1;
    for (int i = 0; i < numRegisteredUsers; i++) {
        if (strcmp(registeredUsers[i].username, receiver) == 0) {
            receiverConnfd = registeredUsers[i].connfd;
            break;
        }
    }

    // 如果找到接收者,发送私聊消息
    if (receiverConnfd != -1) {
        char privateMessage[BUFSIZ];
        sprintf(privateMessage, "[私聊] %s 对你说: %s\n", sender, message);
        send(receiverConnfd, privateMessage, strlen(privateMessage), 0);
    } else {
        printf("找不到用户 %s\n", receiver);
    }
}

// 处理客户端连接的函数
void* handleClientConnection(void *arg) {
    int connfd = *(int*)arg;
    free(arg);
    
    struct sockaddr_in client;
    socklen_t clientLength = sizeof(client);
    
    if (getpeername(connfd, (struct sockaddr*)&client, &clientLength) == -1) {
        perror("获取客户端地址失败");
        close(connfd);
        return NULL;
    }

    char buf[BUFSIZ];
    char timestamp[20]; // 时间戳缓冲区
    char username[50];  // 用户名缓冲区

    // 提示用户输入用户名
    sprintf(buf, "请输入您的用户名: ");
    send(connfd, buf, strlen(buf), 0);

    // 从客户端接收用户名
    int receivedBytes = recv(connfd, username, sizeof(username) - 1, 0);
    if (receivedBytes <= 0) {
        perror("接收用户名错误");
        close(connfd);
        return NULL;
    }
    username[receivedBytes] = '\0'; // 添加字符串结束符

    // 存储用户信息并写入文件
    strcpy(registeredUsers[numRegisteredUsers].ip, inet_ntoa(client.sin_addr));
    strcpy(registeredUsers[numRegisteredUsers].username, username);
    registeredUsers[numRegisteredUsers].connfd = connfd; // 存储连接套接字描述符
    writeUserToFile(registeredUsers[numRegisteredUsers]);
    numRegisteredUsers++;

    int registered = 0; // 标志位,指示用户是否已注册

    while (!registered) {
        // 检查用户是否已注册
        for (int i = 0; i < numRegisteredUsers; i++) {
            if (strcmp(registeredUsers[i].username, username) == 0) {
                registered = 1;
                break;
            }
        }
    }

    while (1) {
        bzero(buf, sizeof(buf));
        int receivedBytes = recv(connfd, buf, sizeof(buf), 0);
        if (receivedBytes == -1) {
            perror("服务器接收错误");
                    } 
        else if (receivedBytes == 0) {
            printf("客户端已断开连接\n");
            break;
        } 
        else {
            // 判断是否为私聊消息
            if (buf[0] == '@') {
                char* receiver = strtok(buf + 1, " "); // 提取接收者用户名(跳过@符号)
                char* message = strtok(NULL, ""); // 提取私聊消息内容
                if (receiver != NULL && message != NULL && strtok(NULL, "") == NULL) {
                    sendPrivateMessage(username, receiver, message); // 发送私聊消息
                } else {
                    printf("私聊消息格式不正确\n");
                    send(connfd, "私聊消息格式不正确\n", strlen("私聊消息格式不正确\n"), 0);
                }
            } else {
                // 获取当前时间
                time_t now = time(NULL);
                struct tm *timeInfo = localtime(&now);
                char timestamp[20];
                strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", timeInfo);

                // 输出到控制台
                printf("[%s] %3s (%s): %s\n", timestamp, username, inet_ntoa(client.sin_addr), buf);

                // 写入带时间戳的文件
                fprintf(fp, "[%s] %3s (%s): %s\n", timestamp, username, inet_ntoa(client.sin_addr), buf);
                fflush(fp); // 确保立即写入数据
            }
        }
    }
    close(connfd);
    return NULL;
}

int main() {
    int tcpsocket = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpsocket == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(9999);
    server.sin_addr.s_addr = inet_addr("172.19.10.249");

    int ret = bind(tcpsocket, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1) {
        perror("bind");
        return -1;
    }

    ret = listen(tcpsocket, 32);
    if (ret == -1) {
        perror("listen");
        return -1;
    }

    struct sockaddr_in client;
    int len = sizeof(client);

    // Open the file for writing
    fp = fopen("messages.txt", "a"); // 'a' append mode
    if (fp == NULL) {
        perror("file open");
        return -1;
    }

    while (1) {
        int connfd = accept(tcpsocket, (struct sockaddr *)&client, &len);
        if (connfd == -1) {
            perror("接受");
            return -1;
        }
        printf("客户端: IP - %s, 端口 - %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        pthread_t thread_id;
        int *pconnfd = (int *)malloc(sizeof(int));
        *pconnfd = connfd;

        if (pthread_create(&thread_id, NULL, handleClientConnection, (void *)pconnfd) != 0) {
            perror("无法创建线程");
            free(pconnfd);
            close(connfd);
            continue;
        }

        pthread_detach(thread_id);
    }

    close(tcpsocket);
    fclose(fp); // Close the file when done

    return 0;
}