This commit is contained in:
2026-05-28 08:48:01 +08:00
parent 1132d9e9a7
commit 43902c20e4
93 changed files with 84995 additions and 26 deletions

BIN
badcount Executable file

Binary file not shown.

Binary file not shown.

View File

@@ -4,24 +4,22 @@
#include <stdlib.h>
#include <unistd.h>
void *increase(void *vargp);
void *decrease(void *vargp);
int cnt = 0;
int main(int argc, char **argv)
int main()
{
unsigned int niters;
pthread_t t1, t2;
if (argc != 2)
{
printf("Usage: %s <niters>\n", argv[0]);
exit(2);
}
niters = atoll(argv[1]);
niters = 10;
for (int i = 0; i < 10; i++)
{
printf("niters=%d\n", niters);
for (int j = 0; j < 20; j++)
{
pthread_create(&t1, NULL, increase, (void *)niters);
pthread_create(&t2, NULL, decrease, (void *)niters);
pthread_join(t1, NULL);
@@ -30,11 +28,15 @@ int main(int argc, char **argv)
if (cnt != 0)
{
printf("Error! cnt=%d\n", cnt);
exit(1);
}
else
{
printf("Correct! cnt=%d\n", cnt);
}
}
niters *= 10;
}
exit(0);
}

Binary file not shown.

BIN
server-exp/cgi-bin/add Executable file

Binary file not shown.

27
server-exp/cgi-bin/add.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 8192
int main(void) {
char *buf, *p;
char content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments from standard input */
scanf("%d&%d", &n1, &n2);
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int) strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}

242
server-exp/common.c Normal file
View File

@@ -0,0 +1,242 @@
/* $begin common.c */
#include "common.h"
/*********************************************************************
* The Rio package - robust I/O functions
**********************************************************************/
/*
* rio_readn - robustly read n bytes (unbuffered)
*/
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readn */
/*
* rio_writen - robustly write n bytes (unbuffered)
*/
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nwritten = 0; /* and call write() again */
else
return -1; /* errorno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
/* $end rio_writen */
/*
* rio_read - This is a wrapper for the Unix read() function that
* transfers min(n, rio_cnt) bytes from an internal buffer to a user
* buffer, where n is the number of bytes requested by the user and
* rio_cnt is the number of unread bytes in the internal buffer. On
* entry, rio_read() refills the internal buffer via a call to
* read() if the internal buffer is empty.
*/
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* refill if buf is empty */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* interrupted by sig handler return */
return -1;
}
else if (rp->rio_cnt == 0) /* EOF */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
/* $end rio_read */
/*
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer
*/
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */
/*
* rio_readnb - Robustly read n bytes (buffered)
*/
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readnb */
/*
* rio_readlineb - robustly read a text line (buffered)
*/
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* error */
}
*bufp = 0;
return n;
}
/* $end rio_readlineb */
/**********************************
* Wrappers for robust I/O routines
**********************************/
/********************************
* Client/server helper functions
********************************/
/*
* open_client_sock - open connection to server at <hostname, port>
* and return a socket descriptor ready for reading and writing.
* Returns -1 and sets errno on Unix error.
* Returns -2 and sets h_errno on DNS (gethostbyname) error.
*/
/* $begin open_client_sock */
int open_client_sock(char *hostname, int port)
{
int client_sock;
struct hostent *hp;
struct sockaddr_in serveraddr;
if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1; /* check errno for cause of error */
/* Fill in the server's IP address and port */
if ((hp = gethostbyname(hostname)) == NULL)
return -2; /* check h_errno for cause of error */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)hp->h_addr_list[0],
(char *)&serveraddr.sin_addr.s_addr, hp->h_length);
serveraddr.sin_port = htons(port);
/* Establish a connection with the server */
if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
return client_sock;
}
/* $end open_client_sock */
/*
* open_listen_sock - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
/* $begin open_listen_sock */
int open_listen_sock(int port)
{
int listen_sock, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listen_sock will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listen_sock, LISTENQ) < 0)
return -1;
return listen_sock;
}
/* $end open_listen_sock */
/* $end wrapper.c */

76
server-exp/common.h Normal file
View File

@@ -0,0 +1,76 @@
/* $begin common.h */
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* defined by BIND for DNS errors */
extern char **environ; /* defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Client/server helper functions */
int open_client_sock(char *hostname, int portno);
int open_listen_sock(int portno);
#endif /* __COMMON_H__ */
/* $end common.h */

BIN
server-exp/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
server-exp/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
server-exp/ftpc Executable file

Binary file not shown.

109
server-exp/ftpc.c Normal file
View File

@@ -0,0 +1,109 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
ftp_file_t file;
FILE *fp;
long fileSize;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
char *token = strtok(buf, " \t\n"); // 提取FTP命令
if (strcmp(token, "get") == 0 || strcmp(token, "put") == 0) {
strncpy(file.cmd, token, MAXLINE - 1);
file.cmd[MAXLINE - 1] = '\0';
} else {
printf("Unsupported command: %s\n", token);
continue; // 如果不是get或put命令跳过处理
}
token = strtok(NULL, " \t\n"); // 提取文件名参数
if (token != NULL) {
strncpy(file.filename, token, MAXLINE - 1);
file.filename[MAXLINE - 1] = '\0';
} else {
printf("Filename is required for command: %s\n", file.cmd);
continue; // 如果没有提供文件名参数,跳过处理
}
if (strcmp(file.cmd,"put")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file");
continue;
} else {
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
}
send(client_sock, &file, sizeof(ftp_file_t), 0);
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
} else {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
send(client_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
fclose(fp);
}
}
else if (strcmp(file.cmd,"get")==0)
{
send(client_sock, &file, sizeof(ftp_file_t), 0);
recv(client_sock, &file, sizeof(ftp_file_t), 0); // 接收文件信息
int fileSize = atoi(file.fileSize);
if (fileSize > 0) {
char *fileContent = malloc(fileSize); // 根据文件大小分配内存
recv(client_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp != NULL) {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
} else {
perror("Error creating file");
}
free(fileContent); // 释放内存
}
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
}
}
close(client_sock);
exit(0);
}

View File

BIN
server-exp/ftps Executable file

Binary file not shown.

101
server-exp/ftps.c Normal file
View File

@@ -0,0 +1,101 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXBUF];
ftp_file_t file;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, &file, sizeof(ftp_file_t),0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
if (strcmp(file.cmd,"put")==0)
{
int fileSize = atoi(file.fileSize);
char *fileContent = malloc(fileSize);
recv(conn_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp == NULL) {
perror("Error opening file for writing");
} else {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
}
free(fileContent);
strcpy(buf, "File received successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
else if (strcmp(file.cmd,"get")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
FILE *fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
strcpy(file.fileSize, "0");
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送错误信息
} else {
fseek(fp, 0, SEEK_END);
long fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送文件信息
fp = fopen(filePath, "r");
if (fp != NULL) {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
fclose(fp);
send(conn_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
strcpy(buf, "File sent successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
}
}
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

View File

Binary file not shown.

13
server-exp/index.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<title>the example web</title>
</head>
<body>
<H1>webserver test page</H1>
<p>
Not pretty but should prove that webserver works:-)
<p>
<IMG SRC="example.jpg">
</body>
</html>

214
server-exp/perf.txt Normal file
View File

@@ -0,0 +1,214 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
28.57 0.10 0.10 1461740 68.41 116.30 rio_readlineb
25.71 0.19 0.09 365435 246.28 930.40 process_trans
20.00 0.26 0.07 29966110 2.34 2.34 rio_read
5.71 0.28 0.02 974534 20.52 20.52 rio_writen
5.71 0.30 0.02 243603 82.10 123.15 feed_static
5.71 0.32 0.02 121832 164.16 246.25 error_request
2.86 0.33 0.01 365435 27.36 376.26 read_requesthdrs
2.86 0.34 0.01 121832 82.08 82.08 parse_dynamic_uri
2.86 0.35 0.01 main
0.00 0.35 0.00 365435 0.00 0.00 is_static
0.00 0.35 0.00 365435 0.00 0.00 rio_readinitb
0.00 0.35 0.00 243603 0.00 0.00 get_filetype
0.00 0.35 0.00 243603 0.00 0.00 parse_static_uri
0.00 0.35 0.00 1 0.00 0.00 open_listen_sock
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 2.86% of 0.35 seconds
index % time self children called name
<spontaneous>
[1] 100.0 0.01 0.34 main [1]
0.09 0.25 365435/365435 process_trans [2]
0.00 0.00 1/1 open_listen_sock [14]
-----------------------------------------------
0.09 0.25 365435/365435 main [1]
[2] 97.1 0.09 0.25 365435 process_trans [2]
0.01 0.13 365435/365435 read_requesthdrs [4]
0.03 0.02 365435/1461740 rio_readlineb [3]
0.02 0.01 121832/121832 error_request [6]
0.02 0.01 243603/243603 feed_static [7]
0.01 0.00 121832/121832 parse_dynamic_uri [9]
0.00 0.00 365435/365435 rio_readinitb [11]
0.00 0.00 365435/365435 is_static [10]
0.00 0.00 243603/243603 parse_static_uri [13]
-----------------------------------------------
0.03 0.02 365435/1461740 process_trans [2]
0.07 0.05 1096305/1461740 read_requesthdrs [4]
[3] 48.6 0.10 0.07 1461740 rio_readlineb [3]
0.07 0.00 29966110/29966110 rio_read [5]
-----------------------------------------------
0.01 0.13 365435/365435 process_trans [2]
[4] 39.3 0.01 0.13 365435 read_requesthdrs [4]
0.07 0.05 1096305/1461740 rio_readlineb [3]
-----------------------------------------------
0.07 0.00 29966110/29966110 rio_readlineb [3]
[5] 20.0 0.07 0.00 29966110 rio_read [5]
-----------------------------------------------
0.02 0.01 121832/121832 process_trans [2]
[6] 8.6 0.02 0.01 121832 error_request [6]
0.01 0.00 487328/974534 rio_writen [8]
-----------------------------------------------
0.02 0.01 243603/243603 process_trans [2]
[7] 8.6 0.02 0.01 243603 feed_static [7]
0.01 0.00 487206/974534 rio_writen [8]
0.00 0.00 243603/243603 get_filetype [12]
-----------------------------------------------
0.01 0.00 487206/974534 feed_static [7]
0.01 0.00 487328/974534 error_request [6]
[8] 5.7 0.02 0.00 974534 rio_writen [8]
-----------------------------------------------
0.01 0.00 121832/121832 process_trans [2]
[9] 2.9 0.01 0.00 121832 parse_dynamic_uri [9]
-----------------------------------------------
0.00 0.00 365435/365435 process_trans [2]
[10] 0.0 0.00 0.00 365435 is_static [10]
-----------------------------------------------
0.00 0.00 365435/365435 process_trans [2]
[11] 0.0 0.00 0.00 365435 rio_readinitb [11]
-----------------------------------------------
0.00 0.00 243603/243603 feed_static [7]
[12] 0.0 0.00 0.00 243603 get_filetype [12]
-----------------------------------------------
0.00 0.00 243603/243603 process_trans [2]
[13] 0.0 0.00 0.00 243603 parse_static_uri [13]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[14] 0.0 0.00 0.00 1 open_listen_sock [14]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function is in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Index by function name
[6] error_request [14] open_listen_sock [5] rio_read (common.c)
[7] feed_static [9] parse_dynamic_uri [11] rio_readinitb
[12] get_filetype [13] parse_static_uri [3] rio_readlineb
[10] is_static [2] process_trans [8] rio_writen
[1] main [4] read_requesthdrs

8
server-exp/test.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>a simple page for testing tiny</title>
<head>
<body>
Hello World
</body>
</html>

1
server-exp/testfile.txt Normal file
View File

@@ -0,0 +1 @@
hello ftp test

BIN
server-exp/togglec Executable file

Binary file not shown.

29
server-exp/togglec.c Normal file
View File

@@ -0,0 +1,29 @@
#include "common.h"
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
send(client_sock, buf, strlen(buf),0);
recv(client_sock, buf, MAXLINE,0);
fputs(buf, stdout);
}
close(client_sock);
exit(0);
}

BIN
server-exp/togglesi Executable file

Binary file not shown.

57
server-exp/togglesi.c Normal file
View File

@@ -0,0 +1,57 @@
#include "common.h"
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

3
server-exp/urls Normal file
View File

@@ -0,0 +1,3 @@
http://127.0.0.1:8088/index.html
http://127.0.0.1:8088/test.html
http://127.0.0.1:8088/cgi-bin/add?2025&523808

BIN
server-exp/webclient Executable file

Binary file not shown.

55
server-exp/webclient.c Normal file
View File

@@ -0,0 +1,55 @@
#include <arpa/inet.h> // 提供IP地址转换函数
#include <netinet/in.h> // 提供套接字地址结构定义
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库函数如exit()
#include <string.h> // 字符串操作函数
#include <sys/socket.h> // 套接字相关函数
#include <sys/types.h> // 数据类型定义
#include <unistd.h> // POSIX API如read()和write()
//#define PORT 8181 /* 目标服务器的端口号 */
//#define IP_ADDRESS "192.168.0.8" /* 目标服务器的IP地址 */
#define BUFSIZE 8196 /* 缓冲区大小 */
char *command = "GET /index.html HTTP/1.0 \r\n\r\n"; /* HTTP GET 请求命令 */
// 错误处理函数,打印错误信息并退出程序
void pexit(char *msg) {
perror(msg); // 打印错误信息
exit(1); // 退出程序
}
int main(int argc, char *argv[]) {//客户端启动命令为"./webclient 127.0.0.1 8088",argv[1]是IP地址argv[2]是端口号8088
int i, sockfd; // sockfd是套接字文件描述符
char buffer[BUFSIZE]; // 用于存储从服务器接收的数据
struct sockaddr_in serv_addr; // 定义服务器地址结构
// 打印尝试连接服务器的信息
printf("客户端尝试连接到 %s 和端口 %s\n", argv[1], argv[2]);
// 创建套接字使用IPv4和TCP协议
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) pexit("socket() 创建失败");
// 配置服务器地址
serv_addr.sin_family = AF_INET; // 地址族为IPv4
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址,如"127.0.0.1"
serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号,如"8088"
// 尝试连接到服务器
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
pexit("connect() 连接失败");
// 发送HTTP GET请求到服务器
printf("发送字节数=%ld %s\n", strlen(command), command);
if (write(sockfd, command, strlen(command)) < 0) pexit("write() 发送请求失败");
// 循环读取服务器返回的数据,并输出到标准输出
while ((i = read(sockfd, buffer, BUFSIZE)) > 0) {
if (write(1, buffer, i) < 0) // 1表示标准输出
pexit("write() 输出到标准输出失败");
}
// 关闭套接字,释放资源
close(sockfd);
return 0; // 程序正常退出
}

BIN
server-exp/weblet Executable file

Binary file not shown.

278
server-exp/weblet.c Normal file
View File

@@ -0,0 +1,278 @@
#include "common.h"
void process_trans(int fd,int hit);
void read_requesthdrs(rio_t *rp,int hit,int nrh);
int is_static(char *uri);
void parse_static_uri(char *uri, char *filename);
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs);
void feed_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void feed_dynamic(int fd, char *filename, char *cgiargs);
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description);
/* 支持的文件扩展名及其对应的MIME类型 */
struct {
char *ext; // 文件扩展名
char *filetype; // MIME类型
} extensions[] = {
{".gif", "image/gif"},
{".jpg", "image/jpg"},
{".jpeg", "image/jpeg"},
{".png", "image/png"},
{".ico", "image/ico"},
{".zip", "image/zip"},
{".gz", "image/gz"},
{".tar", "image/tar"},
{".htm", "text/html"},
{".html", "text/html"},
{0, 0} // 结束标志
};
/* 调试模式宏定义 */
#ifdef DEBUG
#define printf2(format, var) printf(format,var)
#define printf3(format,var1,var2) printf(format,var1,var2)
#else
#define printf2(format, var)
#define printf3(format,var1,var2)
#endif
/* 全局变量说明 */
/* Ctrl+C信号处理函数 */
void ctrlc_handler(int sig)
{
printf("您按下了Ctrl+C终止了Web服务器\n");
exit(0);
}
/* 主函数:监听端口并处理客户端连接 */
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port;
int hit; // 请求计数器
socklen_t clientlen;
struct sockaddr_in clientaddr;
/* 检查命令行参数 */
if (argc != 2) {
fprintf(stderr, "用法: %s <端口号>\n", argv[0]);
exit(1);
}
/* 设置Ctrl+C信号处理 */
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
(void)printf("错误: 无法设置SIGINT信号处理\n");
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
for (hit=1; ; hit++) { //hit为第几次请求或第几次http事务
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
process_trans(conn_sock,hit); // 处理HTTP事务
close(conn_sock);
}
exit(0);
}
/* 处理HTTP事务的核心函数 */
void process_trans(int fd,int hit)
{
int static_flag; // 是否为静态资源标志
struct stat sbuf; // 文件状态信息
char buf[MAXLINE], method[MAXLINE]="", uri[MAXLINE]="", version[MAXLINE]="http/1.1";
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
int rhn=0; // 请求头计数器
printf2("第%d次请求开始\n",hit);
/* 读取请求行和请求头 */
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);
printf3("请求头%d:%s", ++rhn, buf);
//判断第一个请求行是否正好有三个单词构成如GET / http/1.1
/*int wn=0; char *p=buf;
while(*p==' ' || *p=='\t') p++;
while(*p) {
if((*p==' '||*p=='\t') && *(p+1)!=' ') wn++;
p++;
}
if (wn==2) */
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) { // 仅支持GET方法
error_request(fd, method, "501", "未实现",
"本服务器不支持该请求方法");
return;
}
read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头
static_flag = is_static(uri); // 判断资源类型
if(static_flag)
parse_static_uri(uri, filename); // 解析静态资源路径
else
parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数
/* 检查文件是否存在及权限 */
if (stat(filename, &sbuf) < 0) {
error_request(fd, filename, "404", "未找到",
"服务器无法找到该文件");
return;
}
/* 根据资源类型发送响应 */
if (static_flag) { // 静态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无权读取该文件");
return;
}
feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容
}
else { // 动态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无法执行该CGI程序");
return;
}
feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出
}
}
/* 判断URI是否对应静态资源 */
int is_static(char *uri)
{
return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源
}
/* 构造HTTP错误响应 */
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description)
{
char buf[MAXLINE], body[MAXBUF];
/* 构建错误页面内容 */
sprintf(body, "<html><title>错误请求</title>");
sprintf(body, "%s<body bgcolor=\"#ffffff\">\r\n", body);
sprintf(body, "%s<h1>%s %s</h1>\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s</p>\r\n", body, description, cause);
sprintf(body, "%s<hr><em>weblet Web服务器</em>\r\n", body);
/* 发送HTTP响应头 */
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
rio_writen(fd, buf, strlen(buf));
/* 发送错误页面内容 */
rio_writen(fd, body, strlen(body));
}
/* 读取请求头直到空行 */
void read_requesthdrs(rio_t *rp,int hit, int rhn)
{
char buf[MAXLINE];
rio_readlineb(rp, buf, MAXLINE);
while(strcmp(buf, "\r\n")) {
printf3("请求头%d:%s", ++rhn, buf);
rio_readlineb(rp, buf, MAXLINE);
}
printf2("第%d次请求结束\n\n",hit);
}
/* 解析静态资源URI到文件路径 */
void parse_static_uri(char *uri, char *filename)
{
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "index.html"); // 默认返回index.html
}
/* 解析动态资源URI到文件路径和参数 */
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs)
{
char *ptr = index(uri, '?');
if (ptr) {
strcpy(cgiargs, ptr+1); // 提取参数部分
*ptr = '\0'; // 截断URI
} else {
strcpy(cgiargs, "");
}
strcpy(filename, ".");
strcat(filename, uri);
}
/* 发送静态文件内容 */
void feed_static(int fd, char *filename, int filesize)
{
int srcfd;
char *srcp, filetype[MAXLINE], buf[MAXBUF];
/* 发送HTTP响应头 */
get_filetype(filename, filetype);
sprintf(buf, "HTTP/1.0 200 OK\r\n");
sprintf(buf, "%sServer: weblet Web Server\r\n", buf);
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
rio_writen(fd, buf, strlen(buf));
/* 发送文件内容 */
srcfd = open(filename, O_RDONLY, 0);
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
close(srcfd);
rio_writen(fd, srcp, filesize);
munmap(srcp, filesize);
}
/* 根据文件扩展名获取MIME类型 */
void get_filetype(char *filename, char *filetype)
{
int i, len;
strcpy(filetype, "text/html"); // 默认类型
for (i = 0; extensions[i].ext != 0; i++) {
len = strlen(extensions[i].ext);
if (!strcmp(&filename[strlen(filename)-len], extensions[i].ext)) {
strcpy(filetype, extensions[i].filetype);
break;
}
}
}
/* 启动子进程执行CGI程序 */
void feed_dynamic(int fd, char *filename, char *cgiargs)
{
char buf[MAXLINE], *emptylist[] = { NULL };
int pfd[2];
/* 构造HTTP响应头 */
sprintf(buf, "HTTP/1.0 200 OK\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Server: weblet Web Server\r\n");
rio_writen(fd, buf, strlen(buf));
/* 创建管道并启动子进程 */
(void)pipe(pfd);
if (fork() == 0) { // 子进程
close(pfd[1]); // 关闭写端
dup2(pfd[0], STDIN_FILENO); // 重定向标准输入
dup2(fd, STDOUT_FILENO); // 重定向标准输出到客户端
execve(filename, emptylist, environ); // 执行CGI程序
}
close(pfd[0]); // 父进程关闭读端
(void)write(pfd[1], cgiargs, strlen(cgiargs)+1); // 传递参数
wait(NULL); // 等待子进程结束
close(pfd[1]); // 关闭写端
}

View File

@@ -0,0 +1,4 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://lms.dgut.edu.cn/
HostUrl=https://uobs.dgut.edu.cn/view/resources/web/17452239027185951.docx?attname=%E5%AE%9E%E9%AA%8C1%E4%BB%BB%E5%8A%A1%E6%8A%A5%E5%91%8A%EF%BC%88web%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%88%9D%E6%AD%A5%E5%AE%9E%E7%8E%B0%EF%BC%89(1).docx

View File

@@ -0,0 +1,14 @@
common.h : 公共头文件、函数声明、常量定义等由教材wrapper.h简化而来
common.c : RIO函数库和打开网络连接函数源代码教材中libwrapper.c简化而来
webclient.c: web客户端源代码(《操作系统实验教程》第1章的client.c)
webserver.c: web服务器源代码(即教材《Linux编程》第8章的weblet.c)
togglec.c : toggle客户端源代码(教材第8章源代码
togglesi.c : 迭代式toggle服务器源代码教材《Linux编程》第8章源代码
./cgi-bin/add.c: 生成动态网页的cgi程序教材《Linux编程》第8章
Makefile: 目标和源代码间相互依赖关系文件参考教材《Linux编程》第3章
index.html: 缺省网页文件
example.jpg: 网页文件index.html中内嵌的图片
favicon.ico: index.html中内嵌的图标
test.html: 第2个测试用网页
urls: 记录运行http_load执行时访问哪些网址
gmon.out: grof命令生成的性能数据文件

81151
server-exp2/abc Normal file

File diff suppressed because it is too large Load Diff

BIN
server-exp2/cgi-bin/add Normal file

Binary file not shown.

27
server-exp2/cgi-bin/add.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 8192
int main(void) {
char *buf, *p;
char content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments from standard input */
scanf("%d&%d", &n1, &n2);
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int) strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}

243
server-exp2/common.c Normal file
View File

@@ -0,0 +1,243 @@
/* $begin common.c */
#include "common.h"
/*********************************************************************
* The Rio package - robust I/O functions
**********************************************************************/
/*
* rio_readn - robustly read n bytes (unbuffered)
*/
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readn */
/*
* rio_writen - robustly write n bytes (unbuffered)
*/
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nwritten = 0; /* and call write() again */
else
return -1; /* errorno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
/* $end rio_writen */
/*
* rio_read - This is a wrapper for the Unix read() function that
* transfers min(n, rio_cnt) bytes from an internal buffer to a user
* buffer, where n is the number of bytes requested by the user and
* rio_cnt is the number of unread bytes in the internal buffer. On
* entry, rio_read() refills the internal buffer via a call to
* read() if the internal buffer is empty.
*/
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* refill if buf is empty */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* interrupted by sig handler return */
return -1;
}
else if (rp->rio_cnt == 0) /* EOF */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
/* $end rio_read */
/*
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer
*/
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */
/*
* rio_readnb - Robustly read n bytes (buffered)
*/
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readnb */
/*
* rio_readlineb - robustly read a text line (buffered)
*/
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* error */
}
*bufp = 0;
return n;
}
/* $end rio_readlineb */
/**********************************
* Wrappers for robust I/O routines
**********************************/
/********************************
* Client/server helper functions
********************************/
/*
* open_client_sock - open connection to server at <hostname, port>
* and return a socket descriptor ready for reading and writing.
* Returns -1 and sets errno on Unix error.
* Returns -2 and sets h_errno on DNS (gethostbyname) error.
*/
/* $begin open_client_sock */
int open_client_sock(char *hostname, int port)
{
int client_sock;
struct hostent *hp;
struct sockaddr_in serveraddr;
if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1; /* check errno for cause of error */
/* Fill in the server's IP address and port */
if ((hp = gethostbyname(hostname)) == NULL)
return -2; /* check h_errno for cause of error */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)hp->h_addr_list[0],
(char *)&serveraddr.sin_addr.s_addr, hp->h_length);
serveraddr.sin_port = htons(port);
/* Establish a connection with the server */
if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
return client_sock;
}
/* $end open_client_sock */
/*
* open_listen_sock - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
/* $begin open_listen_sock */
int open_listen_sock(int port)
{
int listen_sock, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listen_sock will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listen_sock, LISTENQ) < 0)
return -1;
return listen_sock;
}
/* $end open_listen_sock */
/* $end wrapper.c */

78
server-exp2/common.h Normal file
View File

@@ -0,0 +1,78 @@
/* $begin common.h */
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <stdbool.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* defined by BIND for DNS errors */
extern char **environ; /* defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Client/server helper functions */
int open_client_sock(char *hostname, int portno);
int open_listen_sock(int portno);
#endif /* __COMMON_H__ */
/* $end common.h */

BIN
server-exp2/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
server-exp2/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

13
server-exp2/index.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<title>the example web</title>
</head>
<body>
<H1>webserver test page</H1>
<p>
Not pretty but should prove that webserver works:-)
<p>
<IMG SRC="example.jpg">
</body>
</html>

