Appearance
c
#ifndef __HEAD_H__
#define __HEAD_H__
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>
#define ERR_MSG(msg) \
do { \
fprintf(stderr, "line:%d\n", __LINE__); \
perror(msg); \
} while (0)
#define SERVER_IP "192.168.0.170"
#define SERVER_PORT 5678
#endifc
#include "head.h"
typedef struct node {
struct sockaddr_in addr;
struct node *next;
} *Node;
Node fdlist = NULL;
/**
* @brief 比较两个地址信息结构体是否为同一个
*/
int addrcmp(struct sockaddr_in addr1, struct sockaddr_in addr2) {
return addr1.sin_addr.s_addr == addr2.sin_addr.s_addr &&
addr1.sin_port == addr2.sin_port;
}
/**
*@brief 添加一个地址信息结构体(头插)
*/
int addAddr(struct sockaddr_in addr) {
Node node = (Node)malloc(sizeof(Node));
node->addr = addr;
node->next = fdlist;
fdlist = node;
return 0;
}
/**
*@brief 删除一个地址信息结构体(查找 + 删除)
*/
void delAddr(struct sockaddr_in addr) {
Node prev = NULL;
Node cur = fdlist;
while (cur != NULL && !addrcmp(cur->addr, addr)) {
prev = cur;
cur = cur->next;
}
if (cur == NULL)
return;
if (prev == NULL)
fdlist = cur->next;
else
prev->next = cur->next;
free(cur);
}
typedef struct CallbackData {
int sfd; /* 服务器的 socket */
char *msg; /* 发送的消息 */
int msglen; /* 消息长度,本程序都是 512 */
} CallbackData;
/**
*@brief 遍历链表
*/
int eachAddr(int (*fun)(struct sockaddr_in addr, CallbackData data),
CallbackData data) {
Node node = fdlist;
while (node) {
if (fun(node->addr, data) < 0) {
ERR_MSG("callback in eachfd()");
return -1;
}
node = node->next;
}
return 0;
}
/**
* @brief 封装一下 sendto 让它可以作为回调传入链表遍历
*/
int mysendto(struct sockaddr_in addr, CallbackData data) {
// 分开使用打包的参数
int sfd = data.sfd;
int msglen = data.msglen;
char *msg = data.msg;
// 使用addr进行相关操作
// mysend(sfd, msg, msglen, addr);
if (sendto(sfd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
ERR_MSG("sendto()");
exit(-1);
}
return 0;
}
/**
* @brief 发送广播消息
*/
void boardcast(int sfd, char *msg, int msglen);
/**
* @brief 服务器终端输入,发送给所有连接客户端
*/
void *on_child(void *arg);
/**
* @brief 用户登录
*/
void on_login(int sfd, char *name, struct sockaddr_in cin);
/**
* @brief 用户发消息
*/
void on_chat(int sfd, char *name, char *msg, struct sockaddr_in cin);
/**
* @brief 用户退出
*/
void on_quit(int sfd, char *name, struct sockaddr_in cin);
int main() {
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd < 0) {
ERR_MSG("socket()");
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SERVER_PORT);
sin.sin_addr.s_addr = inet_addr(SERVER_IP);
if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
ERR_MSG("bind()");
return -1;
}
/* 子线程处理服务器输入数据 */
pthread_t tid;
if (pthread_create(&tid, NULL, on_child, (void *)&sfd) != 0) {
fprintf(stderr, "line: %d pthread_create failed\n", __LINE__);
return -1;
}
pthread_detach(tid);
/* 主线程接收客户端传入数据 */
struct sockaddr_in cin;
cin.sin_family = AF_INET;
socklen_t addrlen = sizeof(cin);
int res = -1;
char buf[149] = {0};
while (1) {
bzero(buf, sizeof(buf));
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr *)&cin, &addrlen);
if (res < 0) {
ERR_MSG("recvfrom()");
return -1;
}
char type = buf[0]; /* 'L', 'C', 'Q' */
char *name = buf + 2;
char *msg = name + strlen(name) + 1;
if (type == 'L')
on_login(sfd, name, cin);
if (type == 'C')
on_chat(sfd, name, msg, cin);
if (type == 'Q')
on_quit(sfd, name, cin);
}
close(sfd);
return 0;
}
void on_login(int sfd, char *name, struct sockaddr_in cin) {
/* 添加到链表 */
addAddr(cin);
char buf[512] = {0};
sprintf(buf, "用户[%s]登入聊天室", name);
boardcast(sfd, buf, 512);
}
void on_quit(int sfd, char *name, struct sockaddr_in cin) {
delAddr(cin);
char buf[512] = {0};
sprintf(buf, "用户[%s]退出聊天室", name);
boardcast(sfd, buf, 512);
}
void on_chat(int sfd, char *name, char *msg, struct sockaddr_in cin) {
char buf[512] = {0};
sprintf(buf, "用户[%s]说:%s", name, msg);
boardcast(sfd, buf, 512);
}
void *on_child(void *arg) {
int sfd = *(int *)arg;
char buf[512] = {0};
while (1) {
bzero(buf, sizeof(buf));
scanf(" %s", buf);
boardcast(sfd, buf, 512);
}
}
void boardcast(int sfd, char *msg, int msglen) {
CallbackData data = {.msg = msg, .sfd = sfd, .msglen = msglen};
eachAddr(mysendto, data);
}c
#include "head.h"
void *on_child(void *arg) {
int sfd = *(int *)arg;
char buf[512] = {0};
while (1) {
bzero(buf, sizeof(buf));
if (recvfrom(sfd, buf, sizeof(buf), 0, NULL, NULL) < 0) {
ERR_MSG("recvfrom");
break;
}
printf("[服务器消息]%s\n", buf);
}
pthread_exit(NULL);
}
int main() {
int isOnline = 0;
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SERVER_PORT);
sin.sin_addr.s_addr = inet_addr(SERVER_IP);
/* 子线程接收服务器端数据 */
pthread_t tid;
if (pthread_create(&tid, NULL, on_child, (void *)&sfd) != 0) {
fprintf(stderr, "line: %d pthread_create failed\n", __LINE__);
return -1;
}
pthread_detach(tid);
char buf[149] = {0};
char msg[128] = {0};
char name[20] = {0};
char type;
while (1) {
bzero(buf, sizeof(buf));
bzero(msg, sizeof(msg));
if (!isOnline) {
type = 'L';
printf("请输入您的用户名: ");
scanf(" %s", name);
isOnline = 1;
} else {
scanf(" %s", msg);
type = strncmp(msg, "quit", 4) ? 'C' : 'Q';
}
sprintf(buf, "%c%c%s%c%s", type, 0, name, 0, msg);
if (sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, sizeof(sin)) <
0) {
ERR_MSG("sendto");
return -1;
}
if (type == 'Q')
break;
}
close(sfd);
return 0;
}