C/S系統實現兩數求和html
任務要求:數組
實現配置文件服務器
實現日誌滾動socket
設置非阻塞套接字,EPOLL實現ide
檢測客戶端的鏈接,設置心跳檢測ui
主線程 + 心跳檢測線程 + EPOLL的ET模式處理事務線程spa
注意事項:設置volatile類型跳出死循環線程
做品簡介:3d
本次實驗分爲bin,config,include,log,src五個文件夾以及一個makefile文件,下面是裏面的具體內容以及功能詳解:日誌
bin: 存放二進制文件,生成的可執行文件,執行本系統只需在超級終端打開便可。
config: 存放配置文件,客戶能夠在裏面設置所要鏈接的服務器的ip以及端口,以及所要發送的數據。
include: 存放本系統的頭文件。
log:存放日誌文件,當一個文件大於10M的時候會新建一個文件存放名是log+數字,後綴名是txt。
src:存放本系統的源代碼,裏面兩個文件夾,client存放客戶機的源代碼,server存放服務器的源代碼。
makefile:本系統的編譯文件,在終端打開主目錄,輸入命令make便可。
具體實現:
客戶機:
從ini配置文件中獲得鏈接服務器的ip以及端口,向服務端發送數據包,以及心跳檢測發送心跳包。
客戶端是以阻塞的方式向服務端發送數據(因爲公司業務主要在服務端,因此在這裏不用過多要求),主線程分出一個發送心跳線程每隔3秒向用戶發送一個數據包;數據包格式:
頭部是一個短整型,存放數據包的格式;
0表明發送的是心跳包,4表明發送的是不完整包,8表明發送的包完整。
而後是一個長度爲16的字符數組,存放客戶的名字,這裏用客戶的ip來代替。
接下來是兩個整型表明發送的兩個數字,也就是本任務發送的主要數據。
客戶端每隔3秒發送一個心跳包,由服務器檢測心跳,從配置文件讀取發送的數據向客戶端發送數據,由服務器檢測數據完整性,ip和端口存放在配置文件的IP項目下,數據在TWONUMBER項目下。
接收從服務端發來的數據,用data_type判斷當前數據是否完整,完整就接受輸出從服務器獲得的數據;
服務器:
服務器實現檢測鏈接,epoll處理事物,兩種工做模式(et, lt)
主線程裏面包含兩個線程,心跳檢測線程以及et/lt事物處理線程:
心跳檢測線程:
檢測用戶是否掉線,每隔3秒計數器加1,當et接收到客戶傳來的數據包時,重置計數器,當計數器達到5的時候斷開鏈接。用戶用鏈表鏈接。
et事物處理線程:
接受客戶端發來的鏈接,處理客戶端發來的數據包,完整性檢測。
從waitepoll中獲得事物後,判斷是鏈接請求仍是發送請求,若是是鏈接請求就接受鏈接,若是是發送請求就接收數據。
創建鏈接後要在客戶鏈表中加入客戶信息。
因爲是非阻塞模式因此要循環接收數據,接收到數據包後進行心跳處理,初始化心跳計數器,寫入日誌文件,什麼時間接收了什麼數據,或者發送了什麼數據。。。
lt事物處理線程:
因爲本次任務是et模式下,因此lt只是實現了接收數據,若是想要查看效果則須要在server.c中把et換爲lt便可,lt工做模式比較簡單,因爲lt在接收數據時,一次沒接收完,下次還會發送事物請求,因此沒必要要一次接收完,只須要判斷鏈接請求創建鏈接,發送請求接收數據便可,沒必要要循環接收。
實現代碼:
#include "handsomecui.h" #include "config.h" #include "heart_client.h" #define BUFFER_SIZE 40 int main(int argc, char *argv[]) { int client_sockfd; int len; struct sockaddr_in remote_addr; memset(&remote_addr,0,sizeof(remote_addr)); remote_addr.sin_family=AF_INET; char s_ip[20]; GetConfigFileStringValue("IPANDPORT", "IP", "127.0.0.1", s_ip, sizeof(s_ip), "Config.ini"); printf("IP : %s\n", s_ip); remote_addr.sin_addr.s_addr=inet_addr(s_ip); uint16_t port = GetConfigFileIntValue("IPANDPORT", "PORT", 8866, "Config.ini"); printf("port : %d\n", (int)port); if (port == -1) { printf("Get port failed!\n"); return -1; } remote_addr.sin_port=htons(port); client_sockfd=socket(PF_INET,SOCK_STREAM,0); if(client_sockfd<0) { perror("client socket creation failed"); exit(EXIT_FAILURE); } if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0) { perror("connect to server failed"); exit(EXIT_FAILURE); } pthread_t pth; int err; int *client_sockfd_1 = (int *)malloc(sizeof(int)); *client_sockfd_1 = client_sockfd; if((err = pthread_create(&pth, NULL, send_heart, (void *)client_sockfd_1)) != 0){ fprintf(stderr, "pthread_create: %s\n", strerror(err)); exit(1); } sleep(5); pd = (DATA_PACK *)malloc(sizeof(DATA_PACK)); char *buf = (char *)malloc(sizeof(*pd)); pd->data_type = 8; strcpy(pd->name, "127.0.0.1"); pd->num1= (int )GetConfigFileIntValue("TWONUMBER", "NUMBER1", 0xfffffff, "Config.ini"); pd->num2 = (int )GetConfigFileIntValue("TWONUMBER", "NUMBER2", 0xfffffff, "Config.ini"); len = sizeof(*pd); memcpy(buf, pd, len); send(client_sockfd,buf,len,0); free(pd); len=recv(client_sockfd,pd,BUFFER_SIZE,0); if(pd->data_type == 8) printf("receive from server %s: %d\n",pd->name, pd->num1); else printf("receive from server %s: 發送數字個數不正確\n", pd->name); if(len<0) { perror("receive from server failed"); exit(EXIT_FAILURE); } close(client_sockfd); return 0; }
config.c:配置文件
#include "config.h" void GetCompletePath(UINT8 *pszConfigFileName, UINT8 *pszWholePath) { UINT8 *pszHomePath = NULL; UINT8 szWholePath[256] = {0}; if (pszConfigFileName == NULL || pszWholePath == NULL) { printf("GetCompletePath: input parameter(s) is NULL!\n"); return; } pszHomePath = (UINT8 *)getenv("HOME"); if (pszHomePath == NULL) { printf("GetCompletePath: Can't find home path!\n"); return; } snprintf(szWholePath, sizeof(szWholePath)-1, "/home/handsome/work/socket/Cuijunyong_3/config/%s", pszConfigFileName); strncpy(pszWholePath, szWholePath, strlen(szWholePath)); } void GetStringContentValue(FILE *fp, UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen) { UINT8 szSectionName[100] = {0}; UINT8 szKeyName[100] = {0}; UINT8 szContentLine[256] = {0}; UINT8 szContentLineBak[256] = {0}; UINT32 iContentLineLen = 0; UINT32 iPositionFlag = 0; if (fp == NULL || pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL) { printf("GetStringContentValue: input parameter(s) is NULL!\n"); return; } sprintf(szSectionName, "[%s]", pszSectionName); strcpy(szKeyName, pszKeyName); while (feof(fp) == 0) { memset(szContentLine, 0x00, sizeof(szContentLine)); fgets(szContentLine, sizeof(szContentLine), fp); if (szContentLine[0] == ';' || szContentLine[0] == '\r' || szContentLine[0] == '\n' || szContentLine[0] == '\0') { continue; } if (strncasecmp(szSectionName, szContentLine, strlen(szSectionName)) == 0) { while (feof(fp) == 0) { memset(szContentLine, 0x00, sizeof(szContentLine)); memset(szContentLineBak, 0x00, sizeof(szContentLineBak)); fgets(szContentLine, sizeof(szContentLine), fp); if (szContentLine[0] == ';') { continue; } memcpy(szContentLineBak, szContentLine, strlen(szContentLine)); if (strncasecmp(szKeyName, szContentLineBak, strlen(szKeyName)) == 0) { iContentLineLen = strlen(szContentLine); for (iPositionFlag = strlen(szKeyName); iPositionFlag <= iContentLineLen; iPositionFlag ++) { if (szContentLine[iPositionFlag] == ' ') { continue; } if (szContentLine[iPositionFlag] == '=') { break; } iPositionFlag = iContentLineLen + 1; break; } iPositionFlag = iPositionFlag + 1; if (iPositionFlag > iContentLineLen) { continue; } memset(szContentLine, 0x00, sizeof(szContentLine)); strcpy(szContentLine, szContentLineBak + iPositionFlag); for (iPositionFlag = 0; iPositionFlag < strlen(szContentLine); iPositionFlag ++) { if (szContentLine[iPositionFlag] == '\r' || szContentLine[iPositionFlag] == '\n' || szContentLine[iPositionFlag] == '\0') { szContentLine[iPositionFlag] = '\0'; break; } } strncpy(pszOutput, szContentLine, iOutputLen-1); break; } else if (szContentLine[0] == '[') { break; } } break; } } } void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8 *pszConfigFileName) { FILE *fp = NULL; UINT8 szWholePath[256] = {0}; if (pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL || pszConfigFileName == NULL) { printf("GetConfigFileStringValue: input parameter(s) is NULL!\n"); return; } if (pDefaultVal == NULL) { strcpy(pszOutput, ""); } else { strcpy(pszOutput, pDefaultVal); } GetCompletePath(pszConfigFileName, szWholePath); fp = fopen(szWholePath, "r"); if (fp == NULL) { printf("GetConfigFileStringValue: open %s failed!\n", szWholePath); return; } GetStringContentValue(fp, pszSectionName, pszKeyName, pszOutput, iOutputLen); fclose(fp); fp = NULL; } INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName) { UINT8 szGetValue[512] = {0}; INT32 iGetValue = 0; if (pszSectionName == NULL || pszKeyName == NULL || pszConfigFileName == NULL) { printf("GetConfigFileIntValue: input parameter(s) is NULL!\n"); return -1; } GetConfigFileStringValue(pszSectionName, pszKeyName, NULL, szGetValue, 512-1, pszConfigFileName); if (szGetValue[0] == '\0' || szGetValue[0] == ';') { iGetValue = iDefaultVal; } else { iGetValue = atoi(szGetValue); } return iGetValue; }
heart_client.c:心跳發送
#include "handsomecui.h" #include "heart.h" void *send_heart(void *addr) { int* client_sockfd = (int*)addr; printf("client_socket: %d\n", *client_sockfd); pd = (DATA_PACK *)malloc(sizeof(DATA_PACK)); pd->data_type = 0; strcpy(pd->name, "127.0.0.1"); while(1){ // write(client_sockfd,pd,sizeof(DATA_PACK)); send(*client_sockfd,pd,sizeof(*pd),0); sleep(3); //定時3秒 } free(client_sockfd); free(pd); return NULL; }
#include "handsomecui.h" #include "addfd.h" void addfd(int epollfd, int fd, int flag){ struct epoll_event event; memset(&event, 0x00, sizeof(event)); event.data.fd = fd; event.events = EPOLLIN; if(flag){ event.events |= EPOLLET; } epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); setnonblocking(fd); }
et.c:et工做模式
#include "handsomecui.h" #include "et.h" #include "heart.h" extern s_t *s_head; #define MAX_LEN 1024 extern volatile g_stop; void *et(void *arg){ pth_etlt *pth_arg = (pth_etlt *)arg; struct epoll_event* events = pth_arg->events; int number = pth_arg->number; int epollfd = pth_arg->epollfd; int listenfd = pth_arg->listenfd; int i; DATA_PACK *data = (DATA_PACK *)malloc(sizeof(DATA_PACK)); int p = 0; DATA_PACK *num = (DATA_PACK *)malloc(sizeof(DATA_PACK)); char *buf = (char *)malloc(sizeof(*num)); int len = sizeof(*num); for(i = 0; i < number; i++){ int sockfd = events[i].data.fd; struct sockaddr_in client_address; if(sockfd == listenfd){ socklen_t client_addresslen = sizeof(client_address); int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen); addfd(epollfd, connfd, 1); printf("client ip: %s port: %d\n", inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port)); s_t *p = (s_t *)malloc(sizeof(s_t)), *q; strcpy(p->peerip, inet_ntoa(client_address.sin_addr)); strcpy(p->name, inet_ntoa(client_address.sin_addr)); p->sockfd = sockfd; p->count = 0; q = s_head->next; s_head->next = p; p->next = q; } else if(events[i].events & EPOLLIN){ // printf("ET once\r\n"); while(g_stop){ //printf("g_stop = %d\n", g_stop); //memset(data, 0x00, sizeof(*data)); int ret = recv(sockfd, data, MAX_LEN, 0); if(ret < 0){ if((errno == EAGAIN) || (errno == EWOULDBLOCK)){ //printf("read later \r\n"); break; } close(sockfd); break; } else if(ret == 0){ close(sockfd); } else{ heart_handler(sockfd, data); // printf("接受的數據長度是: %d\n", ret); // printf("recv data from %d buf id %s %d %d \r\n", sockfd, data->name, data->num1, data->num2); if(data->data_type == 8){ if(data->num1 == 0xfffffff || data->num2 == 0xfffffff){ // printf("輸入的數字個數不對\n"); WriteSysLog(data, 3); num->data_type = 4; strcpy(num->name, "127.0.0.1"); send(sockfd, buf, len, 0); break; } WriteSysLog(data, 0); strcpy(num->name, "127.0.0.1"); num->data_type = 8; num->num1 = data->num1 + data->num2; memcpy(buf, num, len); WriteSysLog(num, 1); send(sockfd, buf, len, 0); } } } } else{ printf("作了另外的事情\r\n"); } } pthread_exit((void *)data); }
heart.c:心跳檢測,心跳處理
#include "handsomecui.h" #include "heart.h" extern s_t *s_head; extern volatile g_stop; void init_shead(){ s_head = (s_t *)malloc(sizeof(s_t)); } void heart_handler(int sockfd,DATA_PACK *pd) { s_t *cur = s_head->next; while( NULL != cur){ if(strcmp(cur->name,pd->name) == 0){ cur->count = 0; printf("客戶端IP: %s :用戶 %s 鏈接正常\n",cur->peerip,pd->name); } cur = cur->next; } } void *heart_check(void *p) { printf("心跳檢測線程已開啓!\n"); while(g_stop){ check_handler(); sleep(3); // printf("stop = %d\n", g_stop); } // puts("******"); pthread_exit((void *)1); } void check_handler(){ s_t *temp = NULL; s_t **ppNode = &s_head->next; while(NULL != (*ppNode) && g_stop){ if((*ppNode)->count == 5){ g_stop = 0; printf("客戶端IP: %s :用戶 %s 已經掉線!!\n",(*ppNode)->peerip,(*ppNode)->name); close((*ppNode)->sockfd); temp = *ppNode; *ppNode = (*ppNode)->next; free(temp); temp = NULL; return; } else if((*ppNode)->count > 0){ printf("客戶端IP: %s :用戶 %s 鏈接異常!\n",(*ppNode)->peerip,(*ppNode)->name); (*ppNode)->count++; printf("count = %d\n",(*ppNode)->count); ppNode = &((*ppNode)->next); continue; } else if((*ppNode)->count == 0){ (*ppNode)->count++; printf("count = %d\n",(*ppNode)->count); ppNode = &((*ppNode)->next); } else; } }
log.c:滾動日誌文件
#include "handsomecui.h" #include "log.h" typedef unsigned char UINT8; typedef signed int INT32; typedef unsigned int UINT32; void WriteSysLog(DATA_PACK *str, int s_w){ char buf[512], temp[20]; long MAXLEN = 10*1024*1024; time_t timep; int fp; struct tm *p; time(&timep); p = localtime(&timep); memset(buf,0,sizeof(buf)); sprintf(buf,"%d-%d-%d %d:%d:%d : ",(1900+p->tm_year),(1+p->tm_mon),\ p->tm_mday,p->tm_hour, p->tm_min, p->tm_sec); if(!s_w) strcat(buf, "收到了"); sprintf(temp, "%s", str->name); strcat(buf, temp); if(s_w) strcat(buf, "服務器發送了數據: "); if(!s_w) strcat(buf, "傳來的數據: "); if(s_w < 3){ sprintf(temp, "%d", str->num1); strcat(buf, temp); strcat(buf, "\t"); sprintf(temp, "%d", str->num2); if(!s_w) strcat(buf, temp); strcat(buf, "\n"); } else strcat(buf, "發送數字數目不正確\n"); UINT8 *pszHomePath = (UINT8 *)getenv("HOME"); UINT8 szWholePath[256] = {0}; UINT8 pszConfigFileName[20]; strcpy(pszConfigFileName, "log.txt"); if (pszHomePath == NULL) { printf("GetCompletePath: Can't find home path!\n"); return; } snprintf(szWholePath, sizeof(szWholePath)-1, "/home/handsome/work/socket/Cuijunyong_3/log/%s", pszConfigFileName); //puts(szWholePath); fp = open(szWholePath,O_RDONLY|O_CREAT|O_WRONLY, 0644); if(fp < 0) { fprintf(stderr, "open file 1 error: %s\n", strerror(errno)); } else { off_t n = lseek(fp, 0, SEEK_END); // printf("文件的字節數是:%d\n", (int)n); if(n >= MAXLEN) { close(fp); int i; for(i = 1; i < 100; i++){ strcpy(pszConfigFileName, "log"); temp[0] = i + '0'; temp[1] = '\0'; strcat(pszConfigFileName, temp); strcat(pszConfigFileName, ".txt"); snprintf(szWholePath, sizeof(szWholePath)-1, "/home/handsome/work/socket/Cuijunyong_3/log/%s", pszConfigFileName); puts(szWholePath); fp = open(szWholePath,O_RDONLY|O_CREAT|O_WRONLY, 0644); n = lseek(fp, 0, SEEK_END); if(n >= MAXLEN){ close(fp); continue; } else{ puts(buf); write(fp, buf, strlen(buf)); close(fp); return; } } } else { printf("%s", buf); write(fp, buf, strlen(buf)); close(fp); } } }
lt.c:lt工做模式
#include "handsomecui.h" #include "lt.h" void lt(struct epoll_event* events, int number, int epollfd, int listenfd){ char buf[BUF_SIZE]; int i; for(i = 0; i < number; i++){ int sockfd = events[i].data.fd; if(sockfd == listenfd){ struct sockaddr_in client_address; socklen_t client_addresslen = sizeof(client_address); int connfd = accept(listenfd, (struct sockaddr *)&client_address, &client_addresslen); if(connfd < 0){ printf("接受失敗\r\n"); exit(1); } addfd(epollfd, connfd, 0); } else if(events[i].events & EPOLLIN){ printf("LT once\r\n"); memset(buf, 0x00, sizeof(buf)); int ret = recv(sockfd, buf, sizeof(buf)-1, 0); if(ret <= 0){ printf("rec 0\r\n"); close(sockfd); continue; } printf("recv data from %d buf is %s\r\n", sockfd, buf); } else{ printf("作了另外的一些事\r\n"); } } }
server.c:服務器主代碼
#include "handsomecui.h" #include "lt.h" #include "et.h" #include "addfd.h" #include "heart.h" s_t *s_head = NULL; volatile g_stop = 1; int main(int argc, char **argv){ if(argc <= 2){ printf("請輸入ip地址和端口\r\n"); exit(1); } const char* ip = argv[1]; int port = atoi(argv[2]); int ret = 0; struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; inet_pton(AF_INET, ip, &address.sin_addr); address.sin_port = htons(port); int listenfd = socket(AF_INET, SOCK_STREAM, 0); assert(listenfd >= 0); ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address)); assert(ret != -1); ret = listen(listenfd, 5); assert(ret != -1); struct epoll_event events[MAX_SOCKET_NUMBERS]; int epollfd = epoll_create(5); assert(epollfd != -1); addfd(epollfd,listenfd, 1); int err1; pthread_t pth1; init_shead(); if( (err1 = pthread_create(&pth1, NULL, heart_check, (void *)1) ) != 0){ puts("-------------------"); fprintf(stderr, "pthread_creat : %s\n", strerror(err1)); exit(1); } DATA_PACK *num; while(g_stop){ int err; int ret = epoll_wait(epollfd, events, MAX_SOCKET_NUMBERS, -1); if(ret < 0){ printf("epoll等候失敗\r\n"); exit(1); } pthread_t pth; pth_etlt arg = {events, ret, epollfd, listenfd}; if( (err = pthread_create(&pth, NULL, et, (void *)&arg) ) != 0){ fprintf(stderr, "pthread_creat : %s\n", strerror(err)); exit(1); } //pthread_join(pth, (void**)&num); //printf("num1 = %d num2 = %d\n", num->num1, num->num2); //lt(events, ret, epollfd, listenfd); } close(listenfd); return 0; }
setnonblocking.c:設置非阻塞
#include "handsomecui.h" #include "setnonblocking.h" int setnonblocking(int fd){ int old_option = fcntl(fd, F_GETFL); int new_option = old_option | O_NONBLOCK; fcntl(fd, F_SETFL, new_option); return new_option; }
配置文件config.ini:
[IPANDPORT] ;the name of IP IP=127.0.0.1 ;the age of employee PORT=8866 [TWONUMBER] ;the name of employer NUMBER1=124 ;the age of employer NUMBER2=345