网络聊天室:
程序代码:
ser.c
#include <myhead.h>
//定义消息类型结构体
struct xiaoxi {char type;char name[20];char text[100];
};int main(int argc, const char* argv[])
{// 创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1) {perror("socket error");return -1;}// 设置端口快速重用int reuse = 1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {perror("setsockopt 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]);if (bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1) {perror("bind error\n");return -1;}// 设置监听if ((listen(sfd, 128)) == -1){perror("listen error\n");return -1;}// 4接受请求// 存储客户端信息结构体struct sockaddr_in cin;cin.sin_family = AF_INET;cin.sin_port = htons(atoi(argv[2]));cin.sin_addr.s_addr = inet_addr(argv[1]);socklen_t socklen = sizeof(cin);struct sockaddr_in arr_cin[1024];// 定义struct pollfd fds[1024];for (int i = 0; i < 1024; i++) {fds[i].fd = -1;fds[i].events = POLLIN;}//将0和sfd放入数组fds[0].fd = 0;fds[1].fd = sfd;struct xiaoxi rec;//消息类型结构体变量char buf[128] = "";//接受消息的容器int newfd = -1; //存储文件描述符while (1) {int res = poll(fds, 1024, -1);if (res < 0){perror("poll error");return -1;}else if (res == 0){printf("time out\n");return -1;}if (fds[0].revents == POLLIN) //终端标准输入描述符{bzero(buf, sizeof(buf));fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;for (int j = 4; j < 1024; j++){send(fds[j].fd, buf, sizeof(buf), 0);}}if (fds[1].revents == POLLIN)//sfd{ if ((newfd = accept(sfd, (struct sockaddr*)&cin, &socklen)) == -1) {perror("accept error\n");}arr_cin[newfd] = cin; //将地址信息存入cinprintf("------[%s:%d]连接成功------\n", inet_ntoa(arr_cin[newfd].sin_addr), htons(arr_cin[newfd].sin_port));fds[newfd].fd = newfd; //新生成的文件描述符放入数组fds[newfd].events = POLLIN;}for (int i = 4; i < 1024; i++) {if (fds[i].fd < 0) {continue;}if (fds[i].revents == POLLIN) {bzero(buf, sizeof(buf));bzero(buf, sizeof(buf));int res = recv(fds[i].fd, &rec, sizeof(rec), 0); //接收客户端消息if (res == 0){close(fds[i].fd);printf("------[%s:%d]断开连接------\n", inet_ntoa(arr_cin[fds[i].fd].sin_addr), htons(arr_cin[fds[i].fd].sin_port));fds[i].fd = -1;arr_cin[fds[i].fd].sin_addr.s_addr = 0;arr_cin[fds[i].fd].sin_port = 0;continue;}// 判断消息类型if (rec.type == 'L') // 登录消息{ for (int j = 4; j < 1024; j++)//发送给每个客户端{ if (j == i){continue;}bzero(buf, sizeof(buf));sprintf(buf, "----------%s上线-----------", rec.name);send(fds[j].fd, buf, strlen(buf), 0);}} else if (rec.type == 'C') // 聊天消息{ for (int j = 4; j < 1024; j++) {if (j == i) {continue;}bzero(buf, sizeof(buf));sprintf(buf, "%s:%s", rec.name, rec.text);send(fds[j].fd, buf, strlen(buf), 0);}} else { //退出消息//(rec.type == 'Q')for (int j = 4; j < 1024; j++) {if (j == i){continue;}bzero(buf, sizeof(buf));sprintf(buf, "-----------%s下线------------", rec.name);send(fds[j].fd, buf, strlen(buf), 0);}}}}}return 0;
}
cli.c
#include <myhead.h>struct xiaoxi {char type;char name[20];char text[100];
};int main(int argc, const char* argv[])
{// 创建套接字int cfd = socket(AF_INET, SOCK_STREAM, 0);if (cfd == -1) {perror("socket error\n");return -1;}// 端口快速重用int reus = 1;if ((reus = setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reus, sizeof(reus))) == -1) {perror("setsockopt error\n");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]);// 向客户端发送连接请求if (connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1){perror("connect error");return -1;}struct xiaoxi sendi;char buf[100] = "";char nbuf[20] = "";struct pollfd fds[2];fds[0].fd=0;fds[0].events = POLLIN;fds[1].fd=cfd;fds[1].events = POLLIN;printf("请输入用户名:");fgets(nbuf,sizeof(nbuf),stdin);nbuf[strlen(nbuf)-1]=0;sendi.type = 'L';strcpy(sendi.name, nbuf);send(cfd, &sendi, sizeof(sendi), 0);while (1) {int res = poll(fds,2,-1); //阻塞检测if (fds[0].revents == POLLIN) {bzero(buf, sizeof(buf));fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]=0;sendi.type = 'C'; if(strcmp(buf,"quit")==0){sendi.type = 'Q';sendto(cfd, &sendi, sizeof(sendi), 0, (struct sockaddr*)&sin, sizeof(sin));break;}strcpy(sendi.text,buf);sendto(cfd, &sendi, sizeof(sendi), 0, (struct sockaddr*)&sin, sizeof(sin));}if (fds[1].revents == POLLIN){bzero(buf, sizeof(buf));int res = recv(cfd, buf, sizeof(buf), 0);printf("%s\n", buf);if (res == 0) {printf("服务器下线\n");break;}}}close(cfd);return 0;
}
运行结果: