网络编程项目实战之TCP聊天室

本文详细介绍了使用C语言实现的一个服务器端多线程网络聊天程序,包括服务器端代码的结构、客户端连接处理以及消息广播功能。
摘要由CSDN通过智能技术生成
/*
*
*
*			服务器端代码实现
*
*
*
*/
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <pthread.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <sys/socket.h>  
#include <errno.h>
#define MAX_CLIENTS 20  
#define BUFFER_SIZE 100  
  
typedef struct {  
    int fd;  
    char name[20]; 
    char buff[BUFFER_SIZE];  
} MSG;  
  
int fds[MAX_CLIENTS];  
void service(int a);
void *service_thread(void *arg);  
void sendtoall(int this_fd, const char *name, const char *message);  
  
int main() {  
    int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("sock error :");
		return -1;
	}
	struct sockaddr_in myaddr;
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(6666);
	myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	int ret_bind;
	ret_bind = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr));
	if(ret_bind < 0)
	{
		perror("bind error :");
		return -1;
	}
	int ret_listen;
	ret_listen = listen(sockfd,20);
	if(ret_listen < 0)
	{
		perror("listen error :");
		return -1;
	}
	printf("listen .......\n");
	
	service(sockfd);
	close(sockfd);
    return 0;  
}  
  
void service(int a) {  
    int sockfd = a;
	printf("服务器等待客户链接...\n"); 
    while (1) {  
        char buf[100] = {0};
		struct sockaddr_in cliaddr;
		memset(&cliaddr,0,sizeof(cliaddr));
		int addrln = sizeof(cliaddr);
		int connfd;
		connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&addrln);
		if(connfd < 0)
		{
			perror("accept error :");
			exit(-1);
		}
		printf("accept成功\n");
        if (recv(connfd, buf, sizeof(buf), 0) < 0) 
		{  
            perror("accept recv error");  
            close(connfd); // 关闭连接  
            continue; // 继续等待下一个连接  
        }  
          
        int i;  
		//将文件描述符放入坚听队列
        for (i = 0; i < MAX_CLIENTS; i++) 
		{  
            if (fds[i] == 0) {  
                fds[i] = connfd;  
                break;  
            }  
        }  
          //如果监听完 i 为最大值那么就是满了
        if (i == MAX_CLIENTS) 
		{  
            printf("聊天室已满...\n");  
            close(connfd);  
            continue;  
        }  
          
        // 动态分配 MSG 结构体并初始化  
        MSG *msg = (MSG *)malloc(sizeof(MSG));  
        msg->fd = connfd;  
        strncpy(msg->name, buf, sizeof(msg->name) - 1); // 复制用户名,确保不会溢出  
        msg->name[sizeof(msg->name) - 1] = '\0'; // 确保字符串以 null 结尾  
        printf("用户:%s 进入聊天\n", msg->name);  
        sendtoall(connfd, msg->name, "已加入聊天");
        // 创建线程处理客户端请求,并传递 MSG 结构体的地址  
        pthread_t tid;  
        if (pthread_create(&tid, NULL, service_thread, (void *)msg) != 0) 
		{  
            perror("pthread_create error");  
            free(msg);  
            close(connfd); 
            continue; // 继续等待下一个连接  
        }  
    }  
    
}  
  
void *service_thread(void *arg) {  
    MSG *msg = (MSG *)arg;  
    int fd = msg->fd;  
    char buffer[BUFFER_SIZE];  
    int ret;  
      
    while ((ret = recv(fd, buffer, sizeof(buffer), 0)) > 0) 
	{  
        buffer[ret] = '\0'; // 确保字符串以 null 结尾  
        sendtoall(fd, msg->name, buffer); // 发送消息给所有客户端(除了自己)  
    }  
      
    if (ret == 0 || errno != EINTR) 
	{ // 连接已关闭或出错  
        int i;  
        for (i = 0; i < MAX_CLIENTS; i++) {  
            if (fds[i] == fd) {  
                fds[i] = 0; 
                break;  
            }  
        }  
        printf("用户:%s 退出聊天\n", msg->name);  
        sendtoall(fd, msg->name, "已退出聊天");   
    }  
      
    free(msg); // 释放内存 
    close(fd); // 关闭连接 
    pthread_exit(NULL);  
}  
  
