通过socket扫描本机打开的tcp端口号,模拟用户名、密码登录服务器的过程、socket文件传输及模仿http服务器。
(1)scanport.c:
// 端口扫描程序,只支持扫描TCP端口 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> // 定义一个端口区间信息 typedef struct _port_segment { struct in_addr dest_ip; // 目标IP unsigned short int min_port; // 起始端口 unsigned short int max_port; // 最大端口 } port_segment; /*自定义的错误处理函数*/ void my_err(const char * err_string, int line) { fprintf(stderr, "line:%d ", line); perror(err_string); exit(1); } /* * 描 述:扫描某一IP地址上的某一个端口的函数 * 返回值:-1 出错 * 0 目标端口未打开 * 1 目标端口已打开 */ int do_scan(struct sockaddr_in serv_addr) { int conn_fd; int ret; // 创建一个TCP套接字 conn_fd = socket(AF_INET, SOCK_STREAM,0); if (conn_fd < 0) { my_err("socket", __LINE__); } // 向服务器端发送连接请求 if ((ret = connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof (struct sockaddr))) < 0 ) { if (errno == ECONNREFUSED) { // 目标端口未打开 close(conn_fd); return 0; } else { // 其他错误 close(conn_fd); return -1; } } else if (ret == 0){ printf("port %d found in %s\n", ntohs(serv_addr.sin_port), inet_ntoa(serv_addr.sin_addr)); close(conn_fd); return 1; } return -1; // 实际执行不到这里,只是为了消除编译程序时产生的警告 } // 执行扫描的线程,扫描某一区间的端口 void * scaner(void *arg) { unsigned short int i; struct sockaddr_in serv_addr; port_segment portinfo; // 端口信息 // 读取端口区间信息 memcpy(&portinfo, arg, sizeof(struct _port_segment)); // 初始化服务器端地址结构 memset(&serv_addr, 0, sizeof (struct sockaddr_in)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = portinfo.dest_ip.s_addr; //printf("******serv_addr.sin_addr.s_addr=%s\n", serv_addr.sin_addr.s_addr); //printf("*******portinfo.min_port=%d, portinfo.max_port=%d\n", portinfo.min_port, portinfo.max_port); for (i=portinfo.min_port; i<=portinfo.max_port; i++) { serv_addr.sin_port = htons(i); if (do_scan(serv_addr) < 0) { continue; // 出错则退出进程 } } return NULL; } /* * 命令行参数:-m 最大端口, -a 目标主机的IP地址, -n 最大线程数 */ int main(int argc, char **argv) { pthread_t* thread; // 指向所有的线程ID int max_port; // 最大端口号 int thread_num; // 最大线程数 int seg_len; // 端口区间长度 struct in_addr dest_ip; // 目标主机IP int i; // 检查参数个数 if (argc != 7) { printf("Usage: [-m] [max_port] [-a] [serv_address] [-n] [thread_number]\n"); exit(1); } // 解析命令行参数 for (i=1; i<argc; i++) { if (strcmp("-m", argv[i]) == 0) { max_port = atoi(argv[i+1]); // 将字符串转化为对应的整数 if (max_port < 0 || max_port >= 65535) { //65535会导致死循环 printf("Usage:invalid max dest port\n"); exit(1); } continue; } if (strcmp("-a", argv[i]) == 0) { if (inet_aton(argv[i+1], &dest_ip) == 0) { printf("Usage:invalid dest ip address\n"); exit(1); } continue; } if (strcmp("-n", argv[i]) == 0) { thread_num = atoi(argv[i+1]); if (thread_num <= 0) { printf("Usage:invalid thread_number\n"); exit(1); } continue; } } // 如果输入的最大端口号小于线程数,则将线程数设为最大端口号 if (max_port < thread_num) { thread_num = max_port; } seg_len = max_port / thread_num; if ( (max_port%thread_num) != 0 ) { thread_num += 1; } //printf("max_port=%d, seg_len=%d, thread_num = %d\n", max_port, seg_len, thread_num); // 分配存储所有线程ID的内存空间 thread = (pthread_t*)malloc(thread_num*sizeof(pthread_t)); // 创建线程,根据最大端口号和线程数分配每个线程扫描的端口区间 port_segment *portinfo = (port_segment*)malloc(thread_num * sizeof(port_segment)); for (i=0; i<thread_num; i++) { portinfo[i].dest_ip = dest_ip; portinfo[i].min_port = i*seg_len + 1; //printf("portinfo.min_port=%d, seg_len=%d, i = %d\n", portinfo[i].min_port, seg_len, i); if (i == thread_num - 1) { portinfo[i].max_port = max_port; } else { portinfo[i].max_port = portinfo[i].min_port + seg_len - 1; } // 创建线程 //if (pthread_create(&thread[i], NULL, scaner, (void *)(portinfo + i)) != 0) { if (pthread_create(&thread[i], NULL, scaner, (void *)(&portinfo[i])) != 0) { my_err("pthread_create", __LINE__); } } for (i=0; i<thread_num; i++) { // 主线程等待子线程结束 pthread_join(thread[i], NULL); } free(portinfo); return 0; }
(2)编译运行
gcc -o scanport scanport.c -lpthread ./scanport -m 65534 -a 127.0.0.1 -n 1
// Client/Server模型的服务器端 #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <stdlib.h> #define BUFSIZE 1024 #define SERV_PORT 4507 // 服务器端的端口 #define LISTENQ 12 // 连接请求队列的最大长度 #define INVALID_USERINFO 'n' // 用户信息无效 #define VALID_USERINFO 'y' // 用户信息有效 #define USERNAME 0 // 接收到的是用户名 #define PASSWORD 1 // 接收到的是密码 struct userinfo { // 保存用户名和密码的结构体 char username[32]; char password[32]; }; struct userinfo users[ ] = { {"linux", "unix"}, {"4507", "4508"}, {"clh", "clh"}, {"xl", "xl"}, {" "," "} // 以只含一个空格的字符串作为数组的结束标志 }; /*自定义的错误处理函数*/ void my_err(const char * err_string, int line) { fprintf(stderr, "line:%d ", line); perror(err_string); exit(1); } /* * 函数名: my_recv * 描 述 : 从套接字上读取一次数据(以'\n'为结束标志) * 参 数 : conn_fd -- 从该连接套接字上接收数据 * data_buf -- 读取到的数据保存在此缓冲中 * len -- data_buf所指向的空间长度 * 返回值: 出错返回-1, 服务器端已关闭连接则返回0, 成功返回读取的字节数 */ int my_recv(int conn_fd, char *data_buf, int len) { static char recv_buf[BUFSIZE]; // 自定义缓冲区,BUFSIZE定义在my_recv.h中 static char *pread; // 指向下一次读取数据的位置 static int len_remain = 0; // 自定义缓冲区中剩余字节数 int i; // 如果自定义缓冲区中没有数据,则从套接字读取数据 if (len_remain <= 0) { if ((len_remain =recv(conn_fd, recv_buf, sizeof (recv_buf), 0)) < 0) { my_err("recv", __LINE__); } else if (len_remain == 0) { // 目的计算机端的socket连接关闭 return 0; } pread = recv_buf; // 重新初始化pread指针 } // 从自定义缓冲区中读取一次数据 for (i=0; *pread != '\n'; i++) { if (i > len) { // 防止指针越界 return -1; } data_buf[i] = *pread++; len_remain--; } // 去除结束标志 len_remain--; pread++; return i; // 读取成功 } // 查找用户名是否存在,存在返回该用户名的下标,不存在则返回-1,出错返回-2 int find_name(const char *name) { int i; if (name == NULL) { printf("in find_name, NULL pointer"); return -2; } for (i=0; users[i].username[0] != ' ';i++) { if (strncmp(users[i].username, name, strlen(users[i].username)) == 0) { return i; } } return -1; } // 发送数据 void send_data(int conn_fd, const char *string) { if (send(conn_fd, string, strlen(string), 0) < 0) { my_err("send", __LINE__); // my_err函数在my_recv.h中声明 } } int main() { int sock_fd, conn_fd; int optval; int flag_recv = USERNAME; // 标识接收到的是用户还是密码 int ret; int name_num; pid_t pid; socklen_t cli_len; struct sockaddr_in cli_addr, serv_addr; char recv_buf[128]; // 创建一个TCP套接字 sock_fd = socket(AF_INET, SOCK_STREAM,0); if (sock_fd < 0) { my_err("socket", __LINE__); } // 设置该套接字使之可以重新绑定端口 optval = 1; if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) < 0) { my_err("setsockopt", __LINE__); } // 初始化服务器端地址结构 memset(&serv_addr, 0, sizeof (struct sockaddr_in)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 将套接字绑定到本地端口 if (bind(sock_fd, (struct sockaddr *)&serv_addr, sizeof (struct sockaddr_in)) < 0) { my_err("bind", __LINE__); } // 将套接字转化为监听套接字 if (listen(sock_fd, LISTENQ) < 0) { my_err("listen", __LINE__); } cli_len = sizeof (struct sockaddr_in); while (1) { // 通过accept接受客户端的连接请求,并返回连接套接字用于收发数据 conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len); if (conn_fd < 0) { my_err("accept", __LINE__); } printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr)); // 创建一个子进程处理刚刚接受的连接请求 if ( (pid = fork()) == 0 ) { // 子进程 while(1) { memset(recv_buf, 0, sizeof(recv_buf)); if ((ret = recv(conn_fd, recv_buf, sizeof (recv_buf), 0)) < 0) { perror("recv"); exit(1); } recv_buf[ret-1] = '\0'; // 将数据结束标志'\n'替换成字符串结束标志 if (flag_recv == USERNAME) { // 接收到的是用户名 name_num = find_name(recv_buf); switch (name_num) { case -1: send_data(conn_fd, "n\n"); break; case -2: exit(1); break; default: send_data(conn_fd, "y\n"); flag_recv = PASSWORD; break; } } else if (flag_recv == PASSWORD) { // 接收到的是密码 if (strncmp(users[name_num].password, recv_buf, strlen(users[name_num].password)) == 0) { send_data(conn_fd, "y\n"); send_data(conn_fd, "Welcome login my tcp server\n"); printf("%s login\n", users[name_num].username); break; // 跳出while循环 } else send_data(conn_fd, "n\n"); } } close(sock_fd); close(conn_fd); exit(0); // 结束子进程 } else { // 父进程关闭刚刚接受的连接请求,执行accept等待其他连接请求 close(conn_fd); } } return 0; }(2)客户端client.c:
#include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #define BUFSIZE 1024 #define INVALID_USERINFO 'n' // 用户信息无效 #define VALID_USERINFO 'y' // 用户信息有效 /*自定义的错误处理函数*/ void my_err(const char * err_string, int line) { fprintf(stderr, "line:%d ", line); perror(err_string); exit(1); } /* * 函数名: my_recv * 描 述 : 从套接字上读取一次数据(以'\n'为结束标志) * 参 数 : conn_fd -- 从该连接套接字上接收数据 * data_buf -- 读取到的数据保存在此缓冲中 * len -- data_buf所指向的空间长度 * 返回值: 出错返回-1, 服务器端已关闭连接则返回0, 成功返回读取的字节数 */ int my_recv(int conn_fd, char *data_buf, int len) { static char recv_buf[BUFSIZE]; // 自定义缓冲区,BUFSIZE定义在my_recv.h中 static char *pread; // 指向下一次读取数据的位置 static int len_remain = 0; // 自定义缓冲区中剩余字节数 int i; // 如果自定义缓冲区中没有数据,则从套接字读取数据 if (len_remain <= 0) { if ((len_remain =recv(conn_fd, recv_buf, sizeof (recv_buf), 0)) < 0) { my_err("recv", __LINE__); } else if (len_remain == 0) { // 目的计算机端的socket连接关闭 return 0; } pread = recv_buf; // 重新初始化pread指针 } // 从自定义缓冲区中读取一次数据 for (i=0; *pread != '\n'; i++) { if (i > len) { // 防止指针越界 return -1; } data_buf[i] = *pread++; len_remain--; } // 去除结束标志 len_remain--; pread++; return i; // 读取成功 } /*获取用户输入存入到buf,buf的长度为len,用户输入数据以'\n'为结束标志*/ int get_userinfo(char *buf, int len) { int i; int c; if (buf == NULL) { return -1; } i = 0; while ( ((c = getchar()) != '\n') && (c != EOF) && (i < len-2) ) { buf[i++] = c; } buf[i++] = '\n'; buf[i++] = '\0'; return 0; } // 输入用户名,然后通过fd发送出去 void input_userinfo(int conn_fd, const char *string) { char input_buf[32]; char recv_buf[BUFSIZE]; int flag_userinfo; int count = 0; // 输入用户信息直到正确为止 do { printf("%s:", string); if (get_userinfo(input_buf, 32) < 0) { printf("error return from get_userinfo\n"); exit(1); } if (send(conn_fd, input_buf, strlen(input_buf), 0) < 0) { my_err("send", __LINE__); } // 从连接套接字上读取一次数据 if (my_recv(conn_fd, recv_buf, sizeof (recv_buf)) < 0) { printf("data is too long\n"); exit(1); } if (recv_buf[0] == VALID_USERINFO) { flag_userinfo = VALID_USERINFO; } else { count++; if (count >= 3) { printf("input %s error for three times,exit!\n", string); exit(-1); } printf("%s error,input again!\n", string); flag_userinfo= INVALID_USERINFO; } } while(flag_userinfo == INVALID_USERINFO); } int main(int argc, char **argv) { int i; int ret; int conn_fd; int serv_port; struct sockaddr_in serv_addr; char recv_buf[BUFSIZE]; // 检查参数个数 if (argc != 5) { printf("Usage: [-p] [serv_port] [-a] [serv_address]\n"); exit(1); } // 初始化服务器端地址结构 memset(&serv_addr, 0, sizeof (struct sockaddr_in)); serv_addr.sin_family = AF_INET; // 从命令行获取服务器端的端口与地址 for (i=1; i<argc; i++) { if (strcmp("-p", argv[i]) == 0) { serv_port = atoi(argv[i+1]); if (serv_port < 0 || serv_port > 65535) { printf("invalid serv_addr.sin_port\n"); exit(1); } else { serv_addr.sin_port = htons(serv_port); } continue; } if (strcmp("-a", argv[i]) == 0) { if (inet_aton(argv[i+1], &serv_addr.sin_addr) == 0) { printf("invalid server ip address\n"); exit(1); } continue; } } // 检测是否少输入了某项参数 if (serv_addr.sin_port == 0 || serv_addr.sin_addr.s_addr == 0) { printf("Usage: [-p] [serv_addr.sin_port] [-a][serv_address]\n"); exit(1); } // 创建一个TCP套接字 conn_fd = socket(AF_INET, SOCK_STREAM,0); if (conn_fd < 0) { my_err("socket", __LINE__); } // 向服务器端发送连接请求 if (connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof (struct sockaddr)) < 0) { my_err("connect", __LINE__); } // 输入用户名和密码 input_userinfo(conn_fd, "username"); input_userinfo(conn_fd, "password"); // 读取欢迎信息并打印出来 if ((ret = my_recv(conn_fd, recv_buf, sizeof (recv_buf))) < 0) { printf("data is too long\n"); exit(1); } for (i=0; i<ret; i++) { printf("%c", recv_buf[i]); } printf("\n"); close(conn_fd); return 0; }(3)编译运行:
gcc -o server server.c ./server gcc -o client client.c ./client -p 4507 -a 127.0.0.1
#include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <strings.h> #define HELLO_WORLD_SERVER_PORT 6666 #define LENGTH_OF_LISTENQ_QUEUE 20 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 int main(int argc, char *argv[]) { struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htons(INADDR_ANY); server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); int server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket < 0) { printf("create socket failed!\n"); exit(1); } if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr))) { printf("bind socket failed!\n"); exit(1); } if (listen(server_socket, LENGTH_OF_LISTENQ_QUEUE)) { printf("listen socket failed!\n"); exit(1); } while (1) { struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); int new_server_scoket = accept(server_socket, (struct sockaddr*)&client_addr, &length); if (new_server_scoket < 0) { printf("accept socket failed!\n"); break; } char buffer[BUFFER_SIZE]; bzero(buffer, BUFFER_SIZE); length = recv(new_server_scoket, buffer, BUFFER_SIZE, 0); if (length < 0) { printf("recv data failed!\n"); break; } char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, FILE_NAME_MAX_SIZE + 1); strncpy(file_name, buffer, strlen(file_name) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer)); FILE *fp = fopen(file_name, "r"); if (fp == NULL) { printf("File: %s not found\n", file_name); } else { bzero(buffer, BUFFER_SIZE); int file_length = 0; while((file_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) { printf("file_length = %d\n", file_length); if (send(new_server_scoket, buffer, file_length, 0) < 0) { printf("send file: %s failed\n", file_name); break; } bzero(buffer, BUFFER_SIZE); } fclose(fp); printf("file: %s trandfer finished\n", file_name); } close(new_server_scoket); } close(server_socket); return 0; }(2)file_client.c:
#include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <strings.h> #define HELLO_WORLD_SERVER_PORT 6666 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 int main(int argc, char *argv[]) { if (argc != 2) { printf("use:[./run][server_ipaddress]\n"); exit(1); } struct sockaddr_in client_addr; bzero(&client_addr,sizeof(client_addr)); client_addr.sin_family = AF_INET; if (inet_aton(argv[1], &client_addr.sin_addr) == 0) { printf("server ip address error!\n"); exit(1); } client_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT); socklen_t client_addr_length = sizeof(client_addr); int client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0) { printf("create socket failed\n"); exit(1); } if (connect(client_socket, (struct sockaddr*)&client_addr, client_addr_length) < 0) { printf("can not connect to address\n"); exit(1); } char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, FILE_NAME_MAX_SIZE + 1); printf("please input file name:"); scanf("%s", file_name); char buffer[BUFFER_SIZE]; bzero(buffer, BUFFER_SIZE); strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name)); send(client_socket, buffer, BUFFER_SIZE, 0); FILE *fp = fopen(file_name, "w"); if (fp == NULL) { printf("file can not open\n"); exit(1); } bzero(buffer, BUFFER_SIZE); int length = 0; while (length = recv(client_socket, buffer, BUFFER_SIZE, 0)) { if (length < 0) { printf("recv failed\n"); break; } int write_length = fwrite(buffer, sizeof(char), length, fp); if (write_length < length) { printf("file write failed\n"); break; } bzero(buffer, BUFFER_SIZE); } printf("recieve file: %s from server %s finished!\n", file_name, argv[1]); close(fp); close(client_socket); return 0; }(3)编译运行
gcc -o server file_server.c ./server gcc -o client file_client.c ./client 127.0.0.1server显示:
(1)http服务器server.c:
#include <stdarg.h> #include <errno.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <errno.h> #include <netinet/in.h> #include <sys/socket.h> #include <resolv.h> #include <arpa/inet.h> #include <stdlib.h> #include <signal.h> #include <strings.h> #define DEFAULTIP "127.0.0.1" #define DEFAULTPORT "4040" #define DEFAULTBACK "10" #define DEFAULTDIR "/tmp/http/dir" #define DEFAULTLOG "/tmp/http/log" void prterrmsg(char *msg); #define prterrmsg(msg) { perror(msg); abort(); } void wrterrmsg(char *msg); #define wrterrmsg(msg) { fputs(msg, logfp); fputs(strerror(errno), logfp);fflush(logfp); abort(); } void prtinfomsg(char *msg); #define prtinfomsg(msg) { fputs(msg, stdout); } void wrtinfomsg(char *msg); #define wrtinfomsg(msg) { fputs(msg, logfp); fflush(logfp);} #define MAXBUF 1024 char buffer[MAXBUF + 1]; char *host = 0; char *port = 0; char *back = 0; char *dirroot = 0; char *logdir = 0; unsigned char daemon_y_n = 0; FILE *logfp; #define MAXPATH 150 /*---------------------------------------- *--- dir_up - 查找dirpath所指目录的上一级目录 *---------------------------------------- */ char *dir_up(char *dirpath) { static char Path[MAXPATH]; int len; strcpy(Path, dirpath); len = strlen(Path); if (len > 1 && Path[len - 1] == '/') len--; while (Path[len - 1] != '/' && len > 1) len--; Path[len] = 0; return Path; } /*------------------------------------------------------ *--- AllocateMemory - 分配空间并把d所指的内容复制 *------------------------------------------------------ */ void AllocateMemory(char **s, int l, char *d) { *s = (char *)malloc(l + 1); bzero(*s, l + 1); memcpy(*s, d, l); } /*------------------------------------------------------ *--- GiveResponse - 把Path所指的内容发送到client_sock去 *-------------------如果Path是一个目录,则列出目录内容 *-------------------如果Path是一个文件,则下载文件 *------------------------------------------------------ */ void GiveResponse(FILE * client_sock, char *Path) { struct dirent *dirent; struct stat info; char Filename[MAXPATH]; DIR *dir; int fd, len, ret; char *p, *realPath, *realFilename, *nport; /* 获得实际工作目录或文件 */ len = strlen(dirroot) + strlen(Path) + 1; realPath = (char *)malloc(len + 1); bzero(realPath, len + 1); sprintf(realPath, "%s/%s", dirroot, Path); /* 获得实际工作端口 */ len = strlen(port) + 1; nport = (char *)malloc(len + 1); bzero(nport, len + 1); sprintf(nport, ":%s", port); /* 获得实际工作目录或文件的信息以判断是文件还是目录 */ if (stat(realPath, &info)) { fprintf(client_sock, "HTTP/1.1 200 OK\r\nServer: DAS by Centos\r\nConnection: close\r\n\r\n" "<html>" "<meta charset=\"UTF-8\"/>" "<head><title>%d - %s</title></head>" "<body><font size=+4>Linux下目录访问服务器</font><br><hr width=\"100%%\"><br><center>" "<table border cols=3 width=\"100%%\">" , errno, strerror(errno)); fprintf(client_sock, "</table><font color=\"CC0000\" size=+2>请向管理员咨询为何出现如下错误提示:\n%s %s</font></body></html>", Path, strerror(errno)); goto out; } /* 处理浏览文件请求,即下载文件 */ if (S_ISREG(info.st_mode)) { fd = open(realPath, O_RDONLY); len = lseek(fd, 0, SEEK_END); p = (char *) malloc(len + 1); bzero(p, len + 1); lseek(fd, 0, SEEK_SET); ret = read(fd, p, len); close(fd); fprintf(client_sock, "HTTP/1.1 200 OK\r\nServer: DAS by Centos\r\nConnection: keep-alive\r\nContent-type: application/*\r\nContent-Length:%d\r\n\r\n", len); fwrite(p, len, 1, client_sock); free(p); } else if (S_ISDIR(info.st_mode)) { /* 处理浏览目录请求 */ dir = opendir(realPath); fprintf(client_sock, "HTTP/1.1 200 OK\r\nServer: DAS by Centos\r\nConnection: close\r\n\r\n" "<html>" "<meta charset=\"UTF-8\"/>" "<head><title>%s</title></head>" "<body><font size=+4>Linux下目录访问服务器</font><br><hr width=\"100%%\"><br><center>" "<table border cols=3 width=\"100%%\">", Path); fprintf(client_sock, "<caption><font size=+3>目录 %s</font></caption>\n", Path); fprintf(client_sock, "<tr><td>名称</td><td>大小</td><td>修改时间</td></tr>\n"); if (dir == 0) { fprintf(client_sock, "</table><font color=\"CC0000\" size=+2>%s</font></body></html>", strerror(errno)); return; } /* 读取目录里的所有内容 */ while ((dirent = readdir(dir)) != 0) { if (strcmp(Path, "/") == 0) sprintf(Filename, "/%s", dirent->d_name); else sprintf(Filename, "%s/%s", Path, dirent->d_name); fprintf(client_sock, "<tr>"); len = strlen(dirroot) + strlen(Filename) + 1; realFilename = (char *)malloc(len + 1); bzero(realFilename, len + 1); sprintf(realFilename, "%s/%s", dirroot, Filename); if (stat(realFilename, &info) == 0) { if (strcmp(dirent->d_name, "..") == 0) fprintf(client_sock, "<td><a href=\"http://%s%s%s\">(parent)</a></td>", host, atoi(port) == 80 ? "" : nport, dir_up(Path)); else fprintf(client_sock, "<td><a href=\"http://%s%s%s\">%s</a></td>", host, atoi(port) == 80 ? "" : nport, Filename, dirent->d_name); if (S_ISDIR(info.st_mode)) fprintf(client_sock, "<td>目录</td>"); else if (S_ISREG(info.st_mode)) fprintf(client_sock, "<td>%d</td>", info.st_size); else if (S_ISLNK(info.st_mode)) fprintf(client_sock, "<td>链接</td>"); else if (S_ISCHR(info.st_mode)) fprintf(client_sock, "<td>字符设备</td>"); else if (S_ISBLK(info.st_mode)) fprintf(client_sock, "<td>块设备</td>"); else if (S_ISFIFO(info.st_mode)) fprintf(client_sock, "<td>FIFO</td>"); else if (S_ISSOCK(info.st_mode)) fprintf(client_sock, "<td>Socket</td>"); else fprintf(client_sock, "<td>(未知)</td>"); fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime)); } fprintf(client_sock, "</tr>\n"); free(realFilename); } fprintf(client_sock, "</table></center></body></html>"); } else { /* 既非常规文件又非目录,禁止访问 */ fprintf(client_sock, "HTTP/1.1 200 OK\r\nServer: DAS by Centos\r\nConnection: close\r\n\r\n" "<html>" "<meta charset=\"UTF-8\"/>" "<head><title>permission denied</title></head>" "<body><font size=+4>Linux下目录访问服务器</font><br><hr width=\"100%%\"><br><center>" "<table border cols=3 width=\"100%%\">"); fprintf(client_sock, "</table><font color=\"CC0000\" size=+2>你访问的资源'%s'被禁止访问,请联系管理员解决!</font></body></html>", Path); } out: free(realPath); free(nport); } /*------------------------------------------------------ *--- getoption - 分析取出程序的参数 *------------------------------------------------------ */ void getoption(int argc, char **argv) { // int c, len; // char *p = 0; // // opterr = 0; // while (1) { // int option_index = 0; // static struct option long_options[] = { // {"host", 1, 0, 0}, // {"port", 1, 0, 0}, // {"back", 1, 0, 0}, // {"dir", 1, 0, 0}, // {"log", 1, 0, 0}, // {"daemon", 0, 0, 0}, // {0, 0, 0, 0} // }; // /* 本程序支持如一些参数: // * --host IP地址 或者 -H IP地址 // * --port 端口 或者 -P 端口 // * --back 监听数量 或者 -B 监听数量 // * --dir 网站根目录 或者 -D 网站根目录 // * --log 日志存放路径 或者 -L 日志存放路径 // * --daemon 使程序进入后台运行模式 // */ // c = getopt_long(argc, argv, "H:P:B:D:L", // long_options, &option_index); // if (c == -1 || c == '?') // break; // // if(optarg) len = strlen(optarg); // else len = 0; // // if ((!c && !(strcasecmp(long_options[option_index].name, "host"))) // || c == 'H') // p = host = malloc(len + 1); // else if ((!c // && // !(strcasecmp(long_options[option_index].name, "port"))) // || c == 'P') // p = port = malloc(len + 1); // else if ((!c // && // !(strcasecmp(long_options[option_index].name, "back"))) // || c == 'B') // p = back = malloc(len + 1); // else if ((!c // && !(strcasecmp(long_options[option_index].name, "dir"))) // || c == 'D') // p = dirroot = malloc(len + 1); // else if ((!c // && !(strcasecmp(long_options[option_index].name, "log"))) // || c == 'L') // p = logdir = malloc(len + 1); // else if ((!c // && // !(strcasecmp // (long_options[option_index].name, "daemon")))) { // daemon_y_n = 1; // continue; // } // else // break; // bzero(p, len + 1); // memcpy(p, optarg, len); // } } int main(int argc, char **argv) { struct sockaddr_in addr; int sock_fd, addrlen; /* 获得程序工作的参数,如 IP 、端口、监听数、网页根目录、目录存放位置等 */ //getoption(argc, argv); if (!host) { addrlen = strlen(DEFAULTIP); AllocateMemory(&host, addrlen, DEFAULTIP); } if (!port) { addrlen = strlen(DEFAULTPORT); AllocateMemory(&port, addrlen, DEFAULTPORT); } if (!back) { addrlen = strlen(DEFAULTBACK); AllocateMemory(&back, addrlen, DEFAULTBACK); } if (!dirroot) { addrlen = strlen(DEFAULTDIR); AllocateMemory(&dirroot, addrlen, DEFAULTDIR); } if (!logdir) { addrlen = strlen(DEFAULTLOG); AllocateMemory(&logdir, addrlen, DEFAULTLOG); } printf ("host=%s port=%s back=%s dirroot=%s logdir=%s %s是后台工作模式(进程ID:%d)\n", host, port, back, dirroot, logdir, daemon_y_n?"":"不", getpid()); /* fork() 两次处于后台工作模式下 */ if (daemon_y_n) { if (fork()) exit(0); if (fork()) exit(0); close(0), close(1), close(2); logfp = fopen(logdir, "a+"); if (!logfp) exit(0); } /* 处理子进程退出以免产生僵尸进程 */ signal(SIGCHLD, SIG_IGN); /* 创建 socket */ if ((sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { if (!daemon_y_n) { prterrmsg("socket()"); } else { wrterrmsg("socket()"); } } /* 设置端口快速重用 */ addrlen = 1; setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen, sizeof(addrlen)); addr.sin_family = AF_INET; addr.sin_port = htons(atoi(port)); addr.sin_addr.s_addr = htonl(INADDR_ANY);//htonl(INADDR_ANY); addrlen = sizeof(struct sockaddr_in); /* 绑定地址、端口等信息 */ if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) { if (!daemon_y_n) { prterrmsg("bind()"); } else { wrterrmsg("bind()"); } } /* 开启临听 */ if (listen(sock_fd, atoi(back)) < 0) { if (!daemon_y_n) { prterrmsg("listen()"); } else { wrterrmsg("listen()"); } } while (1) { int len; int new_fd; addrlen = sizeof(struct sockaddr_in); /* 接受新连接请求 */ new_fd = accept(sock_fd, (struct sockaddr *) &addr, (socklen_t *)&addrlen); if (new_fd < 0) { if (!daemon_y_n) { prterrmsg("accept()"); } else { wrterrmsg("accept()"); } break; } bzero(buffer, MAXBUF + 1); sprintf(buffer, "连接来自于: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); if (!daemon_y_n) { prtinfomsg(buffer); } else { wrtinfomsg(buffer); } /* 产生一个子进程去处理请求,当前进程继续等待新的连接到来 */ if (!fork()) { bzero(buffer, MAXBUF + 1); puts("recving..."); if ((len = recv(new_fd, buffer, MAXBUF, 0)) > 0) { FILE *ClientFP = fdopen(new_fd, "w"); if (ClientFP == NULL) { if (!daemon_y_n) { prterrmsg("fdopen()"); } else { prterrmsg("fdopen()"); } } else { char Req[MAXPATH + 1] = ""; sscanf(buffer, "GET %s HTTP", Req); bzero(buffer, MAXBUF + 1); sprintf(buffer, "请求取文件: \"%s\"\n", Req); if (!daemon_y_n) { prtinfomsg(buffer); } else { wrtinfomsg(buffer); } /* 处理用户请求 */ GiveResponse(ClientFP, Req); fclose(ClientFP); } } puts("go out"); exit(0); } close(new_fd); } close(sock_fd); return 0; }(2)编译运行(浏览器为客户端):
gcc -o server server.c ./server服务器运行:
#include <stdlib.h> #include <stdio.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <strings.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include<errno.h> int main(int argc, char *argv[]) { char buffer[1024] = {0}; char host_addr[256] = {0}; char host_file[256] = {0}; char local_file[256] = {0}; int sockfd; int send, totalsend; int nbytes; char request[1024] = {0}; struct sockaddr_in server_addr; struct hostent *host; if (argc != 2) { fprintf(stderr, "Usage:%s web-address!\n", argv[0]); exit(1); } int portnumber = 4040; strcpy(host_addr, argv[1]); strcpy(host_file, "server.c"); if ((host = gethostbyname(argv[1])) == NULL) { fprintf(stderr,"Gethostname error\n", strerror(errno)); exit(1); } char ip_str[32] = {0}; printf("address: %s\n", inet_ntop(host->h_addrtype, host->h_addr, ip_str, sizeof(ip_str))); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr,"Socket Error:%s!\n",strerror(errno)); exit(1); } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(portnumber); server_addr.sin_addr = *((struct in_addr *)host->h_addr); if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1) { fprintf(stderr,"Connect Error:%s!\n",strerror(errno)); exit(1); } sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n \ User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n \ Host: %s:%d\r\nConnection: Close\r\n\r\n", host_file, host_addr, portnumber); strcpy(local_file, "local.txt"); send = 0; totalsend = 0; nbytes = strlen(request); while (totalsend < nbytes) { send = write(sockfd, request + totalsend, nbytes - totalsend); if (send == -1) { printf("send error:%s!\n", strerror(errno)); exit(0); } totalsend += send; printf("%d bytes send OK!\n", totalsend); } FILE * fp = fopen(local_file, "a"); if(!fp) { printf("create file error:%s!\n", strerror(errno)); return 0; } //printf("The following is the response header:\n"); int i = 0; /* 连接成功了,接收http响应,response */ while((nbytes = read(sockfd, buffer, 1)) == 1) { if (i < 4) { if (buffer[0] == '\r' || buffer[0] == '\n') i++; else i = 0; //printf("%c", buffer[0]); } else { fwrite(buffer, 1, 1, fp);/*将http主体信息写入文件*/ //printf("%c", buffer[0]); i++; if(i % 1024 == 0) fflush(fp); /**每1K时存盘一次**/ } } printf("\n"); fclose(fp); /* 结束通讯 */ close(sockfd); return 0; }