1. 封装一些必要的C/S架构头文件(放在common文件夹中)
common
├── head.h
├── common.c
├── common.h
├── tcp_client.c//可以省略
├── tcp_client.h//可以省略,后面用telnet来测试就行
├── tcp_server.c
├── tcp_server.h
-
head.h
#ifndef HEAD_H #define HEAD_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <pthread.h> #include <string.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdbool.h> #include <unistd.h> #include <sys/time.h> #include <poll.h> #include <sys/select.h> #include <errno.h> #endif
-
common.h
#ifndef _COMMON_H #define _COMMON_H char conf_ans[50] = {0}; char *get_value(char *path, char *key); void make_nonblock_ioctl(int fd); void make_block_ioctl(int fd); void make_nonblock(int fd); void make_block(int fd); #endif
-
common.c
#include "head.h" char *get_value(char *path, char *key) { FILE *fp = NULL; ssize_t nrd; char *line = NULL, *sub = NULL; extern char conf_ans[50]; size_t linecap; if (path == NULL || key == NULL) { fprintf(stderr, "Error in argument!\n"); return NULL; } if ((fp = fopen(path, "r")) == NULL) { perror("fopen"); return NULL; } while ((nrd = getline(&line, &linecap, fp)) != -1) { if ((sub = strstr(line, key)) == NULL) continue; else { if (line[strlen(key)] == '=') { strncpy(conf_ans, sub + strlen(key) + 1, nrd - strlen(key) - 2); *(conf_ans + nrd - strlen(key) - 2) = '\0'; break; } } } free(line); fclose(fp); if (sub == NULL) { return NULL; } return conf_ans; } void make_nonblock_ioctl(int fd){ unsigned long ul = 1; ioctl(fd, FIONBIO, &ul); } void make_block_ioctl(int fd) { unsigned long ul = 0; ioctl(fd, FIONBIO, &ul); } void make_nonblock(int fd) { int flag; if ((flag = fcntl(fd, F_GETFL)) < 0) { return; } flag |= O_NONBLOCK; fcntl(fd, F_SETFL, flag); } void make_block(int fd) { int flag; if ((flag = fcntl(fd, F_GETFL)) < 0) { return ; } flag &= ~O_NONBLOCK; fcntl(fd, F_SETFL, flag); }
-
tcp_server.h
#ifndef _TCP_SERVER_H #define _TCP_SERVER_H int socket_create(int port); #endif
-
tcp_server.c
#include "head.h" int socket_create(int port) { int server_listen; if ((server_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return -1; } struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = INADDR_ANY; struct linger m_linger; m_linger.l_onoff = 1; m_linger.l_linger = 5; if (setsockopt(server_listen, SOL_SOCKET, SO_LINGER, &m_linger, (socklen_t)sizeof(m_linger)) < 0) {//延迟关闭的实现 return -1; } int flag = 1; if (setsockopt(server_listen, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {//地址重用 return -1; } if (bind(server_listen, (struct sockaddr *)&server, sizeof(server)) < 0) { return -1; } if (listen(server_listen, 20) < 0){ return -1; } /* int maxfd = server_listen; struct timeval tv; int retval; fd_set rfds; FD_ZERO(&rfds); FD_SET(maxfd, &rfds); int sub, fd; tv.tv_sec = 5; tv.tv_usec = 0; retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if(retval == -1) { perror("select()"); } else if(retval) { printf("Data is available now.\n"); } else { printf("No data within five seconds.\n"); } if ((fd = accept(maxfd, NULL, NULL)) < 0) { perror("accept"); } if(fd > maxfd) { maxfd = fd; }*/ return server_listen; }
-
tcp_client.h
#ifndef _TCP_CLIENT_H #define _TCP_CLIENT_H int socket_connect(char *host, int port); int socket_connect_timeout(char *host, int port, long timeout); #endif
-
tcp_client.c
#include "head.h" #include "common.h" int socket_connect(char *host, int port) { int sockfd; struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = inet_addr(host); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } printf("Socket create.\n"); if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("connect"); return -1; } return sockfd; } int socket_connect_timeout(char *host, int port, long timeout) {//此处就是用select实现的超时 int sockfd; struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = inet_addr(host); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } make_nonblock(sockfd); struct timeval tv; fd_set wfds; FD_ZERO(&wfds); FD_SET(sockfd, &wfds); tv.tv_sec = 0; tv.tv_usec = timeout; if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { int retval, error = -1; int len = sizeof(int); retval = select(sockfd + 1, NULL, &wfds, NULL, &tv); if(retval < 0) { close(sockfd); return -1; } else if(retval) { //ok; if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error,(socklen_t *)&len ) < 0) { close(sockfd); return -1; } if(error) { close(sockfd); return -1; } } else { //timeout; printf("Connect Time Out!\n"); close(sockfd); return -1; } } make_block(sockfd); return sockfd; }
2. server_select
select.c
#include"../common/head.h"
#include"../common/color.h"
#include"../common/common.h"
#include"../common/tcp_server.h"
#define MAX_N 512
#define BUFFSIZE 1024
char ch_char(char c) {
if(c >= 'a' && c <= 'z') {
return c - 32;
}
return c;
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Usage: %s port!\n", argv[0]);
exit(1);
}
int server_listen, fd, client[MAX_N] = {0};
if((server_listen = socket_create(atoi(argv[1]))) < 0) {
perror("socket_create");
exit(1);
}
make_nonblock(server_listen);//变为非阻塞
int maxfd = server_listen;//开始server_listen最大
fd_set rfds, wfds, efds;
for(int i = 0; i < MAX_N; i++) {
client[i] = -1;
}
client[server_listen] = server_listen;
FD_SET(server_listen, &rfds);//先把开始的第一个加入到rfds集合中
while(1) {
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
// FD_SET(server_listen, &rfds);
for(int i = 0; i < MAX_N; i++) {//因为FD_SET不能重用,所以要每次都得重置一波
// if(client[i] == server_listen)continue;
if(client[i] > 0) {
if(maxfd < client[i])maxfd = client[i];//寻找最大的fd
FD_SET(client[i], &rfds);
}
}
if(select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0) {//开始阻塞
perror("select");
return 1;
}
//此时是由于select无限期的阻塞
if(FD_ISSET(server_listen, &rfds)) {//如果server_listen是符合rdfs的,sever_listen特殊
printf("Connect ready on serverlisten!\n");
if((fd = accept(server_listen, NULL, NULL))< 0) {
perror("accept");
return 1;
}
if(fd > MAX_N) {//如果新的fd > MAX_N, 认为客户端太多了;
printf("Too many clients!\n");
close(fd);
} else {//认为他是有用的
make_nonblock(fd);//设置为非阻塞的
if(client[fd] == -1) {
client[fd] = fd;//因为下表对应的fd
}
}
}
for(int i = 0; i < MAX_N; i++) {
if(i == server_listen)continue;//特殊的server_listen
if(FD_ISSET(i, &rfds)) {//i就是fd
char buff[BUFFSIZE] = {0};
memset(buff,0, sizeof(buff));
int retval = recv(i, buff, BUFFSIZE, 0);//收
if(retval <= 0) {//没有收到
printf("Logout!");
client[i] = -1;//client归位
close(i);
continue;
}
for(int j = 0; j < strlen(buff); j++) {
buff[j] = ch_char(buff[j]);
}
FILE *fp;
fp = fopen("a.log", "a+");
fwrite(buff, sizeof(buff), 1, fp);
fclose(fp);
printf("Recv: %s , %ld\n", buff, strlen(buff));
char buff1[BUFFSIZE];
if(strlen(buff) == 2) {
fp = fopen("a.log", "r");
while(fread(buff1, BUFFSIZE, 1, fp) > 0) {
send(i, buff1, strlen(buff1), 0);
}
fp = fopen("a.log", "w+");
fflush(fp);
}
}
}
}
return 0;
}
3. server_poll
#include"../common/head.h"
#include"../common/color.h"
#include"../common/common.h"
#include"../common/tcp_server.h"
#define POLLSIZE 100
#define BUFFSIZE 512
char ch_char(char c) {
if(c >= 'a' && c <= 'z') {
return c - 32;
}
return c;
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Usage: %s port!\n", argv[0]);
exit(1);
}
int server_listen, fd;
if((server_listen = socket_create(atoi(argv[1]))) < 0) {
perror("socket_create");
exit(1);
}
struct pollfd event_set[POLLSIZE];//client[];
for(int i = 0; i < POLLSIZE; i++) {
event_set[i].fd = -1;
}
event_set[0].fd = server_listen;
event_set[0].events = POLLIN;
while(1) {
int retval;
/*阻塞监听是佛有客户端链接请求*/
if((retval = poll(event_set, POLLSIZE, -1)) < 0) {
perror("poll");
return 1;
}
/*listenfd有读时间就绪*/
if(event_set[0].revents & POLLIN) {
/*接受客户端请求accept不会阻塞*/
if((fd = accept(server_listen, NULL, NULL)) < 0) {
perror("accept");
continue;
}
retval --;
int i = 1;
for(i = 1; i < POLLSIZE; i++) {
if(event_set[i].fd < 0) {/*找到空闲位置,存放accept返回的connfd*/
event_set[i].fd = fd;
event_set[i].events = POLLIN;
break;
}
}
if(i == POLLSIZE) {/*达到了最大客户端数*/
printf("Too many clients!\n");
}
}
for(int i = 1; i < POLLSIZE; i++) {/*检测client[]看是哪个fd满足*/
if(event_set[i].fd < 0)continue;
if(event_set[i].revents & (POLLIN | POLLHUP | POLLERR)) {
retval--;
char buff[BUFFSIZE] = {0};
if(recv(event_set[i].fd, buff, BUFFSIZE, 0) > 0) {
printf("Recv : %s \n", buff);
for(int i = 0; i < strlen(buff); i++) {
buff[i] = ch_char(buff[i]);
}
send(event_set[i].fd, buff,strlen(buff), 0);
} else {
close(event_set[i].fd);
event_set[i].fd = -1;/*poll中不监控该文件描述符,直接置位-1,不像select中那样移除*/
}
}
if(retval <= 0)break;
}
}
return 0;
}
4. server_epoll
server.c
#include <sys/epoll.h>
#define MAX_EVENTS 10
#include "../common/head.h"
#include "../common/tcp_server.h"
#include "../common/common.h"
#define BUFFSIZE 512
int main(int argc, char **argv) {
struct epoll_event ev, events[MAX_EVENTS];/*ev:epoll_ctl参数 events[]:epoll_wait参数*/
int listen_sock, conn_sock, nfds, epollfd;
char buff[BUFFSIZE] = {0};
if (argc != 2) exit(1);
listen_sock = socket_create(atoi(argv[1]));
/* Code to set up listening socket, 'listen_sock',
(socket(), bind(), listen()) omitted */
epollfd = epoll_create1(0);/*创建epoll模型,epollfd指向红黑树根节点*/
if (epollfd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;/*指定lfd的监听事件为"读"*/
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {/*将lfd及对应的结构体设置到树上,epollfd可找到该树*/
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
/*epoll为server阻塞监听事件,events为struct epoll_event类型数组,MAX_EVENTS维数组容量,-1代表永久阻塞*/
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) { //判断事件fd是不是lfd;
conn_sock = accept(listen_sock, NULL, NULL); //接受链接
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
make_nonblock(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {//不是lfd
//do_use_fd(events[n].data.fd);
if (events[n].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) {
memset(buff, 0, sizeof(buff));
if (recv(events[n].data.fd, buff, BUFFSIZE, 0) > 0) {
printf("recv: %s", buff);
for (int i = 0; i < strlen(buff); i++) {
if (buff[i] >= 'a' && buff[i] <= 'z') buff[i] -= 32;
}
send(events[n].data.fd, buff, strlen(buff), 0);
} else {
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, events[n].data.fd, NULL) < 0) {//将该文件描述符从红黑树摘除
perror("epoll_ctrl");
}
close(events[n].data.fd);
printf("Logout!\n");
}
}
}
}
}
return 0;
}