200
server-exp2/perf.txt Normal file
View File

@@ -0,0 +1,200 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls us/call us/call name
42.86 0.09 0.09 23039432 0.00 0.00 rio_read
28.57 0.15 0.06 15339432 0.00 0.01 rio_readlineb
9.52 0.17 0.02 100001 0.20 1.69 read_requesthdrs
9.52 0.19 0.02 100000 0.20 0.20 get_filetype
4.76 0.20 0.01 100001 0.10 2.10 process_trans
4.76 0.21 0.01 100001 0.10 0.10 rio_readinitb
0.00 0.21 0.00 200000 0.00 0.00 rio_writen
0.00 0.21 0.00 100000 0.00 0.20 feed_static
0.00 0.21 0.00 100000 0.00 0.00 is_static
0.00 0.21 0.00 100000 0.00 0.00 parse_static_uri
0.00 0.21 0.00 1 0.00 0.00 open_listen_sock
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Copyright (C) 2012-2024 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 4.76% of 0.21 seconds
index % time self children called name
0.01 0.20 100001/100001 main [2]
[1] 100.0 0.01 0.20 100001 process_trans [1]
0.02 0.15 100001/100001 read_requesthdrs [3]
0.00 0.02 100000/100000 feed_static [6]
0.01 0.00 100001/100001 rio_readinitb [8]
0.00 0.00 100001/15339432 rio_readlineb [4]
0.00 0.00 100000/100000 is_static [10]
0.00 0.00 100000/100000 parse_static_uri [11]
-----------------------------------------------
<spontaneous>
[2] 100.0 0.00 0.21 main [2]
0.01 0.20 100001/100001 process_trans [1]
0.00 0.00 1/1 open_listen_sock [12]
-----------------------------------------------
0.02 0.15 100001/100001 process_trans [1]
[3] 80.5 0.02 0.15 100001 read_requesthdrs [3]
0.06 0.09 15239431/15339432 rio_readlineb [4]
-----------------------------------------------
0.00 0.00 100001/15339432 process_trans [1]
0.06 0.09 15239431/15339432 read_requesthdrs [3]
[4] 71.4 0.06 0.09 15339432 rio_readlineb [4]
0.09 0.00 23039432/23039432 rio_read [5]
-----------------------------------------------
0.09 0.00 23039432/23039432 rio_readlineb [4]
[5] 42.9 0.09 0.00 23039432 rio_read [5]
-----------------------------------------------
0.00 0.02 100000/100000 process_trans [1]
[6] 9.5 0.00 0.02 100000 feed_static [6]
0.02 0.00 100000/100000 get_filetype [7]
0.00 0.00 200000/200000 rio_writen [9]
-----------------------------------------------
0.02 0.00 100000/100000 feed_static [6]
[7] 9.5 0.02 0.00 100000 get_filetype [7]
-----------------------------------------------
0.01 0.00 100001/100001 process_trans [1]
[8] 4.8 0.01 0.00 100001 rio_readinitb [8]
-----------------------------------------------
0.00 0.00 200000/200000 feed_static [6]
[9] 0.0 0.00 0.00 200000 rio_writen [9]
-----------------------------------------------
0.00 0.00 100000/100000 process_trans [1]
[10] 0.0 0.00 0.00 100000 is_static [10]
-----------------------------------------------
0.00 0.00 100000/100000 process_trans [1]
[11] 0.0 0.00 0.00 100000 parse_static_uri [11]
-----------------------------------------------
0.00 0.00 1/1 main [2]
[12] 0.0 0.00 0.00 1 open_listen_sock [12]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function is in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Copyright (C) 2012-2024 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Index by function name
[6] feed_static [11] parse_static_uri [8] rio_readinitb
[7] get_filetype [1] process_trans [4] rio_readlineb
[10] is_static [3] read_requesthdrs [9] rio_writen
[12] open_listen_sock [5] rio_read (common.c)

6
server-exp2/t1.txt Normal file
View File

@@ -0,0 +1,6 @@
hello
world
dgut
computer
Hello
WORLD

6
server-exp2/t2.txt Normal file
View File

@@ -0,0 +1,6 @@
hello
world
dgut
computer
Hello
WORLD

45
server-exp2/taskline.c Normal file
View File

@@ -0,0 +1,45 @@
/* task pool management fucntions */
/* task_pool_init */
#include "common.h"
#include "taskline.h"
void task_line_init(task_line_t *tlp, int n)
{
tlp->taskp = calloc(n, sizeof(task_t));
tlp->cnt = n; /* socks holds max of n items */
tlp->inpos= tlp->outpos = 0; /* Empty socks iff inpos== outpos */
sem_init(&tlp->mutex, 0, 1); /* Binary semaphore for locking */
sem_init(&tlp->avail, 0, tlp->cnt);/* Initially, socks has cnt empty cell */
sem_init(&tlp->ready, 0, 0); /* Initially, socks has zero data items */
}
/* Clean up task line */
void task_line_deinit(task_line_t *tlp)
{
free(tlp->taskp);
}
/* Insert item onto the rear of task line */
void task_insert (task_line_t *tlp, task_t item)
{ sem_wait(&tlp->avail); /* Wait for available cell */
sem_wait(&tlp->mutex); /* Lock the shared variable inpos pointer */
tlp->taskp[tlp->inpos] = item; /* Insert the item */
tlp->inpos =(tlp-> inpos +1)%(tlp->cnt); /* adjuset inpos point */
sem_post(&tlp->mutex); /* Unlock the buffer */
sem_post(&tlp->ready); /* Announce available item */
}
/* Remove and return the first item from task_pool */
task_t task_remove(task_line_t *tlp)
{
task_t item;
sem_wait(&tlp->ready); /* Wait for available item */
sem_wait(&tlp->mutex); /* Lock the shared pointer variable tp->outpos */
item = tlp->taskp[tlp->outpos]; /* Remove the item */
tlp->outpos=(tlp->outpos+1)%(tlp->cnt); /* adjuset outpos point */
sem_post(&tlp->avail); /* Announce available slot */
sem_post(&tlp->mutex); /* Unlock the shared pointer variable tp->outpos */
return item;
}

20
server-exp2/taskline.h Normal file
View File

@@ -0,0 +1,20 @@
typedef struct _task_t {
int conn_sock; //客户连接socket
int hit; //第几个客户
} task_t;
typedef struct {
task_t *taskp; /* Buffer array */
int cnt; /* Maximum number of cell */
int inpos; /* buf[inpos] is first available cell */
int outpos; /* buf[outpos] is fist item */
sem_t mutex; /* Protects accesses to socks */
sem_t avail; /* Counts available cells */
sem_t ready; /* Counts ready items */
} task_line_t;
/* task line wrapper functions */
void task_line_init(task_line_t *tlp, int n);
void task_line_deinit(task_line_t *tlp);
void task_insert(task_line_t *tlp, task_t item);
task_t task_remove(task_line_t *tlp);

8
server-exp2/test.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>a simple page for testing tiny</title>
<head>
<body>
Hello World
</body>
</html>

BIN
server-exp2/togglec Executable file

Binary file not shown.

30
server-exp2/togglec.c Normal file
View File

@@ -0,0 +1,30 @@
#include "common.h"
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
//sleep(1);
send(client_sock, buf, strlen(buf),0);
recv(client_sock, buf, MAXLINE,0);
fputs(buf, stdout);
}
close(client_sock);
exit(0);
}

6
server-exp2/togglecm Normal file
View File

@@ -0,0 +1,6 @@
for i in {1..20}
do
./togglec localhost 12345 < $1
sleep 1
done

BIN
server-exp2/togglesp Executable file

Binary file not shown.

82
server-exp2/togglesp.c Normal file
View File

@@ -0,0 +1,82 @@
#include "common.h"
void sigchld_handler(int sig)
{
while (waitpid(-1, 0, WNOHANG) > 0);
return;
}
typedef void (*sighandler_t) (int sig) ;
void Signal(int sig, sighandler_t handler){
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // 确保被信号中断的系统调用自动重启
sigaction(SIGCHLD, &sa, NULL);
}
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
socklen_t clientlen=sizeof(struct sockaddr_in);
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
//设置SIGHLD的信号处理函数用于收割结束的子进程
Signal(SIGCHLD, sigchld_handler);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for(hit=1; ; hit++) {
conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
if (fork() == 0) {
close(listen_sock); /* Child process closes its listening socket */
toggle(conn_sock,hit); /* Child process services client */
close(conn_sock); /* Child process closes connection with client */
exit(0); /* Child process exits */
}
close(conn_sock); /* Parent closes connected socket (important!) */
}
}

BIN
server-exp2/togglest Executable file

Binary file not shown.

85
server-exp2/togglest.c Normal file
View File

