Linux Linux程序练习十二(select实现QQ群聊)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux Linux程序练习十二(select实现QQ群聊)相关的知识,希望对你有一定的参考价值。
//头文件--helper.h #ifndef _vzhang #define _vzhang #ifdef __cplusplus extern "C" { #endif #define MAX_SOCKET_NUM 1024 #define BUF_SIZE 1024 //server create socket int server_socket(int port); //close socket int close_socket(int st); //start select int start_select(int listen_st); //connect server int connect_server(char * ipaddr, int port); //thread for recv message void * thread_recv(void *arg); //thread for send message void * thread_send(void *arg); #ifdef __cplusplus } #endif #endif
//辅助方法--helper.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <time.h> #include <fcntl.h> #include <sys/select.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include "helper.h" //create socket int create_socket() { int st = socket(AF_INET, SOCK_STREAM, 0); if (st < 0) { printf("create socket failed ! error message :%s\n", strerror(errno)); return -1; } return st; } //set reuseaddr int set_reuseaddr(int st) { int on = 1; if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { printf("reuseaddr failed ! error message :%s\n", strerror(errno)); return -1; } return 0; } //bind IP and port int bind_ip(int st, int port) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //type addr.sin_family = AF_INET; //port addr.sin_port = htons(port); //address addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) < 0) { printf("bind failed ! error message :%s\n", strerror(errno)); return -1; } return 0; } //listen int listen_port(int st, int num) { if (listen(st, num) < 0) { printf("listen failed ! error message :%s\n", strerror(errno)); return -1; } return 0; } //server create socket int server_socket(int port) { int st = create_socket(); if (st < 0) { return -1; } if (set_reuseaddr(st) < 0) { return -1; } if (bind_ip(st, port) < 0) { return -1; } if (listen_port(st, 20) < 0) { return -1; } return st; } //get IP address by sockaddr_in void sockaddr_toa(const struct sockaddr_in *addr, char * ipaddr) { unsigned char * p = (unsigned char *) &(addr->sin_addr.s_addr); sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); } //accept client int accept_client(int st) { if (st < 0) { printf("function accept_client param not correct!\n"); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); socklen_t len = sizeof(addr); int client_st = accept(st, (struct sockaddr *) &addr, &len); if (client_st < 0) { printf("accept failed ! error message :%s\n", strerror(errno)); return -1; } char buf[30] = { 0 }; sockaddr_toa(&addr, buf); printf("accept by %s\n", buf); return client_st; } //close socket int close_socket(int st) { if (st < 0) { printf("function close_socket param not correct!\n"); return -1; } close(st); return 0; } //protect message int protect_message(int st, int * starr) { if (st < 0 || starr == NULL) { printf("function recv_message param not correct!\n"); return -1; } char buf[BUF_SIZE] = { 0 }; int i = 0; int rc = recv(st, buf, sizeof(buf), 0); if (rc < 0) { printf("recv failed ! error message :%s\n", strerror(errno)); return -1; } else if (rc == 0) { printf("client is closed !\n"); return -1; } /* *QQ消息处理:接收到client1的消息直接发送给client2 */ for (; i < MAX_SOCKET_NUM; i++) { if (starr[i] != st && starr[i] != -1) { //向其他的chient发送消息 if (send(starr[i], buf, strlen(buf), 0) < 0) { printf("send failed ! error message :%s\n", strerror(errno)); return -1; } } } return 0; } //send message int send_message(int st, int * starr) { return 0; } //start select int start_select(int listen_st) { if (listen_st < 0) { printf("function create_select param not correct!\n"); return -1; } int i = 0; //定义select第一个参数变量 int maxfd = 0; int rc = 0; //创建客户端socket池 int client[MAX_SOCKET_NUM] = { 0 }; //初始化socket池 for (; i < MAX_SOCKET_NUM; i++) { client[i] = -1; } //定义事件数组结构 fd_set allset; while (1) { //清空事件数组 FD_ZERO(&allset); //将服务器端socket加入事件数组 FD_SET(listen_st, &allset); //假设值最大的socket是listen_st maxfd = listen_st; //将所有客户端socket加入到事件数组 for (i = 0; i < MAX_SOCKET_NUM; i++) { if (client[i] != -1) { FD_SET(client[i], &allset); if (maxfd < client[i]) { maxfd = client[i]; } } } //select阻塞接收消息 rc = select(maxfd + 1, &allset, NULL, NULL, NULL); //select函数报错,直接退出循环 if (rc < 0) { printf("select failed ! error message :%s\n", strerror(errno)); break; } if (FD_ISSET(listen_st, &allset)) { rc--; //处理服务端socket事件 int client_st = accept_client(listen_st); if (client_st < 0) { /* * 如果accept失败,直接退出select, * continue不太合适,因为其他的客户端事件还没有处理,这样就会丢事件 */ break; } //将客户端socket放到socket池中 for (i = 0; i < MAX_SOCKET_NUM; i++) { if (client[i] == -1) { client[i] = client_st; break; } } if (i == MAX_SOCKET_NUM) { printf("服务器端socket池已经满了"); //socket池已满,关闭客户端连接 close_socket(client_st); } } //处理客户端事件 for (i = 0; i < MAX_SOCKET_NUM; i++) { //如果事件数组中的事件已经处理完成,直接进入跳出当前循环 if (rc <= 0) { break; } if (client[i] != -1) { if (FD_ISSET(client[i], &allset)) { //该socket有事件发生 if (protect_message(client[i], client) < 0) { //接收消息失败,但是由于逻辑复杂,直接退出select //关闭客户端socket close_socket(client[i]); //将该socket从socket池中删除 client[i] = -1; break; } } } } } return 0; } //connect server int connect_server(char * ipaddr, int port) { int st = create_socket(); if (st < 0) { return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //type addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ipaddr); if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) < 0) { printf("connect failed ! error message :%s\n", strerror(errno)); return -1; } return st; } //thread for recv message void * thread_recv(void *arg) { int st = *(int *) arg; if (st < 0) { printf("function thread_recv param not correct!\n"); return NULL; } char buf[BUF_SIZE] = { 0 }; while (1) { if (recv(st, buf, sizeof(buf), 0) < 0) { printf("recv failed ! error message :%s\n", strerror(errno)); break; } printf("%s", buf); memset(buf, 0, sizeof(buf)); } return NULL; } //thread for send message void * thread_send(void *arg) { int st = *(int *) arg; if (st < 0) { printf("function thread_send param not correct!\n"); return NULL; } char buf[BUF_SIZE] = { 0 }; while (1) { read(STDIN_FILENO, buf, sizeof(buf)); if (send(st, buf, sizeof(buf), 0) < 0) { printf("send failed ! error message :%s\n", strerror(errno)); break; } memset(buf, 0, sizeof(buf)); } return NULL; }
//QQ客户端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <errno.h> #include "helper.h" int main(int arg, char *args[]) { if (arg < 3) { printf("please print two param !\n"); return -1; } int port = atoi(args[2]); char ipaddr[30] = { 0 }; strcpy(ipaddr, args[1]); //connect server int st = connect_server(ipaddr, port); if (st < 0) { return -1; } //recv thread pthread_t thr1, thr2; if (pthread_create(&thr1, NULL, thread_recv, &st) != 0) { printf("pthread_create failed ! error message :%s\n", strerror(errno)); return -1; } if (pthread_create(&thr2, NULL, thread_send, &st) != 0) { printf("pthread_create failed ! error message :%s\n", strerror(errno)); return -1; } //join pthread_join(thr1,NULL); pthread_join(thr2,NULL); //close socket close_socket(st); return 0; }
//QQ服务端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "helper.h" int main(int arg,char *args[]) { //服务器端需要传入端口号 if(arg<2) { printf("please print one param !\n"); return -1; } int port=atoi(args[1]); int st=server_socket(port); if(st<0) { return -1; } //开启select 监听事件 start_select(st); //close socket close_socket(st); return 0; }
.SUFFIXES:.c .o CC=gcc SRCS1=QQserver.c helper.c SRCS2=QQclient.c helper.c OBJS1=$(SRCS1:.c=.o) OBJS2=$(SRCS2:.c=.o) EXEC1=mserver EXEC2=mclient start:$(OBJS1) $(OBJS2) $(CC) -o $(EXEC1) $(OBJS1) $(CC) -o $(EXEC2) $(OBJS2) -lpthread @echo "-------ok-----------" .c.o: $(CC) -Wall -g -o [email protected] -c $< clean: rm -f $(OBJS1) rm -f $(EXEC1) rm -f $(OBJS2) rm -f $(EXEC2)
小结:
这次程序编码调试都很快,调试中第一个错误,客户端连接不上服务器,但是connect不报错,我几乎每次写网络程序都有这个问题,就目前而言有两种出错可能
出错场景1:服务端socket有问题,为0或者不是正确的socket
出错场景2:服务器端的socket没有放在select池中,没有被select监视
我暂时就只有这两种犯错场景。
调试第二个错误:“Segmentation fault”,这个错误我觉得一般都是操作内存出了问题,内存泄漏,我这边是因为在函数sockaddr_toa()中
unsigned char *p=(unsiged char *)&(addr->sin_addr.s_addr); //没有取addr->sin_addr.s_addr的地址转化成unsigned char类型,发生内存泄漏
调试第三个错误是逻辑错误,每次有客户端socket接收数据出错后,我没有从socket池中将该socket清除,导致select()函数不再阻塞,每次直接返回
以上是关于Linux Linux程序练习十二(select实现QQ群聊)的主要内容,如果未能解决你的问题,请参考以下文章
Linux命令:MySQL系列之十二--MySQL备份与还原(针对单张表SELECT备份)