Linux Linux程序练习十二(select实现QQ群聊)
生活随笔
收集整理的這篇文章主要介紹了
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()函數不再阻塞,每次直接返回
這次程序編碼調試都很快,調試中第一個錯誤,客戶端連接不上服務器,但是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群聊)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个高效的内存池实现
- 下一篇: shell 小脚本