@@ -0,0 +1,85 @@
#include "common.h"
void toggle(int conn_sock, int hit);
void *serve_client(void *vargp);
typedef struct _client_data_t {
int conn_sock; //客户连接socket
int hit; //第几个客户
} client_data_t;
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port,*conn_sock_p ;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
client_data_t *cdp; //连接客户信息
int hit; //连接客户计数
socklen_t clientlen=sizeof(struct sockaddr_in);
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
cdp = malloc(sizeof(client_data_t));
cdp->conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
cdp->hit=hit;
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
pthread_create(&tid, NULL, serve_client, (void *)cdp);
}
}
/* thread routine */
void * serve_client (void *vargp)
{
int hit,conn_sock;
client_data_t cd;
cd = *(client_data_t *)vargp;
pthread_detach(pthread_self());
free(vargp);
toggle(cd.conn_sock,cd.hit);
close(conn_sock);
return NULL;
}
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}

104
server-exp2/togglest_pool.c Normal file
View File

@@ -0,0 +1,104 @@
#include "common.h"
#include "pool.h"
#define NTHREADS 4
#define SBUFSIZE 16
void toggle(int conn_sock,int hit);
void *serve_client(void *vargp);
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
//printf("线程%d服务第%d个客户请求通信开始\n",tid,hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
sleep(1);
send (conn_sock, buf, n, 0);
}
//printf("线程%d服务第%d个客户请求通信结束\n",tid,hit);
}
//示例任务
void handle_request(int sock, int taskid, int tid) {
printf("线程%d服务第%d个客户通信开始\n",tid,taskid);
//usleep(1);
toggle(sock,taskid);
printf("线程%d服务第%d个客户通信结束\n",tid,taskid);
close(sock);
}
int main(int argc, char **argv) {
int listen_sock, conn_sock, port,i;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
int nth[NTHREADS];
socklen_t clientlen=sizeof(struct sockaddr_in);
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
printf("start main\n");
// 初始化线程池
threadpool* pool = initThreadPool(NTHREADS);
// 创建示例任务并将其添加到线程池
for (hit=1; ; hit++) {
conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
//printf("server connected to %s (%s)\n", hp->h_name, haddrp);
task* new_task = (task*)malloc(sizeof(task));
new_task->function = handle_request; // 假设任务函数为 toggle
new_task->taskid = hit; // 任务参数,假设为 socket fd 或其他参数
new_task->sock=conn_sock;
addTaskToThreadPool(pool, new_task); /* Insert conn_sock in task pool */
}
// 等待线程池中的所有任务执行完毕
waitThreadPool(pool);
// 销毁线程池
destroyThreadPool(pool);
printf("stop main\n");
return 0;
}

BIN
server-exp2/togglest_pre Executable file

Binary file not shown.

100
server-exp2/togglest_pre.c Normal file
View File

@@ -0,0 +1,100 @@
#include "common.h"
#include "taskline.h"
#define NTHREADS 4
#define SBUFSIZE 16
void toggle(int conn_sock,int hit);
void *handle_request(void *vargp);
task_line_t tlp; /* task pool: shared buffer of connected descriptors */
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port,i;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
task_t item; //连接客户信息socket和客户编号
int nth[NTHREADS];
socklen_t clientlen=sizeof(struct sockaddr_in);
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
port = atoi(argv[1]);
task_line_init(&tlp, SBUFSIZE);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (i = 0; i < NTHREADS; i++) { /* Create worker threads */
nth[i]=i;
pthread_create(&tid, NULL, handle_request, (void *)&nth[i]);
}
for (hit=1; ; hit++) {
item.conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
item.hit=hit;
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
task_insert(&tlp, item); /* Insert conn_sock in task pool */
}
}
void *handle_request(void *vargp)
{
int tid=*(int*) vargp;
task_t item;
pthread_detach(pthread_self());
while (1) {
item=task_remove(&tlp); /* Remove a task from task line */
printf("线程%d服务第%d个客户通信开始\n",tid,item.hit);
toggle(item.conn_sock,item.hit); /* Serve client */
close(item.conn_sock);
printf("线程%d服务第%d个客户通信结束\n",tid,item.hit);
}
}
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
}

2
server-exp2/urls Normal file
View File

@@ -0,0 +1,2 @@
http://127.0.0.1:8088/index.html
http://127.0.0.1:8088/test.html

BIN
server-exp2/webclient Executable file

Binary file not shown.

55
server-exp2/webclient.c Normal file
View File

@@ -0,0 +1,55 @@
#include <arpa/inet.h> // 提供IP地址转换函数
#include <netinet/in.h> // 提供套接字地址结构定义
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库函数如exit()
#include <string.h> // 字符串操作函数
#include <sys/socket.h> // 套接字相关函数
#include <sys/types.h> // 数据类型定义
#include <unistd.h> // POSIX API如read()和write()
//#define PORT 8181 /* 目标服务器的端口号 */
//#define IP_ADDRESS "192.168.0.8" /* 目标服务器的IP地址 */
#define BUFSIZE 8196 /* 缓冲区大小 */
char *command = "GET /index.html HTTP/1.0 \r\n\r\n"; /* HTTP GET 请求命令 */
// 错误处理函数,打印错误信息并退出程序
void pexit(char *msg) {
perror(msg); // 打印错误信息
exit(1); // 退出程序
}
int main(int argc, char *argv[]) {//客户端启动命令为"./client 127.0.0.1 8088",argv[1]是IP地址argv[2]是端口号8088
int i, sockfd; // sockfd是套接字文件描述符
char buffer[BUFSIZE]; // 用于存储从服务器接收的数据
struct sockaddr_in serv_addr; // 定义服务器地址结构
// 打印尝试连接服务器的信息
printf("客户端尝试连接到 %s 和端口 %s\n", argv[1], argv[2]);
// 创建套接字使用IPv4和TCP协议
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) pexit("socket() 创建失败");
// 配置服务器地址
serv_addr.sin_family = AF_INET; // 地址族为IPv4
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址,如"127.0.0.1"
serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号,如"8088"
// 尝试连接到服务器
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
pexit("connect() 连接失败");
// 发送HTTP GET请求到服务器
printf("发送字节数=%ld %s\n", strlen(command), command);
if (write(sockfd, command, strlen(command)) < 0) pexit("write() 发送请求失败");
// 循环读取服务器返回的数据,并输出到标准输出
while ((i = read(sockfd, buffer, BUFSIZE)) > 0) {
if (write(1, buffer, i) < 0) // 1表示标准输出
pexit("write() 输出到标准输出失败");
}
// 关闭套接字,释放资源
close(sockfd);
return 0; // 程序正常退出
}

BIN
server-exp2/weblet Executable file

Binary file not shown.

265
server-exp2/weblet.c Normal file
View File

