多人聊天程序的实现

本文详细介绍了如何利用Linux系统上的UDP套接字编程实现一个支持多人参与的聊天程序。通过创建并绑定UDP套接字,设置多线程处理并发连接,实现了消息的广播,确保每个在线用户都能接收到并显示所有聊天内容。同时,讨论了在网络编程中可能遇到的丢包、乱序问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*************************************************************************
	> File Name: server.c
	> Author: qinf
	> Mail: 
	> Created Time: Thu 03 Apr 2014 09:39:57 AM CST
 ************************************************************************/

#include "mul_chat.h"

struct linknode {
	struct sockaddr_in addr;
	struct linknode *next;
};
typedef struct linknode *linklist;
linklist create_linklist();
void insert_linknode(linklist L, struct sockaddr_in client_addr);
void delete_linknode(linklist L, struct sockaddr_in client_addr);
int linklist_len(linklist L);

int main(int argc, char *argv[])
{
	int server_fd, serveraddr_len, client_len;
	struct sockaddr_in server_addr, client_addr;
	int pid, iret, result;
	fd_set inputs, testfds;
	struct timeval timeout;
	char buf[512];
	linklist L;
	L = create_linklist();

	//1.socket
	iret = server_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (iret == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	printf("==================\n");
	serveraddr_len = sizeof(server_addr);
	//2.bind
	bind(server_fd, (struct sockaddr*)&server_addr, serveraddr_len);
	//3.recvfrom
	FD_ZERO(&inputs);
	FD_SET(server_fd, &inputs);
	while (1) {
		testfds = inputs;
		result = select(1024, &testfds, NULL, NULL, NULL);
		if (result == -1) {
			perror("select");
			exit(EXIT_FAILURE);
		}
		if (FD_ISSET(server_fd, &testfds)) {
			memset(buf, '\0', 512);
			int iret = recvfrom(server_fd, buf, 512, 0, (struct sockaddr*)&client_addr, &client_len);
			printf("iret: %d\n", iret);
			if (strncmp(buf, "on", 2) == 0) {
				//上线,将client的地址结构添加到链表中
				insert_linknode(L, client_addr);
				printf("--------linklist len = %d\n", linklist_len(L));
			} else if (iret == 0) {
				//退出,将client从链表中删除
				delete_linknode(L, client_addr);
			} else {
				//向所有client发送
				linklist l =  L->next;
				while(l != NULL) {
					//4.sendto
					char test_buf[512];
					memset(test_buf, 0, 512);
					sprintf(test_buf, "from %s: %s", inet_ntoa(l->addr.sin_addr), buf);
					sendto(server_fd, test_buf, strlen(test_buf), 0, (struct sockaddr*)&l->addr, sizeof(struct sockaddr));
					l = l->next;
				}
			}
		}
		if (L == NULL)
			break;
	}
	//5.close
	close(server_fd);
}

linklist create_linklist() {
	linklist L = (linklist)malloc(sizeof(struct linknode));
//	memset(L, 0, sizeof(struct linknode));
	L->next = NULL;
	return L;
}

void insert_linknode(linklist L,struct sockaddr_in client_addr) {
	linklist l = L;
	linklist node = (linklist)malloc(sizeof(struct linknode));
	node->addr.sin_family = client_addr.sin_family;
	node->addr.sin_addr = client_addr.sin_addr;
	node->addr.sin_port = client_addr.sin_port;
	node->next = l->next;
	l->next = node;
}

void delete_linknode(linklist L, struct sockaddr_in client_addr) {
	linklist l = L, p;
	while (l->next != NULL) {
		p = l->next;
		if (p->addr.sin_addr.s_addr == client_addr.sin_addr.s_addr) {
			l->next = p->next;
			free(p);
			break;
		}
		l = l->next;
	}
}
int linklist_len(linklist L)
{
	linklist l = L->next;
	int len = 0;
	while (l != NULL) {
		++len;
		l = l->next;
	}
	return len;
}

/*************************************************************************
	> File Name: client1.c
	> Author: qinf
	> Mail: 
	> Created Time: Thu 03 Apr 2014 09:39:48 AM CST
 ************************************************************************/

#include "mul_chat.h"

int main(int argc, char *argv[])
{
	int client_fd, pid, iret;
	struct sockaddr_in client_addr, server_addr;
	fd_set fd_rd, fd_rd_back;
	char stdin_buf[512], socket_buf[512];
	//socket
	client_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == client_fd) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
	FD_ZERO(&fd_rd);
	FD_SET(0, &fd_rd);
	FD_SET(client_fd, &fd_rd);
	//server_addr
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	
	int len = sizeof(server_addr);
	//上线
	int ii = sendto(client_fd, "on", 2, 0, (struct sockaddr *)&server_addr, len);
	//printf("sendto:ii %d\n", ii);
	while (1) {
		fd_rd_back = fd_rd;//这个必须放在循环里面,每次初始化
		select(1024, &fd_rd_back, NULL, NULL, NULL);
		if (FD_ISSET(0, &fd_rd_back)) {
			memset(stdin_buf, 0, 512);
			iret = read(0, stdin_buf, 512);
			if (iret == 0) {
				sendto(client_fd, "bye", 3, 0, (struct sockaddr *)&server_addr, len);
				break;
			}
			stdin_buf[iret] = '\0';
			ii = sendto(client_fd, stdin_buf, strlen(stdin_buf), 0, (struct sockaddr *)&server_addr, len);
			//printf("sendto: ii %d\n", ii);
		}
		if (FD_ISSET(client_fd, &fd_rd_back)) {
			memset(socket_buf, 0, 512);
			iret = recvfrom(client_fd, socket_buf, 512, 0, NULL, NULL);
			socket_buf[iret] = '\0';
			write(1, socket_buf, strlen(socket_buf));
		}
	}
	close(client_fd);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值