简单的Client / Server 使用 linux 伯克利 socket实现
服务器:
/* *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:监听socket static 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:数据传输socket int 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); //调整maxfd if(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); } else handle_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 //
客户端:
/* * 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; } //用于select fd_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