网络聊天室编写(基于UDP)
服务器
#include
#define PORT 8888 //端口号:接收方绑定的端口号
#define IP "192.168.114.56" //本机IP
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:", __LINE__); \
perror(msg);\
}while(0)
//需要传入到分支线程的参数
typedef struct Climsg{
char code;
char user[32];
char text[128];
}msg_t;
//创建链表存储数据
typedef struct Node{
struct sockaddr_in addr;
struct Node *next;
}node_t,*nodeptr;
//创建节点的函数
int create_node(nodeptr *phead){
*phead = (nodeptr)malloc(sizeof(node_t));
if(NULL == *phead){
printf("内存分配失败\n");
exit(-1);
}
(*phead)->next=NULL;
return 0;
}
//尾插法
int insert_data_by_tail(node_t *phead,struct sockaddr_in addr){
if(NULL == phead)
{
printf("参数为NULL,请检查\n");
return -1;
}
//将新客户端使用尾插法插入链表中
nodeptr pnew = NULL;
create_node(&pnew);
pnew->addr = addr;
nodeptr ptemp =phead;
while(ptemp->next != NULL)
{
ptemp = ptemp->next;
}
//让尾结点的指针域指向新节点
ptemp->next = pnew;
return 0;
}
int main(int argc, const char *argv[])
{
int sfd = 0;
if(-1==(sfd=socket(AF_INET,SOCK_DGRAM,0))){
ERR_MSG("socket error");
}
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
socklen_t sin_len = sizeof(sin);
if(-1 == bind(sfd,(struct sockaddr *)&sin,sin_len)){
ERR_MSG("bind error");
}
struct sockaddr_in cin,temp_cin;
memset(&cin,0,sizeof(cin));
socklen_t cin_len = sizeof(cin);
char name[32] = {0};
pid_t pid = 0;
msg_t msg;
msg_t msg_send;
//创建头结点
nodeptr phead;
create_node(&phead);
phead->addr = cin;
if(-1 == (pid = fork())){
ERR_MSG("fork error");
}else if(0 == pid){ //子进程 接收数据 (1、d 登录操作 2、q 群聊操作 3、t 退出操作)
while(1){
memset(&msg,0,sizeof(msg));
if(-1 == recvfrom(sfd, (void*)&msg, sizeof(msg),0, (struct sockaddr *)&cin,&cin_len)){
perror("recv error");
}
switch(msg.code){
case 'd':
printf("用户[%s]上线\n", msg.user);
insert_data_by_tail(phead,cin);
nodeptr q=phead->next;
while(q != NULL){
msg.code='d';
if(-1 == sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&q->addr,sizeof(q->addr)))
{
ERR_MSG("send error");
}
q=q->next;
}
break;
case 'q':
if(strcmp("系统提示",msg.user)!=0){
printf("[%s]:%s\n",msg.user, msg.text);
}
node_t *p = phead->next;
while(p != NULL){
msg.code='q';
if(-1 == sendto(sfd,(void *)&msg,sizeof(msg),0,(struct sockaddr *)&p->addr,sizeof(p->addr))){
ERR_MSG("send error");
}
p=p->next;
}
break;
case 't':
printf("用户[%s]:已退出\n", msg.user);
nodeptr t = phead;
nodeptr pdel = NULL;
while(t->next != NULL){
msg.code='t';
if( 0 == memcmp(&(t->next->addr), &cin,sizeof(cin))){
pdel = t->next;
t->next = pdel->next;
free(pdel);
}else{
t = t->next;
if(-1 == sendto(sfd, &msg,sizeof(msg),0,(struct sockaddr *)&t->addr,sizeof(t->addr))){
ERR_MSG("send error");
}
}
}
break;
}
}
}else if(0 < pid){
//父进程 发送系统消息
while(1){
strcpy(msg_send.user,"系统");
memset(msg_send.text,0,128);
fgets(msg_send.text,128,stdin);
msg_send.text[strlen(msg_send.text)-1] = '\0';
msg_send.code = 'q';
if(-1 == sendto(sfd,&msg_send,sizeof(msg_send),0,(struct sockaddr *)&sin,sin_len)){
ERR_MSG("send error");
}
}
}
kill(pid, SIGKILL);
wait(NULL);//给子进程回收资源
exit(0);
close(sfd);
return 0;
}
客户端
#include
#define PORT 8888 //端口号:接收方绑定的端口号
#define ERR_MSG(msg) do{\
fprintf(stderr, "__%d__:", __LINE__); \
perror(msg);\
}while(0)
#define IP "192.168.114.56" //本机IP,ifconfig
#define M 32
#define N 128
typedef struct _Node{
struct sockaddr_in addr;
struct _Node *next;
}node_t;
typedef struct _Msg{
char code;
char user[M];
char text[N];
}msg_t;
int main(int argc, const char *argv[])
{
//创建用户数据报套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd){
ERR_MSG("socket error");
}
//填充服务器网络信息结构体
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
socklen_t sin_len = sizeof(sin);
int res = 0;
char name[32]={0};
msg_t msg;
pid_t pid;
struct sockaddr_in cin;
memset(&cin,0,sizeof(cin));
socklen_t cin_len = sizeof(cin);
//输入用户名,完成登陆操作
printf("请输入登录信息:");
msg.code = 'd';
memset(msg.user, 0, 32);
fgets(name, 32, stdin);//在终端获取用户名
strcpy(msg.user,name);
msg.user[strlen(msg.user) - 1] = '\0';
if (-1 == sendto(sockfd,&msg,sizeof(msg),0, (struct sockaddr *)&sin,sin_len)){ //给服务器发送用户名
ERR_MSG("send error");
}
//创建进程
if(-1 == (pid = fork())){
ERR_MSG("fork error");
}else if(0 == pid){
//子进程 接收数据
while (1){
memset(&msg,0,sizeof(msg));
//接收服务器的应答
if (-1 == (res=recvfrom(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&sin,&sin_len))){
ERR_MSG("recv error");
}
if(strcmp(msg.user,name) == -10){
continue;
}else{
//打印应答信息
switch(msg.code){
case 'd':
printf("用户[%s]登录上线了....\n", msg.user);
break;
case 'q':
printf("[%s]:%s\n",msg.user,msg.text);
break;
case 't':
printf("用户[%s]已退出\n", msg.user);
break;
}
}
}
}else if(0 < pid){
//父进程 发送数据(2、q:群聊操作 3、t:退出操作)
while(1){
//在终端获取群聊
memset(msg.text, 0, 128);
fgets(msg.text, 128, stdin);
msg.text[strlen(msg.text) - 1] = '\0'; //清空结尾的 '\n'
if( 0 ==strcmp(msg.text, "quit")){
msg.code = 't';
if (-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&sin,sin_len)){
ERR_MSG("send error");
}
break;
}else{
msg.code = 'q';
}
//给服务器发送群聊消息
if (-1 == sendto(sockfd, &msg, sizeof(msg), 0,(struct sockaddr *)&sin,sin_len)){
ERR_MSG("send error");
}
}
}
//关闭套接字
close(sockfd);
return 0;
}