void sendtoall(int this_fd, const char *name, const char *message) 
{  
    int i;  
    for (i = 0; i < MAX_CLIENTS; i++) {  
        if (fds[i] != 0 && fds[i] != this_fd) { // 发送给所有客户端(除了自己)  
            char send_buffer[BUFFER_SIZE + 20]; // 分配足够的空间来存储用户名和消息 
            snprintf(send_buffer, sizeof(send_buffer), "[%s]: %s", name, message);  
            send(fds[i], send_buffer, strlen(send_buffer), 0);   
        }  
    }  
}

客户端代码实现:

/*               
		客户端代码             
  */


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

void start(int sockfd,char *name,int len);  
void *recv_thread(void *p);  
char name[20];
int main() {  
	int sockfd;  
	sockfd = socket(AF_INET, SOCK_STREAM, 0);  
	if (sockfd < 0) {  
		perror("socket error:");  
		return -1;  
	}  

	struct sockaddr_in addr; 
       memset(&addr,0,sizeof(addr));	
	addr.sin_family = AF_INET;  
	addr.sin_port = htons(6666);  
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");  

	int ret_connect;  
	ret_connect = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));  
	if (ret_connect < 0) {  
		perror("connect error:");  
		close(sockfd);   
		return -1;  
	}  

	printf("启动服务.....\n");  
	char name[20]; 
	int num;
	printf("你的名字是:");  
	fgets(name, sizeof(name), stdin);
	
	start(sockfd,name,20);   
	return 0;  
}  

void start(int sockfd,char *name,int len) { 
	pthread_t pid;  
	printf("%s",name);
	pthread_create(&pid, NULL, recv_thread, (void *)&sockfd); // 传递sockfd的地址给线程函数  
	send(sockfd, name, strlen(name), 0);  
	
	while (1) {  
		char buf[100];  
		//printf("请输入发送内容:\n");  
		fgets(buf, sizeof(buf), stdin);  
		buf[strcspn(buf, "\n")] = 0;
		if (strcmp(buf, "bye") == 0) {
			break;
		}
		send(sockfd, buf, strlen(buf), 0); // 只发送实际的字符串长度
	}
	close(sockfd); 
	pthread_join(pid, NULL); 

}
void *recv_thread(void *p) { 
	int sockfd = *(int *)p; 
	while (1) {
		char buf[100]; 
		int ret_recv = recv(sockfd, buf, sizeof(buf), 0); 
		if (ret_recv <= 0) { 
			break; 
		} else { 
			buf[ret_recv] = '\0'; // 确保字符串以null结尾  
			buf[strcspn(buf, "\n")] = 0;
			//write(STDOUT_FILENO, "你:", 3);
            write(STDOUT_FILENO, buf, ret_recv); // 写入接收到的消息  
            write(STDOUT_FILENO, "\n", 1); 
		}
	}
	return NULL;
}

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言网络编程聊天室是一个基于TCP/IP协议的聊天程序,可以在Linux操作系统上使用。它可以通过socket和多线程实现,也可以使用UDP或epoll来处理高并发。该聊天室可以支持多个客户端与服务器进行实时通信,并允许用户加入和退出不同的聊天室。 实现C语言网络编程聊天室的主要步骤包括: 1. 需求分析:确定聊天室的功能需求。 2. 学习TCP/IP协议:理解C/S模型、socket编程的常规步骤以及阻塞与非阻塞socket等概念。 3. 文件操作和数据库:学习如何进行文件操作和数据库的操作,以便存储聊天记录和用户信息等。 4. 实现思路:考虑如何设计服务器和客户端之间的通信方式,以及如何处理多个客户端的并发连接。 5. 编写代码:根据需求和思路,编写服务器和客户端的代码。 6. 运行测试:运行服务器和客户端程序,检查是否能够实现实时通信和聊天室的基本功能。 如果要退出聊天室,可以使用exit_chatroom函数。该函数会遍历聊天室列表,找到用户所在的聊天室,并将用户从聊天室中移除。如果用户未加入聊天室,则会返回相应的提示信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C语言练手项目--C 语言编写聊天室](https://blog.csdn.net/qq_38880380/article/details/84979553)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [网络编程基础,纯C语言实现聊天室(附源代码)——从铁矿到钢铁的打造](https://blog.csdn.net/weixin_43164603/article/details/107301548)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值