@@ -0,0 +1,265 @@
#include "common.h"
void process_trans(int fd,int hit);
void read_requesthdrs(rio_t *rp,int hit,int nrh);
int is_static(char *uri);
void parse_static_uri(char *uri, char *filename);
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs);
void feed_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void feed_dynamic(int fd, char *filename, char *cgiargs);
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description);
/* 支持的文件扩展名及其对应的MIME类型 */
struct {
char *ext; // 文件扩展名
char *filetype; // MIME类型
} extensions[] = {
{".gif", "image/gif"},
{".jpg", "image/jpg"},
{".jpeg", "image/jpeg"},
{".png", "image/png"},
{".ico", "image/ico"},
{".zip", "image/zip"},
{".gz", "image/gz"},
{".tar", "image/tar"},
{".htm", "text/html"},
{".html", "text/html"},
{0, 0} // 结束标志
};
/* 调试模式宏定义 */
#ifdef DEBUG
#define printf2(format, var) printf(format,var)
#define printf3(format,var1,var2) printf(format,var1,var2)
#else
#define printf2(format, var)
#define printf3(format,var1,var2)
#endif
/* 全局变量说明 */
/* Ctrl+C信号处理函数 */
void ctrlc_handler(int sig)
{
printf("您按下了Ctrl+C终止了Web服务器\n");
exit(0);
}
/* 主函数:监听端口并处理客户端连接 */
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port;
int hit; // 请求计数器
socklen_t clientlen;
struct sockaddr_in clientaddr;
/* 检查命令行参数 */
if (argc != 2) {
fprintf(stderr, "用法: %s <端口号>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
for (hit=1; ; hit++) { //hit为第几次请求或第几次http事务
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
process_trans(conn_sock,hit); // 处理HTTP事务
close(conn_sock);
}
exit(0);
}
/* 处理HTTP事务的核心函数 */
void process_trans(int fd,int hit)
{
int static_flag; // 是否为静态资源标志
struct stat sbuf; // 文件状态信息
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
int rhn=0; // 请求头计数器
/* 设置Ctrl+C信号处理 */
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
(void)printf("错误: 无法设置SIGINT信号处理\n");
printf2("第%d次请求开始\n",hit);
/* 读取请求行和请求头 */
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);
printf3("请求头%d:%s", ++rhn, buf);
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) { // 仅支持GET方法
error_request(fd, method, "501", "未实现",
"本服务器不支持该请求方法");
return;
}
read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头
static_flag = is_static(uri); // 判断资源类型
if(static_flag)
parse_static_uri(uri, filename); // 解析静态资源路径
else
parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数
/* 检查文件是否存在及权限 */
if (stat(filename, &sbuf) < 0) {
error_request(fd, filename, "404", "未找到",
"服务器无法找到该文件");
return;
}
/* 根据资源类型发送响应 */
if (static_flag) { // 静态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无权读取该文件");
return;
}
feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容
}
else { // 动态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无法执行该CGI程序");
return;
}
feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出
}
}
/* 判断URI是否对应静态资源 */
int is_static(char *uri)
{
return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源
}
/* 构造HTTP错误响应 */
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description)
{
char buf[MAXLINE], body[MAXBUF];
/* 构建错误页面内容 */
sprintf(body, "<html><title>错误请求</title>");
sprintf(body, "%s<body bgcolor=\"#ffffff\">\r\n", body);
sprintf(body, "%s<h1>%s %s</h1>\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s</p>\r\n", body, description, cause);
sprintf(body, "%s<hr><em>weblet Web服务器</em>\r\n", body);
/* 发送HTTP响应头 */
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
rio_writen(fd, buf, strlen(buf));
/* 发送错误页面内容 */
rio_writen(fd, body, strlen(body));
}
/* 读取请求头直到空行 */
void read_requesthdrs(rio_t *rp,int hit, int rhn)
{
char buf[MAXLINE];
rio_readlineb(rp, buf, MAXLINE);
while(strcmp(buf, "\r\n")) {
printf3("请求头%d:%s", ++rhn, buf);
rio_readlineb(rp, buf, MAXLINE);
}
printf2("第%d次请求结束\n\n",hit);
}
/* 解析静态资源URI到文件路径 */
void parse_static_uri(char *uri, char *filename)
{
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "index.html"); // 默认返回index.html
}
/* 解析动态资源URI到文件路径和参数 */
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs)
{
char *ptr = index(uri, '?');
if (ptr) {
strcpy(cgiargs, ptr+1); // 提取参数部分请求头
*ptr = '\0'; // 截断URI
} else {
strcpy(cgiargs, "");
}
strcpy(filename, ".");
strcat(filename, uri);
}
/* 发送静态文件内容 */
void feed_static(int fd, char *filename, int filesize)
{
int srcfd;
char *srcp, filetype[MAXLINE], buf[MAXBUF];
/* 发送HTTP响应头 */
get_filetype(filename, filetype);
sprintf(buf, "HTTP/1.0 200 OK\r\n");
sprintf(buf, "%sServer: weblet Web Server\r\n", buf);
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
rio_writen(fd, buf, strlen(buf));
/* 发送文件内容 */
srcfd = open(filename, O_RDONLY, 0);
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
close(srcfd);
rio_writen(fd, srcp, filesize);
munmap(srcp, filesize);
}
/* 根据文件扩展名获取MIME类型 */
void get_filetype(char *filename, char *filetype)
{
int i, len;
strcpy(filetype, "text/html"); // 默认类型
for (i = 0; extensions[i].ext != 0; i++) {
len = strlen(extensions[i].ext);
if (!strcmp(&filename[strlen(filename)-len], extensions[i].ext)) {
strcpy(filetype, extensions[i].filetype);
break;
}
}
}
/* 启动子进程执行CGI程序 */
void feed_dynamic(int fd, char *filename, char *cgiargs)
{
char buf[MAXLINE], *emptylist[] = { NULL };
int pfd[2];
/* 构造HTTP响应头 */
sprintf(buf, "HTTP/1.0 200 OK\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Server: weblet Web Server\r\n");
rio_writen(fd, buf, strlen(buf));
/* 创建管道并启动子进程 */
(void)pipe(pfd);
if (fork() == 0) { // 子进程
close(pfd[1]); // 关闭写端
dup2(pfd[0], STDIN_FILENO); // 重定向标准输入
dup2(fd, STDOUT_FILENO); // 重定向标准输出到客户端
execve(filename, emptylist, environ); // 执行CGI程序
}
close(pfd[0]); // 父进程关闭读端
(void)write(pfd[1], cgiargs, strlen(cgiargs)+1); // 传递参数
wait(NULL); // 等待子进程结束
close(pfd[1]); // 关闭写端
}

View File

@@ -0,0 +1,19 @@
common.h : 公共头文件、函数声明、常量定义等由教材wrapper.h简化而来
common.c : RIO函数库和打开网络连接函数源代码教材中libwrapper.c简化而来
webclient.c: web客户端源代码(《操作系统实验教程》第1章的client.c)
webserver.c: web服务器源代码(即教材《Linux编程》第8章的weblet.c)
togglec.c : toggle客户端源代码(教材第8章源代码
togglesi.c : 迭代式toggle服务器源代码教材《Linux编程》第8章源代码
togglesp.c : 多进程toggle服务器源代码教材《Linux编程》第9章源代码
togglest.c : 多线程toggle服务器源代码教材《Linux编程》第9章源代码
togglest_pre.c : 预线程toggle服务器源代码教材《Linux编程》第9章源代码
togglest_pool.c : 线程池toggle服务器源代码
./cgi-bin/add.c: 生成动态网页的cgi程序教材《Linux编程》第8章
Makefile: 目标和源代码间相互依赖关系文件参考教材《Linux编程》第3章
index.html: 缺省网页文件
example.jpg: 网页文件index.html中内嵌的图片
favicon.ico: index.html中内嵌的图标
test.html: 第2个测试用网页
urls: 记录运行http_load执行时访问哪些网址

BIN
web1/bb/ftps Executable file

Binary file not shown.

1
web1/bb/hp.txt Normal file
View File

@@ -0,0 +1 @@
yeyettttttttttttttttttt

1
web1/bb/ooo.txt Normal file
View File

@@ -0,0 +1 @@
yuuuuutttt

175
web1/bb/perf.txt Normal file
View File

@@ -0,0 +1,175 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls us/call us/call name
42.86 0.03 0.03 28890 1.04 2.08 process_trans
28.57 0.05 0.02 115560 0.17 0.26 rio_readlineb
14.29 0.06 0.01 2239020 0.00 0.00 rio_read
14.29 0.07 0.01 _init
0.00 0.07 0.00 57778 0.00 0.00 rio_writen
0.00 0.07 0.00 28890 0.00 0.78 read_requesthdrs
0.00 0.07 0.00 28890 0.00 0.00 rio_readinitb
0.00 0.07 0.00 28889 0.00 0.00 feed_static
0.00 0.07 0.00 28889 0.00 0.00 get_filetype
0.00 0.07 0.00 28889 0.00 0.00 is_static
0.00 0.07 0.00 28889 0.00 0.00 parse_static_uri
0.00 0.07 0.00 1 0.00 0.00 open_listen_sock
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 14.29% of 0.07 seconds
index % time self children called name
0.03 0.03 28890/28890 main [2]
[1] 85.7 0.03 0.03 28890 process_trans [1]
0.00 0.02 28890/28890 read_requesthdrs [4]
0.01 0.00 28890/115560 rio_readlineb [3]
0.00 0.00 28890/28890 rio_readinitb [8]
0.00 0.00 28889/28889 is_static [11]
0.00 0.00 28889/28889 parse_static_uri [12]
0.00 0.00 28889/28889 feed_static [9]
-----------------------------------------------
<spontaneous>
[2] 85.7 0.00 0.06 main [2]
0.03 0.03 28890/28890 process_trans [1]
0.00 0.00 1/1 open_listen_sock [13]
-----------------------------------------------
0.01 0.00 28890/115560 process_trans [1]
0.01 0.01 86670/115560 read_requesthdrs [4]
[3] 42.9 0.02 0.01 115560 rio_readlineb [3]
0.01 0.00 2239020/2239020 rio_read [5]
-----------------------------------------------
0.00 0.02 28890/28890 process_trans [1]
[4] 32.1 0.00 0.02 28890 read_requesthdrs [4]
0.01 0.01 86670/115560 rio_readlineb [3]
-----------------------------------------------
0.01 0.00 2239020/2239020 rio_readlineb [3]
[5] 14.3 0.01 0.00 2239020 rio_read [5]
-----------------------------------------------
<spontaneous>
[6] 14.3 0.01 0.00 _init [6]
-----------------------------------------------
0.00 0.00 57778/57778 feed_static [9]
[7] 0.0 0.00 0.00 57778 rio_writen [7]
-----------------------------------------------
0.00 0.00 28890/28890 process_trans [1]
[8] 0.0 0.00 0.00 28890 rio_readinitb [8]
-----------------------------------------------
0.00 0.00 28889/28889 process_trans [1]
[9] 0.0 0.00 0.00 28889 feed_static [9]
0.00 0.00 57778/57778 rio_writen [7]
0.00 0.00 28889/28889 get_filetype [10]
-----------------------------------------------
0.00 0.00 28889/28889 feed_static [9]
[10] 0.0 0.00 0.00 28889 get_filetype [10]
-----------------------------------------------
0.00 0.00 28889/28889 process_trans [1]
[11] 0.0 0.00 0.00 28889 is_static [11]
-----------------------------------------------
0.00 0.00 28889/28889 process_trans [1]
[12] 0.0 0.00 0.00 28889 parse_static_uri [12]
-----------------------------------------------
0.00 0.00 1/1 main [2]
[13] 0.0 0.00 0.00 1 open_listen_sock [13]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function is in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the numbe./perf.txt

BIN
web1/cgi-bin/add Normal file

Binary file not shown.

27
web1/cgi-bin/add.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 8192
int main(void) {
char *buf, *p;
char content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments from standard input */
scanf("%d&%d", &n1, &n2);
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int) strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}

