標籤:
server
/* *run command: *g++ server.cpp -o server && ./server */#ifndef SERVER#define SERVER#include<arpa/inet.h>#include<assert.h>#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<errno.h>#include<assert.h>#include<string.h>#include<sys/types.h>#include<unistd.h>#include<netinet/in.h>#include<sys/socket.h>#include<sys/wait.h>#include<signal.h>const int SERVPORT = 5555;//伺服器監聽連接埠號碼const int BACKLOG = 10; //最大同時串連請求數const int MAX_DATA_SIZE = 1000;const int MAX_NAME_LENGTH = 21;const int MAX_PHONE_LENGTH = 15;const int MAX_HOMEADDRESS_LENGTH = 61;struct Address{char name[MAX_NAME_LENGTH];char phone[MAX_PHONE_LENGTH];char home_address[MAX_HOMEADDRESS_LENGTH];int age;};//void insert_client_fd(int* client_list,int client_fd);//void accept_client();//void* interact_with_client(void* clients_list);void* send_msg_to_client(int client_fd, char* msg);void sig_int(int signo);void generate_phone(char* phone);void generate_name(char* name);void generate_rand_n_address(int n);bool find_phone_with_name(char* name, char* phone);bool find_name_with_phone(char* phone, char* name);bool write_address(Address** addresses, int n);void print_addresses();void handle_request(const int client_id, const char* buf);void init();void serv();void client_add(int* client, int fd);void client_del(int* client, int fd);static char const* fileName = ".address";//sock_fd:監聽socketstatic int sock_fd;/* * 讀寫鎖 */pthread_rwlock_t rwlock;int main(int argc, char* argv[]){init();//accept_client();serv();return 0;}/* * 初始化 */void init(){if (signal(SIGINT, sig_int) == SIG_ERR){perror("signal(SIGINT,sig_int)");}if (signal(SIGQUIT, sig_int) == SIG_ERR){perror("signal(SIGQUIT,sig_int)");}srand(time(0));print_addresses();//每次隨機產生10個//generate_rand_n_address(10);}void serv(){if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket建立出錯");_exit(1);}//本地地址資訊struct sockaddr_in my_addr;my_addr.sin_family = AF_INET;my_addr.sin_port = htons(SERVPORT);my_addr.sin_addr.s_addr = INADDR_ANY;////INADDR_ANY;inet_addr(serverIP);//printf("%s\n", inet_ntoa(my_addr.sin_addr));bzero(&(my_addr.sin_zero), 8);printf("before bind\n");if (bind(sock_fd, (struct sockaddr*) &my_addr, sizeof(my_addr)) == -1){perror("bind出錯!");exit(1);}printf("before listen\n");if (listen(sock_fd, BACKLOG) == -1){perror("listen出錯");exit(1);}int client[BACKLOG + 1];memset(client, -1, sizeof(int) * (BACKLOG + 1));/* * 最大的fd */int maxfd;fd_set rset, allset;FD_ZERO(&allset);FD_SET(sock_fd,&allset);maxfd = sock_fd;for (;;){rset = allset;if (select(maxfd + 1, &rset, NULL, NULL, NULL) < 0){perror("select error");}//client_fd:資料轉送socketint client_fd;char buf[MAX_DATA_SIZE];//監聽fd 準備好了if (FD_ISSET(sock_fd,&rset)){//用戶端地址資訊struct sockaddr_in remote_addr;socklen_t sin_size;sin_size = sizeof(struct sockaddr_in);printf("before accept\n");if ((client_fd = accept(sock_fd, (struct sockaddr*) &remote_addr,&sin_size)) == -1){perror("accept出錯\n");continue;}client_add(client, client_fd);FD_SET(client_fd,&allset);//調整maxfdif(client_fd>maxfd)maxfd=client_fd;printf("received a connection from %s assigned fd=%d\n", inet_ntoa(remote_addr.sin_addr), client_fd);if (vfork() == 0){//服務程式:子進程//完成發送訊息後,退出char buf[MAX_DATA_SIZE];sprintf(buf, "Hello,Your Number is %d", client_fd);send_msg_to_client(client_fd, buf);exit(0);}}//有用戶端發來請求for (int i = 0; i <= BACKLOG; i++){client_fd = client[i];//printf("client[%d]=%d\n",i,client_fd);//client_fd有請求if (client_fd > 0 && FD_ISSET(client_fd,&rset)){int receivebytes = recv(client_fd, buf, MAX_DATA_SIZE, 0);if(receivebytes<0){perror("recv error!");}else if(receivebytes==0){FD_CLR(client_fd,&allset);client_del(client,client_fd);close(client_fd);}else{buf[receivebytes] = '\0';printf("received cmd from %d:%s\n", client_fd, buf);if (strncmp(buf, "quit", 4) == 0){FD_CLR(client_fd,&allset);client_del(client,client_fd);close(client_fd);printf("client_fd id=%lu quit\n", client_fd);}elsehandle_request(client_fd, buf);}}//if (client_fd > 0 && FD_ISSET(client_fd,&rset))}//for (int i = 0; i <= maxfd; i++)}}void client_add(int* client, int fd){for (int i = 0; i <= BACKLOG; i++){if (client[i] == -1){client[i] = fd;break;//居然兩次,都忘記了break,編程水平太低了...}}}void client_del(int* client, int fd){for (int i = 0; i <= BACKLOG; i++){if (client[i] == fd)client[i] = -1;}}void handle_request(const int client_id, const char* buf){char* helpString = "help #顯示可用的命令\n""list #顯示通訊錄所有內容(假設沒有重名)\n""name TheNameString#查詢手機號\n""phon ThePhoneNumberString#phone 查詢名字\n""shel #本地shell \n""quit #quit\n""inse #insert 暫時不實現\n";if (strncmp(buf, "help", 4) == 0){send_msg_to_client(client_id, helpString);}else if (strncmp(buf, "list", 4) == 0){FILE* fp = fopen(fileName, "r");if (fp == NULL){perror("fopen error!");return;}else{Address addr;char buf[MAX_DATA_SIZE];int count = 1;while (fread(&addr, sizeof(Address), 1, fp) == 1){sprintf(buf, "%-5dname:'%s',phone=%s,home_address=%s,age:%d\n",count, addr.name, addr.phone, addr.home_address,addr.age);//一次發送一條記錄send_msg_to_client(client_id, buf);count++;}}fclose(fp);}else if (strncmp(buf, "name", 4) == 0){char name[MAX_NAME_LENGTH];strcpy(name, buf + 5);printf("name=%s\n", name);char phone[MAX_PHONE_LENGTH];phone[0] = '\0';find_phone_with_name(name, phone);char buf[MAX_DATA_SIZE];if (strlen(phone) > 0){sprintf(buf, "phone=%s\n", phone);}else{sprintf(buf, "No such Name=%s\n", name);}send_msg_to_client(client_id, buf);}else if (strncmp(buf, "phon", 4) == 0){char phone[MAX_PHONE_LENGTH];strcpy(phone, buf + 5);printf("phone=%s\n", phone);char name[MAX_NAME_LENGTH];name[0] = '\0';find_name_with_phone(name, phone);char buf[MAX_DATA_SIZE];if (strlen(name) > 0){sprintf(buf, "name=%s\n", name);}else{sprintf(buf, "No such Phone Number=%s\n", phone);}send_msg_to_client(client_id, buf);}/* * 線上程中,不可以放在handle_request中處理 * (還沒有找到原因) */else{char temp[MAX_DATA_SIZE];sprintf(temp, "Unknow Command:%s\n\ttry help\n", buf);send_msg_to_client(client_id, temp);}}/* * 保證SIGINT,SIGQUIT,可以正常關閉串連 */void sig_int(int signo){printf("\nclose(%d)\nexit\n", sock_fd);close(sock_fd);_exit(0);}/* * 發送訊息到client_fd */void* send_msg_to_client(int client_fd, char* msg){assert(msg!=NULL);if (send(client_fd, msg, strlen(msg), 0) == -1){perror("send出錯\n");}return NULL;}/* * 隨機產生 n 項 */void generate_rand_n_address(int n){Address** addresses = (Address**) malloc(sizeof(Address*) * n);for (int i = 0; i < n; i++){addresses[i] = (Address*) malloc(sizeof(Address));generate_name(addresses[i]->name);generate_phone(addresses[i]->phone);addresses[i]->age = rand() % 100 + 10;printf("generate:name->%s\tphone->%s\tage=%d\n", addresses[i]->name,addresses[i]->phone, addresses[i]->age);}write_address(addresses, n);//釋放空間for (int i = 0; i < n; i++)free(addresses[i]);free(addresses);}/* * 將數組addresses,n個address寫入檔案 */bool write_address(Address* addresses[], int n){FILE* fp = fopen(fileName, "a");if (fp == NULL){perror("fopen error!\n");exit(1);}else{for (int i = 0; i < n; i++)if (fwrite(addresses[i], sizeof(Address), 1, fp) < 0){perror("fwrite error!\n");exit(1);}}fclose(fp);return 1;}/* * 尋找name 用電話號碼 phone */bool find_name_with_phone(char* name, char* phone){assert(name!=NULL);assert(phone!=NULL);FILE* fp = fopen(fileName, "r");if (fp == NULL){perror("fopen error!\n");return 0;}else{Address addr;while (fread((char*) &addr, sizeof(Address), 1, fp) == 1){if (strcmp(addr.phone, phone) == 0){strcpy(name, addr.name);return 1;}}}return 0;}/* * 重名的 返迴文件中第一個名字的phone * 成功則,phone是結果並返回1,否則返回0,phone不變 */bool find_phone_with_name(char* name, char* phone){assert(name!=NULL);assert(phone!=NULL);FILE* fp = fopen(fileName, "r");if (fp == NULL){perror("fopen error!\n");return 0;}else{Address addr;while (fread((char*) &addr, sizeof(Address), 1, fp) == 1){if (strcmp(addr.name, name) == 0){strcpy(phone, addr.phone);return 1;}}}return 0;}/* * 將檔案(若有)的所有通訊錄輸出到標準輸出 */void print_addresses(){FILE* fp = fopen(fileName, "r");if (fp == NULL){perror("fopen error!");return;}else{Address addr;int count = 1;while (fread(&addr, sizeof(Address), 1, fp) == 1){printf("%-5dname:'%s',phone=%s,home_address=%s,age:%d\n", count,addr.name, addr.phone, addr.home_address, addr.age);count++;}}fclose(fp);}/* * 隨機產生一個字串 長度小於 MAX_NAME_LENGTH */char* alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";void generate_name(char* name){int len = rand() % MAX_NAME_LENGTH;if (len < 5){len += 5;}for (int i = 0; i < len; i++){name[i] = alphabet[rand() % (sizeof(alphabet))];}name[len] = '\0';}/* * 產生11位的電話號碼 */char* number = "0123456789";void generate_phone(char* phone){phone[0] = '1';for (int i = 1; i < 11; i++){phone[i] = number[rand() % 10];}phone[11] = '\0';//末尾}#endif //
client
/* * run command *g++ client.cpp -o client && ./client 192.168.111.139#serverIP */#ifndef CLIENT#define CLIENT#include<sys/select.h>#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#include<netdb.h>#include<sys/types.h>#include<unistd.h>#include<pthread.h>#include<arpa/inet.h>#include<netinet/in.h>#include<signal.h>#include<sys/socket.h>const int SERVPORT = 5555;const int MAX_DATA_SIZE = 1000;//每次最大資料轉送量//void* send_msg_to_server(void* args);void* interact_with_server(void* agrs);void sig_int(int signo);static int sockfd = -1;int main(int argc, char* argv[]){if (signal(SIGINT, sig_int) == SIG_ERR){perror("signal(SIGINT,sig_int)");}if (signal(SIGQUIT, sig_int) == SIG_ERR){perror("signal(SIGQUIT,sig_int)");}if (argc < 2){printf("請輸入server IP\n");exit(1);}char* serverIP = argv[1];struct sockaddr_in serv_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERVPORT);serv_addr.sin_addr.s_addr = inet_addr(serverIP);//*((struct in_addr*)host->h_addr);printf("serverIP:%s\n", inet_ntoa(serv_addr.sin_addr));bzero(&(serv_addr.sin_zero), 8);int recvbytes;char buf[MAX_DATA_SIZE];if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket建立出錯!");exit(1);}if (connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1){perror("connect出錯!");sleep(1);}if (sockfd < 0){return NULL;}//用於selectfd_set readSet;FD_ZERO(&readSet);FD_SET(sockfd,&readSet);//FD_SET(STDIN_FILENO,&readSet);int reseivebytes;//char buf[MAX_DATA_SIZE];while (1){fd_set readSet_temp;readSet_temp = readSet;//一次使用 1s接收資料timeval tvptr;tvptr.tv_sec = 1;tvptr.tv_usec = 0;bool flag_recived = 0;int rs;while ((rs = select(sockfd + 1, &readSet_temp, NULL, NULL, &tvptr)) > 0){flag_recived = 1;//recv準備好了if ((reseivebytes = recv(sockfd, buf, MAX_DATA_SIZE, 0)) == -1){perror("recv 錯誤!\n");exit(1);}else if (reseivebytes > 0){buf[reseivebytes] = '\0';printf("received msg from server:\n%s\n", buf);}else if (reseivebytes < 0){//串連已斷開printf("close(%d)\n", sockfd);close(sockfd);return NULL;}memset(buf, 0, MAX_DATA_SIZE);//readSet_temp還原readSet_temp = readSet;}if (rs < 0){perror("select!");}/* *沒有recv不必輸出提示資訊 *///if(flag_recived==1)printf("input cmd sended to server-->");/* * 讀入一行(包含末尾的'\n'),去除'\n'='\0' */fgets(buf, MAX_DATA_SIZE, stdin);/* * 空行不發送 */if (strlen(buf) > 1){buf[strlen(buf) - 1] = '\0';if (strncmp(buf, "shel", 4) == 0){system(buf + 5);}else if (send(sockfd, buf, strlen(buf), 0) == -1){perror("send 出錯!\n");}if (strncmp(buf, "quit", 4) == 0){close(sockfd);exit(0);}}}close(sockfd);return 0;}void sig_int(int signo){printf("\nclose(%d)\n", sockfd);close(sockfd);_exit(0);}#endif
簡單的Client / Server 使用 linux 伯克利 socket實現 編輯