日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux Linux程序练习十二(select实现QQ群聊)

發布時間:2025/6/15 linux 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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));//typeaddr.sin_family = AF_INET;//portaddr.sin_port = htons(port);//addressaddr.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_stmaxfd = 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//關閉客戶端socketclose_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));//typeaddr.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 serverint 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=mclientstart:$(OBJS1) $(OBJS2)$(CC) -o $(EXEC1) $(OBJS1)$(CC) -o $(EXEC2) $(OBJS2) -lpthread@echo "-------ok-----------" .c.o:$(CC) -Wall -g -o $@ -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()函數不再阻塞,每次直接返回

?

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的Linux Linux程序练习十二(select实现QQ群聊)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。