242
web1/common.c Normal file
View File

@@ -0,0 +1,242 @@
/* $begin common.c */
#include "common.h"
/*********************************************************************
* The Rio package - robust I/O functions
**********************************************************************/
/*
* rio_readn - robustly read n bytes (unbuffered)
*/
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readn */
/*
* rio_writen - robustly write n bytes (unbuffered)
*/
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nwritten = 0; /* and call write() again */
else
return -1; /* errorno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
/* $end rio_writen */
/*
* rio_read - This is a wrapper for the Unix read() function that
* transfers min(n, rio_cnt) bytes from an internal buffer to a user
* buffer, where n is the number of bytes requested by the user and
* rio_cnt is the number of unread bytes in the internal buffer. On
* entry, rio_read() refills the internal buffer via a call to
* read() if the internal buffer is empty.
*/
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* refill if buf is empty */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* interrupted by sig handler return */
return -1;
}
else if (rp->rio_cnt == 0) /* EOF */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
/* $end rio_read */
/*
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer
*/
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */
/*
* rio_readnb - Robustly read n bytes (buffered)
*/
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readnb */
/*
* rio_readlineb - robustly read a text line (buffered)
*/
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* error */
}
*bufp = 0;
return n;
}
/* $end rio_readlineb */
/**********************************
* Wrappers for robust I/O routines
**********************************/
/********************************
* Client/server helper functions
********************************/
/*
* open_client_sock - open connection to server at <hostname, port>
* and return a socket descriptor ready for reading and writing.
* Returns -1 and sets errno on Unix error.
* Returns -2 and sets h_errno on DNS (gethostbyname) error.
*/
/* $begin open_client_sock */
int open_client_sock(char *hostname, int port)
{
int client_sock;
struct hostent *hp;
struct sockaddr_in serveraddr;
if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1; /* check errno for cause of error */
/* Fill in the server's IP address and port */
if ((hp = gethostbyname(hostname)) == NULL)
return -2; /* check h_errno for cause of error */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)hp->h_addr_list[0],
(char *)&serveraddr.sin_addr.s_addr, hp->h_length);
serveraddr.sin_port = htons(port);
/* Establish a connection with the server */
if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
return client_sock;
}
/* $end open_client_sock */
/*
* open_listen_sock - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
/* $begin open_listen_sock */
int open_listen_sock(int port)
{
int listen_sock, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listen_sock will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listen_sock, LISTENQ) < 0)
return -1;
return listen_sock;
}
/* $end open_listen_sock */
/* $end wrapper.c */

76
web1/common.h Normal file
View File

@@ -0,0 +1,76 @@
/* $begin common.h */
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* defined by BIND for DNS errors */
extern char **environ; /* defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Client/server helper functions */
int open_client_sock(char *hostname, int portno);
int open_listen_sock(int portno);
#endif /* __COMMON_H__ */
/* $end common.h */

1
web1/data.txt Normal file
View File

@@ -0,0 +1 @@
hello world

BIN
web1/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
web1/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
web1/ftpc Executable file

Binary file not shown.

109
web1/ftpc.c Normal file
View File

@@ -0,0 +1,109 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
ftp_file_t file;
FILE *fp;
long fileSize;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
char *token = strtok(buf, " \t\n"); // 提取FTP命令
if (strcmp(token, "get") == 0 || strcmp(token, "put") == 0) {
strncpy(file.cmd, token, MAXLINE - 1);
file.cmd[MAXLINE - 1] = '\0';
} else {
printf("Unsupported command: %s\n", token);
continue; // 如果不是get或put命令跳过处理
}
token = strtok(NULL, " \t\n"); // 提取文件名参数
if (token != NULL) {
strncpy(file.filename, token, MAXLINE - 1);
file.filename[MAXLINE - 1] = '\0';
} else {
printf("Filename is required for command: %s\n", file.cmd);
continue; // 如果没有提供文件名参数,跳过处理
}
if (strcmp(file.cmd,"put")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file");
continue;
} else {
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
}
send(client_sock, &file, sizeof(ftp_file_t), 0);
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
} else {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
send(client_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
fclose(fp);
}
}
else if (strcmp(file.cmd,"get")==0)
{
send(client_sock, &file, sizeof(ftp_file_t), 0);
recv(client_sock, &file, sizeof(ftp_file_t), 0); // 接收文件信息
int fileSize = atoi(file.fileSize);
if (fileSize > 0) {
char *fileContent = malloc(fileSize); // 根据文件大小分配内存
recv(client_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp != NULL) {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
} else {
perror("Error creating file");
}
free(fileContent); // 释放内存
}
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
}
}
close(client_sock);
exit(0);
}

101
web1/ftps.c Normal file
View File

@@ -0,0 +1,101 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXBUF];
ftp_file_t file;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, &file, sizeof(ftp_file_t),0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
if (strcmp(file.cmd,"put")==0)
{
int fileSize = atoi(file.fileSize);
char *fileContent = malloc(fileSize);
recv(conn_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp == NULL) {
perror("Error opening file for writing");
} else {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
}
free(fileContent);
strcpy(buf, "File received successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
else if (strcmp(file.cmd,"get")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
FILE *fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
strcpy(file.fileSize, "0");
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送错误信息
} else {
fseek(fp, 0, SEEK_END);
long fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送文件信息
fp = fopen(filePath, "r");
if (fp != NULL) {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
fclose(fp);
send(conn_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
strcpy(buf, "File sent successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
}
}
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

1
web1/hp.txt Normal file
View File

@@ -0,0 +1 @@
yeyettttttttttttttttttt

13
web1/index.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<title>the example web</title>
</head>
<body>
<H1>webserver test page</H1>
<p>
Not pretty but should prove that webserver works:-)
<p>
<IMG SRC="example.jpg">
</body>
</html>

1
web1/ooo.txt Normal file
View File

@@ -0,0 +1 @@
yuuuuutttt

BIN
web1/perf.txt Normal file

Binary file not shown.

8
web1/test.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>a simple page for testing tiny</title>
<head>
<body>
Hello World
</body>
</html>

BIN
web1/togglec Executable file

Binary file not shown.

29
web1/togglec.c Normal file
View File

@@ -0,0 +1,29 @@
#include "common.h"
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
send(client_sock, buf, strlen(buf),0);
recv(client_sock, buf, MAXLINE,0);
fputs(buf, stdout);
}
close(client_sock);
exit(0);
}

BIN
web1/togglesi Executable file

Binary file not shown.

57
web1/togglesi.c Normal file
View File

@@ -0,0 +1,57 @@
#include "common.h"
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

2
web1/urls Normal file
View File

@@ -0,0 +1,2 @@
http://127.0.0.1:8088/index.html
http://127.0.0.1:8088/test.html

BIN
web1/webclient Executable file

Binary file not shown.

55
web1/webclient.c Normal file
View File

@@ -0,0 +1,55 @@
#include <arpa/inet.h> // 提供IP地址转换函数
#include <netinet/in.h> // 提供套接字地址结构定义
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库函数如exit()
#include <string.h> // 字符串操作函数
#include <sys/socket.h> // 套接字相关函数
#include <sys/types.h> // 数据类型定义
#include <unistd.h> // POSIX API如read()和write()
//#define PORT 8181 /* 目标服务器的端口号 */
//#define IP_ADDRESS "192.168.0.8" /* 目标服务器的IP地址 */
#define BUFSIZE 8196 /* 缓冲区大小 */
char *command = "GET /index.html HTTP/1.0 \r\n\r\n"; /* HTTP GET 请求命令 */
// 错误处理函数,打印错误信息并退出程序
void pexit(char *msg) {
perror(msg); // 打印错误信息
exit(1); // 退出程序
}
int main(int argc, char *argv[]) {//客户端启动命令为"./webclient 127.0.0.1 8088",argv[1]是IP地址argv[2]是端口号8088
int i, sockfd; // sockfd是套接字文件描述符
char buffer[BUFSIZE]; // 用于存储从服务器接收的数据
struct sockaddr_in serv_addr; // 定义服务器地址结构
// 打印尝试连接服务器的信息
printf("客户端尝试连接到 %s 和端口 %s\n", argv[1], argv[2]);
// 创建套接字使用IPv4和TCP协议
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) pexit("socket() 创建失败");
// 配置服务器地址
serv_addr.sin_family = AF_INET; // 地址族为IPv4
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址,如"127.0.0.1"
serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号,如"8088"
// 尝试连接到服务器
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
pexit("connect() 连接失败");
// 发送HTTP GET请求到服务器
printf("发送字节数=%ld %s\n", strlen(command), command);
if (write(sockfd, command, strlen(command)) < 0) pexit("write() 发送请求失败");
// 循环读取服务器返回的数据,并输出到标准输出
while ((i = read(sockfd, buffer, BUFSIZE)) > 0) {
if (write(1, buffer, i) < 0) // 1表示标准输出
pexit("write() 输出到标准输出失败");
}
// 关闭套接字,释放资源
close(sockfd);
return 0; // 程序正常退出
}

