网编day5

网络聊天室

服务器代码

#include <iohead.h>

#define SER_PORT 8888       // 服务器端口号
#define SER_IP "192.168.78.128" // 服务器IP地址
#define MAX_CLIENTS 1024    // 最大客户端数量
#define USERNAME_SIZE 20    // 用户名最大长度

// 客户端信息结构体
typedef struct 
{
    int socket;             // 套接字描述符
    char username[USERNAME_SIZE]; // 用户名
    struct sockaddr_in addr; // 客户端地址信息
    int logged_in;          // 登录状态标志
} ClientInfo;


ClientInfo clients[MAX_CLIENTS];  // 客户端信息数组
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;  // 线程安全锁

// 广播消息给其他用户
void broadcast(const char* msg, int exclude_socket) 
{
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++)
	{
        if (clients[i].socket != -1 && clients[i].logged_in && clients[i].socket != exclude_socket) 
		{
            send(clients[i].socket, msg, strlen(msg), 0);
        }
    }
    pthread_mutex_unlock(&clients_mutex);
}

int main(int argc, const char* argv[]) 
{
    // 1. 创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) 
	{
        perror("socket error");
        return -1;
    }
    printf("socket成功 sfd = %d\n", sfd);

    // 设置套接字选项:允许端口号快速重用
    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) 
	{
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    // 2. 绑定地址和端口
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SER_PORT);
    sin.sin_addr.s_addr = inet_addr(SER_IP);

    if (bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1) 
	{
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    // 3. 监听连接
    if (listen(sfd, 128) == -1) 
	{
        perror("listen error");
        return -1;
    }
    printf("listen success\n");

    // 4. 初始化客户端信息数组
    for (int i = 0; i < MAX_CLIENTS; i++) 
	{
        clients[i].socket = -1;
        clients[i].logged_in = 0;
        memset(clients[i].username, 0, USERNAME_SIZE);
    }
    
    fd_set readfds, tempfds;
    FD_ZERO(&readfds);
    FD_SET(sfd, &readfds);  
    FD_SET(0, &readfds);    

    int maxfd = sfd;
    int new_fd;
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);

    while (1) 
	{
        tempfds = readfds;
        int res = select(maxfd + 1, &tempfds, NULL, NULL, NULL);
        if (res == -1) 
		{
            perror("select error");
            continue;
        }

        // 处理新客户端连接
        if (FD_ISSET(sfd, &tempfds)) 
		{
            new_fd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
            if (new_fd == -1) 
			{
                perror("accept error");
                continue;
            }

            // 将新客户端添加到集合
            FD_SET(new_fd, &readfds);
            if (new_fd > maxfd) maxfd = new_fd;
            
            // 初始化客户端信息
            pthread_mutex_lock(&clients_mutex);
            for (int i = 0; i < MAX_CLIENTS; i++) 
			{
                if (clients[i].socket == -1) 
				{
                    clients[i].socket = new_fd;
                    clients[i].addr = cin;
                    clients[i].logged_in = 0; // 等待用户名
                    break;
                }
            }
            pthread_mutex_unlock(&clients_mutex);
            
            printf("客户端[%s:%d]连接, fd=%d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), new_fd);
        }

        // 处理标准输入
        if (FD_ISSET(0, &tempfds)) 
		{
            char wbuf[128];
            scanf("%s", wbuf);
            printf("服务器消息: %s\n", wbuf);
            
            // 广播服务器消息
            char server_msg[256];
            snprintf(server_msg, sizeof(server_msg), "[服务器]: %s", wbuf);
            broadcast(server_msg, -1);
        }

        // 处理客户端消息
        for (int i = 0; i <= maxfd; i++) 
		{
            if (FD_ISSET(i, &tempfds) && i != sfd && i != 0) 
			{
                char rbuf[256];
                int res = recv(i, rbuf, sizeof(rbuf) - 1, 0);
                
                // 查找客户端信息
                ClientInfo *current_client = NULL;
                pthread_mutex_lock(&clients_mutex);
                for (int j = 0; j < MAX_CLIENTS; j++) 
				{
                    if (clients[j].socket == i) 
					{
                        current_client = &clients[j];
                        break;
                    }
                }
                pthread_mutex_unlock(&clients_mutex);
                
                if (!current_client) 
				{
                    close(i);
                    FD_CLR(i, &readfds);
                    continue;
                }
                
                if (res <= 0) 
				{  
					// 客户端断开
                    if (current_client->logged_in) 
					{
                        char leave_msg[256];
                        snprintf(leave_msg, sizeof(leave_msg), "【系统】%s 已下线\n", current_client->username);
                        broadcast(leave_msg, i);  // 广播下线消息
                    }
                    
                    pthread_mutex_lock(&clients_mutex);
                    printf("客户端 %s:%d (fd=%d) 断开连接\n", 
                           inet_ntoa(current_client->addr.sin_addr), 
                           ntohs(current_client->addr.sin_port), i);
                    
                    close(i);
                    FD_CLR(i, &readfds);
                    
                    // 清除客户端信息
                    current_client->socket = -1;
                    current_client->logged_in = 0;
                    memset(current_client->username, 0, USERNAME_SIZE);
                    
                    // 更新maxfd
                    if (i == maxfd) 
					{
                        for (int j = maxfd; j >= 0; j--) 
						{
                            if (FD_ISSET(j, &readfds)) 
							{
                                maxfd = j;
                                break;
                            }
                        }
                    }
                    pthread_mutex_unlock(&clients_mutex);
                } 
				else 
				{  
					// 正常消息
                    rbuf[res] = '\0';  // 确保字符串终止
                    
                    if (!current_client->logged_in) 
					{
                        // 第一次连接,接收用户名
                        strncpy(current_client->username, rbuf, USERNAME_SIZE - 1);
                        current_client->username[USERNAME_SIZE - 1] = '\0';
                        current_client->logged_in = 1;
                       
                        printf("用户 %s 登录 (fd=%d)\n", current_client->username, i);
                        
                        // 发送欢迎消息给新用户
                        char welcome_msg[256];
                        snprintf(welcome_msg, sizeof(welcome_msg), "【系统】欢迎 %s! 您已成功登录\n", current_client->username);
                        send(i, welcome_msg, strlen(welcome_msg), 0);
                        
                        // 广播上线消息给所有用户(包括自己)
                        char notice[256];
                        snprintf(notice, sizeof(notice), "【系统】%s 已上线\n", current_client->username);
                        broadcast(notice, -1);
                    } 
					else 
					{
                        // 格式化消息:用户名: 消息内容
                        char formatted_msg[512];
                        snprintf(formatted_msg, sizeof(formatted_msg), "%s: %s", current_client->username, rbuf);
                        
                        printf("%s\n", formatted_msg);
                        
                        // 广播给其他用户(不包括发送者)
                        broadcast(formatted_msg, i);
                    }
                }
            }
        }
    }

    close(sfd);
    return 0;
}

