项目————网络聊天室
服务器
#include <myhead.h>
typedef struct msg{char flag;char name[20];char cont[128];
}msg_t;typedef struct link{struct sockaddr_in cin;struct link* next;
}link_t;void do_login(int sfd,msg_t msg,link_t *L,struct sockaddr_in cin){link_t* p=L;if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin))){perror("sendto error");}//将用户的网络消息结构体,头插链表link_t* q =(link_t*)malloc(sizeof(link_t));if(q == NULL){printf("malloc error");return ;}q->cin = cin;q->next = L->next;L->next = q;return;
}
//群聊操作
void do_chat(int sfd,msg_t msg,link_t *L,struct sockaddr_in cin){link_t* p=L;while(1){p=p->next;if(memcmp(&cin,&(p->cin),sizeof(cin))){if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin))){printf("sendto error");}}}return;
}//退出操作
void do_quit(int sfd,msg_t msg,link_t* L,struct sockaddr_in cin){link_t* p=L;link_t* q=NULL;while(p->next != NULL){if(memcmp(&(p->next->cin)),&cin,sizeof(cin)){p=p->next;if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)) == -1){perror("sendto error");}}else{q = p->next;p->next = q->next;free(q);q=NULL;}}return;
}
int main(int argc, const char *argv[])
{if(argc != 3){printf("请输入IP地址和端口号\n");return -1;}int sfd =socket(AF_INET,SOCK_DGRAM,0);if(sfd == -1){perror("sfd error");return -1;}//填充服务端网络信息struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(atoi(argv[2]));sin.sin_addr.s_addr = inet_addr(argv[1]);socklen_t sin_len = sizeof(sin);//绑定if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1){perror("bind error");return -1;}//定义客户端结构体struct sockaddr_in cin;socklen_t cin_len = sizeof(cin);msg_t msg;pid_t pid;//创建进程pid=fork();if(pid == 0){//创建链表link_t* L= (link_t*)malloc(sizeof(link_t));if(L == NULL){printf("link error");return -1;}memset(L,0,sizeof(L));L->next = NULL;//循环收发数据while(1){memset(&msg,0,sizeof(msg));memset(&cin,0,sizeof(cin));if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,cin_len)){perror("recvfeom error");return -1;}switch(msg.flag){case 'L':do_login(sfd,msg,L,cin);break;case 'C':do_chat(sfd,msg,L,cin);break;case 'Q':do_quit(sfd,msg,L,cin);break;}}//父进程用来接受客户端发来的消息}else{strcpy(msg.name,"系统消息");msg.flag = 'C';while(1){memset(msg.cont,0,sizeof(msg.cont));fgets(msg.cont,128,stdin);msg.cont[strlen(msg.cont)-1] = '\0';if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len) == -1){perror("sendto error");return -1;}}}close(sfd);return 0;
}
客户端
#include <myhead.h>
typedef struct msg {char flag; //操作码:'L'表示登录 'C'表示群聊 'Q'表示退出 'S'表示系统消息//定义操作码为1个字符,免去考虑网络字节序问题char name[45];char cont[256];
} msg_t; //定义消息结构体类型int main(int argc, const char *argv[])
{if (3 != argc) { //考虑用命令行传参方式输入ip地址及端口号,先进行参数判断printf("请输入端口号\n");return -1;}int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sockfd){PRINERR("socket error");}struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddr_len = sizeof(serveraddr);msg_t msg;memset(&msg, 0, sizeof(msg));//输入用户名printf("请输入用户名:>>");fgets(msg.name, 45, stdin);msg.name[strlen(msg.name)-1] = '\0';msg.flag = 'L';strcpy(msg.cont, "加入群聊");//给服务器发送登录的信息if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len)){PRINERR("sendto error");}pid_t pid = 0;if(-1 == (pid = fork())){PRINERR("fork error");}if(0 == pid){//子进程,循环接收并打印收到的数据while(1){if(-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL)){PRINERR("recvfrom error");}//打印收到的数据printf("[%s]: %s\n", msg.name, msg.cont);}}else if(0 < pid){//父进程,循环在终端接收数据并发送给客户端while(1){memset(msg.cont,0,sizeof(msg.cont));fgets(msg.cont, 128, stdin);//在终端获取聊天信息msg.cont[strlen(msg.cont)-1] = '\0';if(!strcmp(msg.cont, "quit")){msg.flag = 'Q';strcpy(msg.cont, "退出群聊");}else{msg.flag = 'C';}if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len)){PRINERR("sendto error");}if(!strcmp(msg.cont, "退出群聊")){break;}}//杀死子进程kill(pid, SIGKILL);wait(NULL);//等待回收子进程资源}close(sockfd);return 0;
}