// 聊天室服务器
//
#include "chatroom.h"
// 发送器结构体
typedef struct Sender {
int connfd; // 发送器连接套接字
char nickname[MAX_NCK]; // 发送器昵称
} SENDER;
LIST* g_snds = NULL; // 发送器结构体列表
LIST* g_rcvs = NULL; // 接收器套接字列表
LIST* g_msgs = NULL; // 消息包队列
// 发送器结构体列表互斥量
pthread_mutex_t g_mtx_snds = PTHREAD_MUTEX_INITIALIZER;
// 接收器套接字列表互斥量
pthread_mutex_t g_mtx_rcvs = PTHREAD_MUTEX_INITIALIZER;
// 消息包队列互斥量
pthread_mutex_t g_mtx_msgs = PTHREAD_MUTEX_INITIALIZER;
// 消息包队列非空条件变量
pthread_cond_t g_cnd_msgs = PTHREAD_COND_INITIALIZER;
// SIGINT信号处理函数
void sigint (int signum) {
printf ("\n服务器:再见!\n");
exit (0);
}
// 从消息包队列弹出消息
char* pop_msg (void) {
pthread_mutex_lock (&g_mtx_msgs);
while (list_empty (g_msgs))
pthread_cond_wait (&g_cnd_msgs, &g_mtx_msgs);
char* msg = (char*)list_pop (g_msgs);
pthread_mutex_unlock (&g_mtx_msgs);
return msg;
}
// 向消息包队列压入消息
void push_msg (char* msg) {
pthread_mutex_lock (&g_mtx_msgs);
list_push (g_msgs, msg);
pthread_cond_broadcast (&g_cnd_msgs);
pthread_mutex_unlock (&g_mtx_msgs);
}
// 发送线程过程函数
void* send_proc (void* arg) {
// 发送循环
for (;;) {
char* msg = pop_msg (); // 从消息包对列中弹出消息
pthread_mutex_lock (&g_mtx_rcvs);
// 遍历接收器套接字列表
list_begin (g_rcvs);
while (! list_end (g_rcvs)) {
int* connfd = (int*)list_next (g_rcvs); // 针对每一个接收器
// 向接收器发送消息包
if (send (*connfd, msg,
(strlen (msg) + 1) * sizeof (msg[0]), 0) == -1) {
perror ("send");
close (*connfd);
*connfd = -1;
continue;
}
// 从接收器接收应答包
char ack[MAX_ACK];
ssize_t rb = recv (*connfd, ack, sizeof (ack), 0);
if (rb == -1) {
perror ("recv");
close (*connfd);
*connfd = -1;
continue;
}
if (rb == 0) {
printf ("服务器:接收器已关闭连接。\n");
close (*connfd);
*connfd = -1;
continue;
}
}
// 从接收器套接字列表中删除已失效的接收器套接字
list_remove (g_rcvs, (void*)-1);
pthread_mutex_unlock (&g_mtx_rcvs);
free (msg); // 销毁已发送的消息包
}
return NULL;
}
// 发送器登入
void login (const SENDER* sender) {
pthread_mutex_lock (&g_mtx_snds);
list_push (g_snds, (void*)sender);
pthread_mutex_unlock (&g_mtx_snds);
}
// 发送器登出
void logout (const SENDER* sender) {
pthread_mutex_lock (&g_mtx_snds);
list_remove (g_snds, (void*)sender);
pthread_mutex_unlock (&g_mtx_snds);
}
// 接收线程过程函数
void* recv_proc (void* arg) {
SENDER* sender = (SENDER*)arg;
login (sender);
// 向消息包队列压入欢迎包
char* msg = malloc (MAX_MSG * sizeof (char));
sprintf (msg, "系统> 热烈欢迎%s进入聊天室!",
sender->nickname);
push_msg (msg);
// 接收循环
for (;;) {
// 从发送器接收文本包
char txt[MAX_TXT];
ssize_t rb = recv (sender->connfd, txt, sizeof (txt), 0);
if (rb == -1) {
perror ("recv");
break;
}
if (rb == 0) {
printf ("服务器:发送器已关闭连接。\n");
// 向消息包队列压入告别包
char* msg = malloc (MAX_MSG * sizeof (char));
sprintf (msg, "系统> %s挥一挥衣袖,不带走一片云彩...",
sender->nickname);
push_msg (msg);
break;
}
// 向消息包队列压入文本包
char* msg = malloc (MAX_MSG * sizeof (char));
sprintf (msg, "%s> %s", sender->nickname, txt);
push_msg (msg);
// 向发送器发送应答包
char ack[MAX_ACK] = "OK";
if (send (sender->connfd, ack, (strlen (ack) + 1) *
sizeof (ack[0]), 0) == -1) {
perror ("send");
break;
}
}
logout (sender);
close (sender->connfd);
free (sender);
return NULL;
}
// 启动发送线程
int start_send (void) {
printf ("服务器:启动发送线程...\n");
int error;
pthread_attr_t attr;
if ((error = pthread_attr_init (&attr)) != 0) {
fprintf (stderr, "pthread_attr_init: %s\n",
strerror (error));
return -1;
}
if ((error = pthread_attr_setdetachstate (&attr,
PTHREAD_CREATE_DETACHED)) != 0) {
fprintf (stderr, "pthread_attr_setdetachstate: %s\n",
strerror (error));
return -1;
}
pthread_t tid;
if ((error = pthread_create (&tid, &attr, send_proc,
NULL)) != 0) {
fprintf (stderr, "pthread_create: %s\n", strerror (error));
return -1;
}
if ((error = pthread_attr_destroy (&attr)) != 0) {
fprintf (stderr, "pthread_attr_destroy: %s\n",
strerror (error));
return -1;
}
return 0;
}
// 启动
int start (unsigned short port) {
if (signal (SIGINT, sigint) == SIG_ERR) {
perror ("signal");
return -1;
}
printf ("服务器:创建网络流套接字...\n");
int sockfd = socket (AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror ("socket");
return -1;
}
int on = 1;
if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) == -1) {
perror ("setsockopt");
return -1;
}
printf ("服务器:准备地址并绑定...\n");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1) {
perror ("bind");
return -1;
}
printf ("服务器:监听套接字...\n");
if (listen (sockfd, 1024) == -1) {
perror ("listen");
return -1;
}
g_snds = list_create ();
g_rcvs = list_create ();
g_msgs = list_create ();
// 启动发送线程
if (start_send () == -1)
return -1;
return sockfd; // 返回监听套接字
}
// 昵称可用否
int verify (const char* nickname) {
int ok = 1;
pthread_mutex_lock (&g_mtx_snds);
// 遍历发送器结构体列表
list_begin (g_snds);
while (! list_end (g_snds)) {
// 针对每一个发送器
if (! strcmp ((**(SENDER**)list_next (g_snds)).nickname,
nickname)) {
ok = 0;
break;
}
}
pthread_mutex_unlock (&g_mtx_snds);
return ok;
}
// 等待客户机连接
int wait_client (int sockfd, char nickname[]) {
printf ("服务器:等待连接请求...\n");
// 接受来自客户机的连接请求
struct sockaddr_in addrcli;
socklen_t addrlen = sizeof (addrcli);
int connfd = accept (sockfd, (struct sockaddr*)&addrcli,
&addrlen);
if (connfd == -1) {
perror ("accept");
return -1;
}
// 从客户机接收标志包
char tag[MAX_TAG];
if (recv (connfd, tag, MAX_TAG, 0) == -1) {
perror ("recv");
close (connfd);
return -2;
}
char ack[MAX_ACK] = "OK";
// 发送器
if (! strncmp (tag, "SNDR", 4))
if (verify (tag + 4)) {
printf ("服务器:接受来自%s:%u发送器的连接请求。\n",
inet_ntoa (addrcli.sin_addr), ntohs (addrcli.sin_port));
strcpy (nickname, tag + 4);
}
else {
printf ("服务器:拒绝来自%s:%u发送器的连接请求。\n",
inet_ntoa (addrcli.sin_addr), ntohs (addrcli.sin_port));
strcpy (ack, "更换昵称再试一次!");
}
// 接收器
else
if (! strncmp (tag, "RCVR", 4))
printf ("服务器:接受来自%s:%u接收器的连接请求。\n",
inet_ntoa (addrcli.sin_addr), ntohs (addrcli.sin_port));
// 非法客户机
else {
printf ("服务器:拒绝来自%s:%u客户机的连接请求。\n",
inet_ntoa (addrcli.sin_addr), ntohs (addrcli.sin_port));
close (connfd);
return -2;
}
// 向客户机发送应答包
if (send (connfd, ack, (strlen (ack) + 1) *
sizeof (ack[0]), 0) == -1) {
perror ("send");
close (connfd);
return -2;
}
if (strcmp (ack, "OK")) {
close (connfd);
return -2;
}
return connfd; // 返回连接套接字
}
// 启动接收线程
int start_recv (int connfd, const char* nickname) {
printf ("服务器:启动接收线程...\n");
int error;
pthread_attr_t attr;
if ((error = pthread_attr_init (&attr)) != 0) {
fprintf (stderr, "pthread_attr_init: %s\n",
strerror (error));
return -1;
}
if ((error = pth
Linux下多人聊天室(完整版)
需积分: 44 144 浏览量
2019-06-20
12:34:22
上传
评论 12
收藏 28KB RAR 举报
qq_36042180
- 粉丝: 21
- 资源: 48
最新资源
- F103-霸道开发板2.8寸电阻触摸屏例程.rar
- Google(高德)地图瓦片python代码下载
- Python实现输出杨辉三角形
- polsarpro官方教程、操作说明 PolSARpro v5.0 Software Training Course
- STM32 TouchGFX的使用二图片显示
- buildx镜像文件,也可以通过网上其他方式获取
- 【中级软件设计师】上午题12-软件工程(2):单元测试、黑盒测试、白盒测试、软件运行与维护
- 免费计算机毕业设计-医院住院管理系统的设计与实现(包含代码+论文)
- tt100k数据转换yolo格式
- 免费计算机毕业设计-学生在线网络考试系统的设计与实现(包含论文+源码)
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