客户端代码

#include <iohead.h>

#define SER_PORT 8888        // 服务器端口号
#define SER_IP "192.168.78.128"   // 服务器IP地址
#define BUFFER_SIZE 1024


int cfd;
char username[20];  // 存储用户名

// 接收服务器消息的线程函数
void *recv_thread(void *arg) 
{
    char rbuf[BUFFER_SIZE];
    while (1) 
	{
        memset(rbuf, 0, BUFFER_SIZE);
        int res = recv(cfd, rbuf, BUFFER_SIZE, 0);
        if (res <= 0) 
		{
            printf("\n【系统】与服务器断开连接\n");
            exit(1);
        }
        printf("%s", rbuf);
    }
    return NULL;
}

int main(int argc, const char*argv[]) 
{
    // 1. 创建套接字
    cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == cfd) 
	{
        perror("socket error");
        return -1;
    }
    printf("socket success   cfd = %d\n", cfd);

    // 2. 连接服务器
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SER_PORT);
    sin.sin_addr.s_addr = inet_addr(SER_IP);

    if (connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1) 
	{
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");

    // 3. 输入用户名并发送给服务器
    printf("请输入用户名: ");
    fgets(username, sizeof(username), stdin);
    username[strlen(username) - 1] = '\0'; 
    send(cfd, username, strlen(username), 0);

    // 4. 创建接收消息线程
    pthread_t tid;
    pthread_create(&tid, NULL, recv_thread, NULL);
    pthread_detach(tid);  

    // 5. 主循环:发送消息
    char wbuf[BUFFER_SIZE];
    while (1) 
	{
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf) - 1] = '\0';  // 去除换行符
        
        if (strcmp(wbuf, "quit") == 0) 
		{
            printf("系统已退出\n");
            break;
        }
        
        send(cfd, wbuf, strlen(wbuf), 0);
    }

    // 6. 关闭套接字
    close(cfd);
    return 0;
}

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值