BIN
web1/weblet Executable file

Binary file not shown.

278
web1/weblet.c Normal file
View File

@@ -0,0 +1,278 @@
#include "common.h"
void process_trans(int fd,int hit);
void read_requesthdrs(rio_t *rp,int hit,int nrh);
int is_static(char *uri);
void parse_static_uri(char *uri, char *filename);
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs);
void feed_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void feed_dynamic(int fd, char *filename, char *cgiargs);
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description);
/* 支持的文件扩展名及其对应的MIME类型 */
struct {
char *ext; // 文件扩展名
char *filetype; // MIME类型
} extensions[] = {
{".gif", "image/gif"},
{".jpg", "image/jpg"},
{".jpeg", "image/jpeg"},
{".png", "image/png"},
{".ico", "image/ico"},
{".zip", "image/zip"},
{".gz", "image/gz"},
{".tar", "image/tar"},
{".htm", "text/html"},
{".html", "text/html"},
{0, 0} // 结束标志
};
/* 调试模式宏定义 */
#ifdef DEBUG
#define printf2(format, var) printf(format,var)
#define printf3(format,var1,var2) printf(format,var1,var2)
#else
#define printf2(format, var)
#define printf3(format,var1,var2)
#endif
/* 全局变量说明 */
/* Ctrl+C信号处理函数 */
void ctrlc_handler(int sig)
{
printf("您按下了Ctrl+C终止了Web服务器\n");
exit(0);
}
/* 主函数:监听端口并处理客户端连接 */
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port;
int hit; // 请求计数器
socklen_t clientlen;
struct sockaddr_in clientaddr;
/* 检查命令行参数 */
if (argc != 2) {
fprintf(stderr, "用法: %s <端口号>\n", argv[0]);
exit(1);
}
/* 设置Ctrl+C信号处理 */
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
(void)printf("错误: 无法设置SIGINT信号处理\n");
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
for (hit=1; ; hit++) { //hit为第几次请求或第几次http事务
clientlen = sizeof(clientaddr);
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
process_trans(conn_sock,hit); // 处理HTTP事务
close(conn_sock);
}
exit(0);
}
/* 处理HTTP事务的核心函数 */
void process_trans(int fd,int hit)
{
int static_flag; // 是否为静态资源标志
struct stat sbuf; // 文件状态信息
char buf[MAXLINE], method[MAXLINE]="", uri[MAXLINE]="", version[MAXLINE]="http/1.1";
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
int rhn=0; // 请求头计数器
printf2("第%d次请求开始\n",hit);
/* 读取请求行和请求头 */
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);
printf3("请求头%d:%s", ++rhn, buf);
//判断第一个请求行是否正好有三个单词构成如GET / http/1.1
/*int wn=0; char *p=buf;
while(*p==' ' || *p=='\t') p++;
while(*p) {
if((*p==' '||*p=='\t') && *(p+1)!=' ') wn++;
p++;
}
if (wn==2) */
sscanf(buf, "%s %s %s", method, uri, version);
if (strcasecmp(method, "GET")) { // 仅支持GET方法
error_request(fd, method, "501", "未实现",
"本服务器不支持该请求方法");
return;
}
read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头
static_flag = is_static(uri); // 判断资源类型
if(static_flag)
parse_static_uri(uri, filename); // 解析静态资源路径
else
parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数
/* 检查文件是否存在及权限 */
if (stat(filename, &sbuf) < 0) {
error_request(fd, filename, "404", "未找到",
"服务器无法找到该文件");
return;
}
/* 根据资源类型发送响应 */
if (static_flag) { // 静态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无权读取该文件");
return;
}
feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容
}
else { // 动态资源处理
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
error_request(fd, filename, "403", "禁止访问",
"服务器无法执行该CGI程序");
return;
}
feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出
}
}
/* 判断URI是否对应静态资源 */
int is_static(char *uri)
{
return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源
}
/* 构造HTTP错误响应 */
void error_request(int fd, char *cause, char *errnum,
char *shortmsg, char *description)
{
char buf[MAXLINE], body[MAXBUF];
/* 构建错误页面内容 */
sprintf(body, "<html><title>错误请求</title>");
sprintf(body, "%s<body bgcolor=\"#ffffff\">\r\n", body);
sprintf(body, "%s<h1>%s %s</h1>\r\n", body, errnum, shortmsg);
sprintf(body, "%s<p>%s: %s</p>\r\n", body, description, cause);
sprintf(body, "%s<hr><em>weblet Web服务器</em>\r\n", body);
/* 发送HTTP响应头 */
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: text/html\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
rio_writen(fd, buf, strlen(buf));
/* 发送错误页面内容 */
rio_writen(fd, body, strlen(body));
}
/* 读取请求头直到空行 */
void read_requesthdrs(rio_t *rp,int hit, int rhn)
{
char buf[MAXLINE];
rio_readlineb(rp, buf, MAXLINE);
while(strcmp(buf, "\r\n")) {
printf3("请求头%d:%s", ++rhn, buf);
rio_readlineb(rp, buf, MAXLINE);
}
printf2("第%d次请求结束\n\n",hit);
}
/* 解析静态资源URI到文件路径 */
void parse_static_uri(char *uri, char *filename)
{
strcpy(filename, ".");
strcat(filename, uri);
if (uri[strlen(uri)-1] == '/')
strcat(filename, "index.html"); // 默认返回index.html
}
/* 解析动态资源URI到文件路径和参数 */
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs)
{
char *ptr = index(uri, '?');
if (ptr) {
strcpy(cgiargs, ptr+1); // 提取参数部分
*ptr = '\0'; // 截断URI
} else {
strcpy(cgiargs, "");
}
strcpy(filename, ".");
strcat(filename, uri);
}
/* 发送静态文件内容 */
void feed_static(int fd, char *filename, int filesize)
{
int srcfd;
char *srcp, filetype[MAXLINE], buf[MAXBUF];
/* 发送HTTP响应头 */
get_filetype(filename, filetype);
sprintf(buf, "HTTP/1.0 200 OK\r\n");
sprintf(buf, "%sServer: weblet Web Server\r\n", buf);
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
rio_writen(fd, buf, strlen(buf));
/* 发送文件内容 */
srcfd = open(filename, O_RDONLY, 0);
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
close(srcfd);
rio_writen(fd, srcp, filesize);
munmap(srcp, filesize);
}
/* 根据文件扩展名获取MIME类型 */
void get_filetype(char *filename, char *filetype)
{
int i, len;
strcpy(filetype, "text/html"); // 默认类型
for (i = 0; extensions[i].ext != 0; i++) {
len = strlen(extensions[i].ext);
if (!strcmp(&filename[strlen(filename)-len], extensions[i].ext)) {
strcpy(filetype, extensions[i].filetype);
break;
}
}
}
/* 启动子进程执行CGI程序 */
void feed_dynamic(int fd, char *filename, char *cgiargs)
{
char buf[MAXLINE], *emptylist[] = { NULL };
int pfd[2];
/* 构造HTTP响应头 */
sprintf(buf, "HTTP/1.0 200 OK\r\n");
rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Server: weblet Web Server\r\n");
rio_writen(fd, buf, strlen(buf));
/* 创建管道并启动子进程 */
(void)pipe(pfd);
if (fork() == 0) { // 子进程
close(pfd[1]); // 关闭写端
dup2(pfd[0], STDIN_FILENO); // 重定向标准输入
dup2(fd, STDOUT_FILENO); // 重定向标准输出到客户端
execve(filename, emptylist, environ); // 执行CGI程序
}
close(pfd[0]); // 父进程关闭读端
(void)write(pfd[1], cgiargs, strlen(cgiargs)+1); // 传递参数
wait(NULL); // 等待子进程结束
close(pfd[1]); // 关闭写端
}

View File

@@ -0,0 +1,14 @@
common.h : 公共头文件、函数声明、常量定义等由教材wrapper.h简化而来
common.c : RIO函数库和打开网络连接函数源代码教材中libwrapper.c简化而来
webclient.c: web客户端源代码(《操作系统实验教程》第1章的client.c)
webserver.c: web服务器源代码(即教材《Linux编程》第8章的weblet.c)
togglec.c : toggle客户端源代码(教材第8章源代码
togglesi.c : 迭代式toggle服务器源代码教材《Linux编程》第8章源代码
./cgi-bin/add.c: 生成动态网页的cgi程序教材《Linux编程》第8章
Makefile: 目标和源代码间相互依赖关系文件参考教材《Linux编程》第3章
index.html: 缺省网页文件
example.jpg: 网页文件index.html中内嵌的图片
favicon.ico: index.html中内嵌的图标
test.html: 第2个测试用网页
urls: 记录运行http_load执行时访问哪些网址
gmon.out: grof命令生成的性能数据文件