Part 1.实现聊天室功能
1.service.c
#include <myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.109.31"
#define MAX_CLIENTS 100
typedef struct clientmsg {
int fd; // 客户端的文件描述符
char username[20];
struct sockaddr_in cin;
struct clientmsg *next;
} *ClientNode;
// 创建客户端节点
ClientNode create_client_node(int fd, const char *username, struct sockaddr_in cin)
{
ClientNode node = (ClientNode)malloc(sizeof(struct clientmsg));
if (node == NULL) return NULL;
node->fd = fd;
strncpy(node->username, username, sizeof(node->username)-1);
node->cin = cin;
node->next = NULL;
return node;
}
// 添加客户端到链表
void add_client(ClientNode *head, ClientNode new_client)
{
if (*head == NULL)
{
*head = new_client;
}
else
{
ClientNode temp = *head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = new_client;
}
}
// 从链表中移除客户端
void remove_client(ClientNode *head, int fd)
{
if (*head == NULL) return;
ClientNode current = *head;
ClientNode prev = NULL;
while (current != NULL)
{
if (current->fd == fd)
{
if (prev == NULL)
{
*head = current->next;
}
else
{
prev->next = current->next;
}
free(current);
return;
}
prev = current;
current = current->next;
}
}
// 广播消息给所有客户端(除了发送者)
void broadcast_message(ClientNode head, int sender_fd, const char *message)
{
ClientNode current = head;
while (current != NULL)
{
if (current->fd != sender_fd)
{
send(current->fd, message, strlen(message), 0);
}
current = current->next;
}
}
// 获取客户端用户名
const char* get_client_username(ClientNode head, int fd)
{
ClientNode current = head;
while (current != NULL)
{
if (current->fd == fd)
{
return current->username;
}
current = current->next;
}
return "Unknown";
}
int main(int argc, const char *argv[])
{
// 创建服务器端套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1)
ERR_MSG("socket error");
// 设置地址重用
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 创建服务器端地址信息结构体并且绑定
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)
ERR_MSG("bind error");
// 开启监听
if (listen(sfd, 128) == -1)
ERR_MSG("listen error");
printf("服务器启动成功,等待客户端连接...\n");
// 客户端链表头指针
ClientNode clients = NULL;
// 创建文件描述符集合
fd_set readfds, tempfds;
FD_ZERO(&readfds);
FD_SET(sfd, &readfds); // 添加监听套接字
FD_SET(0, &readfds); // 添加标准输入
int maxfd = sfd;
while (1)
{
tempfds = readfds;
// 使用select监听文件描述符
int res = select(maxfd + 1, &tempfds, NULL, NULL, NULL);
if (res == -1)
{
ERR_MSG("select error");
}
// 检查是否有新的连接请求
if (FD_ISSET(sfd, &tempfds))
{
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
int new_fd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
if (new_fd == -1)
{
perror("accept error");
continue;
}
// 接收客户端发送的用户名
char username[20] = "";
int username_len = recv(new_fd, username, sizeof(username)-1, 0);
if (username_len <= 0)
{
close(new_fd);
continue;
}
username[username_len] = '\0';
// 创建客户端节点并添加到链表
ClientNode new_client = create_client_node(new_fd, username, cin);
if (new_client == NULL)
{
close(new_fd);
continue;
}
add_client(&clients, new_client);
// 将新客户端的文件描述符添加到select监听集合
FD_SET(new_fd, &readfds);
if (new_fd > maxfd)
{
maxfd = new_fd;
}
printf("%s [%s:%d] 加入聊天室\n",
username, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
// 广播欢迎消息
char welcome_msg[128];
snprintf(welcome_msg, sizeof(welcome_msg),
"--------- %s 加入聊天室----------", username);
broadcast_message(clients, new_fd, welcome_msg);
}
// 检查服务器终端输入
if (FD_ISSET(0, &tempfds))
{
char buf[128];
if (fgets(buf, sizeof(buf), stdin) == NULL) continue;
buf[strcspn(buf, "\n")] = '\0'; // 移除换行符
if (strlen(buf) > 0)
{
// 广播服务器消息
char server_msg[150];
snprintf(server_msg, sizeof(server_msg), "服务器: %s", buf);
broadcast_message(clients, -1, server_msg);
}
}
// 检查所有客户端是否有数据可读
ClientNode current = clients;
while (current != NULL)
{
int client_fd = current->fd;
ClientNode next = current->next; // 保存下一个指针,因为当前节点可能被删除
if (FD_ISSET(client_fd, &tempfds))
{
char rbuf[128] = "";
int res = recv(client_fd, rbuf, sizeof(rbuf)-1, 0);
if (res <= 0)
{
// 客户端断开连接
printf("%s 已退出聊天室\n", current->username);
// 广播退出消息
char quit_msg[128];
snprintf(quit_msg, sizeof(quit_msg),
"----------%s 已退出聊天室----------", current->username);
broadcast_message(clients, client_fd, quit_msg);
// 清理资源
close(client_fd);
FD_CLR(client_fd, &readfds);
remove_client(&clients, client_fd);
// 更新maxfd
if (client_fd == maxfd)
{
maxfd = sfd;
ClientNode temp = clients;
while (temp != NULL)
{
if (temp->fd > maxfd)
{
maxfd = temp->fd;
}
temp = temp->next;
}
}
} else
{
rbuf[res] = '\0';
// 广播客户端消息
char broadcast_msg[256];
snprintf(broadcast_msg, sizeof(broadcast_msg),
"%s: %s", current->username, rbuf);
broadcast_message(clients, client_fd, broadcast_msg);
printf("%s: %s\n", current->username, rbuf);
}
}
current = next;
}
}
close(sfd);
return 0;
}
2.cilent.c
#include<myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.109.31"
int main(int argc, const char *argv[])
{
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == cfd)
ERR_MSG("socket error");
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(-1 == connect(cfd,(struct sockaddr *)&sin,sizeof(sin)))
ERR_MSG("bind error");
printf("请输入用户名>>>\n");
char namebuf[20] = "";
scanf(" %s",namebuf);
send(cfd,namebuf,strlen(namebuf),0);
pid_t pid = fork();
while(1)
{
if(pid > 0)
{
while(1)
{
char rbuf[128] = "";
int ret = recv(cfd,rbuf,sizeof(rbuf),0);
if(ret < 0)
break;
printf("%s\n",rbuf);
}
close(cfd);
kill(pid,SIGKILL);
wait(NULL);
}
else if(pid == 0)
{
//与服务器端进行通信
while(1)
{
//从套接字中读取消息
char wbuf[128] = "";
fgets(wbuf,sizeof(wbuf),stdin);
wbuf[strlen(wbuf)-1] = 0;
if(strcmp(wbuf,"quit") == 0)
break;
send(cfd,wbuf,strlen(wbuf),0);
}
close(cfd);
exit(0);
}
}
return 0;
}
Part 2.牛客网刷题
