2023年广东工业大学软件体系架构实验。

2023年春

lab1:socket运用

使用原始socket()实现一个C/S架构的应用,支持服务器时间回显示;
要求:从客户端发送命令,接收服务器的时间并显示到终端;

sever.c 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <winsock2.h>

#pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;

int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) //初始化WinSock库
{
printf("WSAStartup failed!\n");
return 1;
}

// 创建套接字
SOCKET server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_sockfd == INVALID_SOCKET)
{
printf("Create socket failed!\n");
return 1;
}

// 设置套接字选项(可选)
int opt = 1;
setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));

// 绑定端口
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999); //绑定端口号
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_sockfd, (sockaddr*)&server_addr, sizeof(server_addr)) != 0)
{
printf("Bind port failed!\n");
closesocket(server_sockfd); //关闭套接字
return 1;
}

// 监听端口
if (listen(server_sockfd, 5) != 0)
{
printf("Listen failed!\n");
closesocket(server_sockfd); //关闭套接字
return 1;
}

printf("Server is listening...\n");

while(1)
{
// 等待客户端连接
sockaddr_in client_addr;
int client_len = sizeof(client_addr);
SOCKET conn_sockfd = accept(server_sockfd, (sockaddr*)&client_addr, &client_len);
if ( conn_sockfd == INVALID_SOCKET )
{
printf("Accept failed!\n");
continue;
}

// 读取客户端数据
char buff[1024];
memset(buff, 0, sizeof(buff));
recv(conn_sockfd, buff, sizeof(buff), 0);
printf("Received command from client: %s\n", buff);

// 判断客户端发送的命令,并发送对应的响应
if (strcmp(buff, "time") == 0)
{
time_t now = time(NULL);
char* timeStr = ctime(&now);
send(conn_sockfd, timeStr, strlen(timeStr), 0);
printf("Sent time to client: %s", timeStr);
}
else
{
const char* errMsg = "Invalid command!";
send(conn_sockfd, errMsg, strlen(errMsg), 0);
printf("Sent error message to client: %s", errMsg);
}

// 关闭连接套接字
closesocket(conn_sockfd);
}

// 关闭监听套接字
closesocket(server_sockfd);
WSACleanup(); //释放WinSock库

return 0;
}

client.c 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;

int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) //初始化WinSock库
{
printf("WSAStartup failed!\n");
return 1;
}

// 创建套接字
SOCKET client_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_sockfd == INVALID_SOCKET)
{
printf("Create socket failed!\n");
return 1;
}

// 连接服务器
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9999); //连接到服务器的端口号
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //连接到服务器的IP地址
if (connect(client_sockfd, (sockaddr*)&server_addr, sizeof(server_addr)) != 0)
{
printf("Connect server failed!\n");
closesocket(client_sockfd); //关闭套接字
return 1;
}

// 发送命令
char command[100];
printf("Please enter command (time): ");
scanf("%s", command);
send(client_sockfd, command, strlen(command), 0);

// 接收响应
char buff[1024];
memset(buff, 0, sizeof(buff));
recv(client_sockfd, buff, sizeof(buff), 0);
printf("Received message from server: %s\n", buff);

// 关闭套接字
closesocket(client_sockfd);
WSACleanup(); //释放WinSock库

return 0;
}

演示步骤

以下是基于 Windows 操作系统的演示步骤,具体步骤可能因环境不同而有所不同:

  1. 打开两个命令提示符窗口,分别用作服务器端和客户端。

  2. 在服务器端窗口中,输入以下命令:

    1
    gcc server.c -o server.exe -lws2_32

    该命令将编译 server.c 文件,并生成可执行文件 server.exe。

  3. 在服务器端窗口中,输入以下命令启动服务器:

    1
    server.exe

    该命令将启动服务器,监听 9999 端口,并等待客户端连接。如果启动成功,将会输出以下信息:

    1
    Server started, waiting for connection...
  4. 在客户端窗口中,输入以下命令:

    1
    gcc client.c -o client.exe -lws2_32

    该命令将编译 client.c 文件,并生成可执行文件 client.exe。

  5. 在客户端窗口中,输入以下命令启动客户端:

    1
    client.exe

    此时将会提示输入命令:

    1
    Please enter command (time):
  6. 在客户端窗口中,输入 time 命令并回车,等待服务器响应:

    1
    time

    如果连接成功,客户端将会收到服务器返回的时间信息,并输出到控制台:

    1
    Received message from server: Tue Sep 28 04:59:36 2021
  7. 可以在客户端窗口中多次输入命令以观察不同的服务器响应。

  8. 在客户端窗口中输入 exit 命令退出客户端:

    1
    exit

    或直接关闭客户端窗口。

  9. 如果服务器端不再需要运行,可以在服务器端窗口中按 Ctrl+C 组合键退出服务器程序。

注意:在使用命令行输入命令和输出响应时,请确保程序不会受到缓冲区溢出攻击等风险。建议使用安全的输入函数和输出函数,例如 fgets()printf_s()

注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 代码中使用了 WinSock 库,需要链接 ws2_32.lib 库,否则会报错。

2. 代码中使用了 SOCK_STREAM 协议,即 TCP 协议,因此需要在服务器端先监听端口,而在客户端连接之前,先绑定端口并监听。

3. 代码中使用了命令行输入和输出,因此需要在控制台中运行程序。

4. 代码中只实现了一个简单的 C/S 应用,其他功能需要自行扩展。
5. 代码中使用了原始的 socket() 函数,需要注意以下几点:

socket() 函数返回的是一个套接字描述符,需要通过该描述符来操作套接字。

网络字节序是大端序,需要注意数据在传输过程中是否需要进行大小端转换。

send() 和 recv() 函数返回的是实际发送或接收的字节数,需要考虑分包发送和接收的情况,避免数据丢失和粘包等问题。

6. 代码中使用了 time() 函数获取当前时间,需要注意该函数返回的是自 1970 年 1 月 1 日 00:00:00 GMT 到当前时间的秒数,因此需要使用 ctime() 函数将其转换为可读的时间字符串。

7. 代码中使用了 scanf() 函数从命令行读取用户输入的命令,需要注意该函数容易受到缓冲区溢出攻击,建议使用 fgets() 等安全的输入函数。

8. 密传输等,来保障网络传输的安全性。在实际应用中,需要考虑网络连接的安全问题,如身份验证、加密传输等,来保障网络传输的安全性。

lab2:enroll 方式

  • 注意要求linux的操作系统!

EPoll是Linux专属的一种I/O多路复用方式,因此不能在Windows环境下使用。在Windows上可以使用select等其他的I/O多路复用方式,

server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import socket
import select

SERVER_HOST = 'localhost'
SERVER_PORT = 8000
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'

# 创建套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setblocking(0)

# 绑定套接字,开始监听
server_socket.bind((SERVER_HOST, SERVER_PORT))
server_socket.listen(5)

# 创建epoll对象,并注册server_socket
epoll = select.epoll()
epoll.register(server_socket.fileno(), select.EPOLLIN)

# 存储客户端连接信息
client_sockets = {}
client_data = {}

print(f'Server started at {SERVER_HOST}:{SERVER_PORT}')

try:
while True:
events = epoll.poll(1)
for fileno, event in events:
# 如果是server_socket事件,表示有新的客户端连接请求
if fileno == server_socket.fileno():
try:
while True:
conn, client_addr = server_socket.accept()
conn.setblocking(0)

# 注册新的客户端连接
epoll.register(conn.fileno(), select.EPOLLIN)
client_sockets[conn.fileno()] = conn
client_data[conn.fileno()] = bytearray()

except socket.error:
pass

# 如果是已连接的客户端事件,表示有消息传入
elif event & select.EPOLLIN:
client_socket = client_sockets[fileno]
data = client_socket.recv(1024)

# 如果客户端关闭,则注销epoll事件并关闭连接
if not data:
epoll.unregister(fileno)
client_socket.close()
del client_sockets[fileno]
del client_data[fileno]
continue

# 将客户端发来的消息转发给其他客户端
data = client_data[fileno] + data # 拼接接收到的消息
if EOL1 in data or EOL2 in data:
client_data[fileno] = bytearray()
for socket_fileno, socket_obj in client_sockets.items():
if socket_fileno != fileno:
socket_obj.sendall(data)
else:
client_data[fileno] = data

except KeyboardInterrupt:
print('Server stopped')
finally:
epoll.unregister(server_socket.fileno())
epoll.close()
server_socket.close()

client.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import socket
import threading


class Client:
HOST = 'localhost'
PORT = 8000
EOL = b'\n\n'

def __init__(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.HOST, self.PORT))

def send(self, data):
self.socket.sendall(data + self.EOL)

def handle_receive(self):
try:
while True:
data = self.socket.recv(1024)
if not data:
break
print(data.decode())
finally:
self.socket.close()

def start(self):
threading.Thread(target=self.handle_receive).start()
while True:
data = input('Enter message: ')
self.send(data.encode())


if __name__ == '__main__':
client = Client()
client.start()

说明

说明:

该示例代码实现了一个简单的聊天室样例,服务器可以同时支持多个客户端连接,一个客户端发送消息,服务器会将消息转发到其他客户端并回显消息。

在服务端中,使用epoll进行I/O多路复用。当有新的客户端连接请求时,服务端会接受连接,并注册新的epoll事件。当有已连接客户端发来消息时,服务端会将消息转发给其他客户端并回显消息。客户端代码中,创建一个Client对象,通过send方法发送消息,同时开启一个线程监听服务端发来的消息。当接受到服务端发来的消息后,打印出消息内容。

需要注意的是,该示例代码只是提供了一个简单的例子,实际应用中还需要考虑线程安全、异常处理等情况。

演示操作:

该示例代码需要在两台或多台计算机上运行,下面演示如何在本地环境中运行该示例代码:

  1. 打开两个终端窗口分别作为客户端和服务端,进入到示例代码所在的目录。
  2. 在服务端窗口中执行 python server.py 命令启动服务端。
  3. 在客户端窗口中执行 python client.py 命令启动客户端。

启动客户端后,每次输入消息并按回车键发送消息,服务端会将该消息转发给其他已连接的客户端,并在客户端窗口打印出服务端转发回来的消息。可以同时在多个客户端窗口中开启客户端,并模拟多个客户端发送消息。

lab3:libevent在linux

  1. 使用libevent在linux(或windows)下实现一个WEB服务器,支持简单的
    网页和图片的请求(C/C++);
  2. 使用springboot实现一个支持RESTful接口的WEB应用服务器(java);
  3. 使用django实现一个支持RESTful接口的WEB应用服务器(python);
    要求:从浏览器发送消息,可以回显示消息和图片;

当使用makefile编译链接完成之后,即可得到server可执行文件
在终端下输入命令 ./server “自己定义一个端口号” “自己设定的根目录” 即可启动服务器.

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
src = $(wildcard ./*.c)  
obj = $(patsubst ./%.c, ./%.o, $(src))

myArgs= -Wall -g
target=server
CC=gcc

ALL:$(target)

$(target):$(obj)
$(CC) $^ -o $@ $(myArgs) -levent

$(obj):%.o:%.c
$(CC) -c $^ -o $@ $(myArgs) -levent

clean:
-rm -rf $(obj) $(target)

.PHONY: clean ALL

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <event2/event.h>
#include "libevent_http.h"

int main(int argc, char **argv)
{
if(argc < 3)
{
printf("./event_http port path\n");
return -1;
}
if(chdir(argv[2]) < 0) {
printf("dir is not exists: %s\n", argv[2]);
perror("chdir err:");
return -1;
}

struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;

struct sockaddr_in sin;
base = event_base_new();
if (!base)
{
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[1]));

// 创建监听的套接字,绑定,监听,接受连接请求
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener)
{
fprintf(stderr, "Could not create a listener!\n");
return 1;
}

// 创建信号事件, 捕捉并处理
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
if (!signal_event || event_add(signal_event, NULL)<0)
{
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}

// 事件循环
event_base_dispatch(base);

evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);

printf("done\n");

return 0;
}

libevent_http.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#include <time.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include "libevent_http.h"

#define _HTTP_CLOSE_ "Connection: close\r\n"

int response_http(struct bufferevent *bev, const char *method, char *path)
{
if(strcasecmp("GET", method) == 0){
//get method ...
strdecode(path, path);
char *pf = &path[1];

if(strcmp(path, "/") == 0 || strcmp(path, "/.") == 0)
{
pf="./";
}

printf("***** http Request Resource Path = %s, pf = %s\n", path, pf);

struct stat sb;
if(stat(pf,&sb) < 0)
{
perror("open file err:");
send_error(bev);
return -1;
}

if(S_ISDIR(sb.st_mode))//澶勭悊鐩綍
{
//搴旇鏄剧ず鐩綍鍒楄〃
send_header(bev, 200, "OK", get_file_type(".html"), -1);
send_dir(bev, pf);
}
else //澶勭悊鏂囦欢
{
send_header(bev, 200, "OK", get_file_type(pf), sb.st_size);
send_file_to_http(pf, bev);
}
}

return 0;
}

/*
*charset=iso-8859-1 瑗挎鐨勭紪鐮侊紝璇存槑缃戠珯閲囩敤鐨勭紪鐮佹槸鑻辨枃锛?
*charset=gb2312 璇存槑缃戠珯閲囩敤鐨勭紪鐮佹槸绠€浣撲腑鏂囷紱
*charset=utf-8 浠h〃涓栫晫閫氱敤鐨勮瑷€缂栫爜锛?
* 鍙互鐢ㄥ埌涓枃銆侀煩鏂囥€佹棩鏂囩瓑涓栫晫涓婃墍鏈夎瑷€缂栫爜涓?
*charset=euc-kr 璇存槑缃戠珯閲囩敤鐨勭紪鐮佹槸闊╂枃锛?
*charset=big5 璇存槑缃戠珯閲囩敤鐨勭紪鐮佹槸绻佷綋涓枃锛?
*
*浠ヤ笅鏄緷鎹紶閫掕繘鏉ョ殑鏂囦欢鍚嶏紝浣跨敤鍚庣紑鍒ゆ柇鏄綍绉嶆枃浠剁被鍨?
*灏嗗搴旂殑鏂囦欢绫诲瀷鎸夌収http瀹氫箟鐨勫叧閿瓧鍙戦€佸洖鍘?
*/
const char *get_file_type(char *name)
{
char* dot;

dot = strrchr(name, '.'); //鑷彸鍚戝乏鏌ユ壘鈥?鈥欏瓧绗?濡備笉瀛樺湪杩斿洖NULL

if (dot == (char*)0)
return "text/plain; charset=utf-8";
if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
return "text/html; charset=utf-8";
if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
return "image/jpeg";
if (strcmp(dot, ".gif") == 0)
return "image/gif";
if (strcmp(dot, ".png") == 0)
return "image/png";
if (strcmp(dot, ".css") == 0)
return "text/css";
if (strcmp(dot, ".au") == 0)
return "audio/basic";
if (strcmp( dot, ".wav") == 0)
return "audio/wav";
if (strcmp(dot, ".avi") == 0)
return "video/x-msvideo";
if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
return "video/quicktime";
if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
return "video/mpeg";
if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
return "model/vrml";
if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
return "audio/midi";
if (strcmp(dot, ".mp3") == 0)
return "audio/mpeg";
if (strcmp(dot, ".ogg") == 0)
return "application/ogg";
if (strcmp(dot, ".pac") == 0)
return "application/x-ns-proxy-autoconfig";

return "text/plain; charset=utf-8";
}

int send_file_to_http(const char *filename, struct bufferevent *bev)
{
int fd = open(filename, O_RDONLY);
int ret = 0;
char buf[4096] = {0};

while((ret = read(fd, buf, sizeof(buf)) ) )
{
bufferevent_write(bev, buf, ret);
memset(buf, 0, ret);
}
close(fd);
return 0;
}

int send_header(struct bufferevent *bev, int no, const char* desp, const char *type, long len)
{
char buf[256]={0};

sprintf(buf, "HTTP/1.1 %d %s\r\n", no, desp);
//HTTP/1.1 200 OK\r\n
bufferevent_write(bev, buf, strlen(buf));
// 鏂囦欢绫诲瀷
sprintf(buf, "Content-Type:%s\r\n", type);
bufferevent_write(bev, buf, strlen(buf));
// 鏂囦欢澶у皬
sprintf(buf, "Content-Length:%ld\r\n", len);
bufferevent_write(bev, buf, strlen(buf));
// Connection: close
bufferevent_write(bev, _HTTP_CLOSE_, strlen(_HTTP_CLOSE_));
//send \r\n
bufferevent_write(bev, "\r\n", 2);

return 0;
}

int send_error(struct bufferevent *bev)
{
send_header(bev,404, "File Not Found", "text/html", -1);
send_file_to_http("404.html", bev);
return 0;
}

int send_dir(struct bufferevent *bev,const char *dirname)
{
char encoded_name[1024];
char path[1024];
char timestr[64];
struct stat sb;
struct dirent **dirinfo;
int i;

char buf[4096] = {0};
sprintf(buf, "<html><head><meta charset=\"utf-8\"><title>%s</title></head>", dirname);
sprintf(buf+strlen(buf), "<body><h1>The current directory: %s</h1><table>", dirname);
//娣诲姞鐩綍鍐呭
int num = scandir(dirname, &dirinfo, NULL, alphasort);
for(i=0; i<num; ++i)
{
// 缂栫爜
strencode(encoded_name, sizeof(encoded_name), dirinfo[i]->d_name);

sprintf(path, "%s%s", dirname, dirinfo[i]->d_name);
printf("############# path = %s\n", path);
if (lstat(path, &sb) < 0)
{
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s\">%s</a></td></tr>\n",
encoded_name, dirinfo[i]->d_name);
}
else
{
strftime(timestr, sizeof(timestr),
" %d %b %Y %H:%M", localtime(&sb.st_mtime));
if(S_ISDIR(sb.st_mode))
{
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s/\">%s/</a></td><td>%s</td><td>%ld</td></tr>\n",
encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);
}
else
{
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%ld</td></tr>\n",
encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);
}
}
bufferevent_write(bev, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
}
sprintf(buf+strlen(buf), "</table></body></html>");
bufferevent_write(bev, buf, strlen(buf));
printf("################# Dir Read OK !!!!!!!!!!!!!!\n");

return 0;
}

void conn_readcb(struct bufferevent *bev, void *user_data)
{
printf("******************** begin call %s.........\n",__FUNCTION__);
char buf[4096]={0};
char method[50], path[4096], protocol[32];
bufferevent_read(bev, buf, sizeof(buf));
printf("buf[%s]\n", buf);
sscanf(buf, "%[^ ] %[^ ] %[^ \r\n]", method, path, protocol);
printf("method[%s], path[%s], protocol[%s]\n", method, path, protocol);
if(strcasecmp(method, "GET") == 0)
{
response_http(bev, method, path);
}
printf("******************** end call %s.........\n", __FUNCTION__);
}

void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
printf("******************** begin call %s.........\n", __FUNCTION__);
if (events & BEV_EVENT_EOF)
{
printf("Connection closed.\n");
}
else if (events & BEV_EVENT_ERROR)
{
printf("Got an error on the connection: %s\n",
strerror(errno));
}

bufferevent_free(bev);
printf("******************** end call %s.........\n", __FUNCTION__);
}

void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { 1, 0 };

printf("Caught an interrupt signal; exiting cleanly in one seconds.\n");
event_base_loopexit(base, &delay);
}

void listener_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data)
{
printf("******************** begin call-------%s\n",__FUNCTION__);
struct event_base *base = user_data;
struct bufferevent *bev;
printf("fd is %d\n",fd);
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev)
{
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_NORMAL);
bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, NULL);
bufferevent_enable(bev, EV_READ | EV_WRITE);

printf("******************** end call-------%s\n",__FUNCTION__);
}

/*
* 杩欓噷鐨勫唴瀹规槸澶勭悊%20涔嬬被鐨勪笢瑗匡紒鏄?瑙g爜"杩囩▼銆?
* %20 URL缂栫爜涓殑鈥?鈥?space)
* %21 '!' %22 '"' %23 '#' %24 '$'
* %25 '%' %26 '&' %27 ''' %28 '('......
* 鐩稿叧鐭ヨ瘑html涓殑鈥?鈥?space)鏄?nbsp
*/
void strdecode(char *to, char *from)
{
for ( ; *from != '\0'; ++to, ++from)
{
if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
{
// 渚濇鍒ゆ柇from涓?%20 涓変釜瀛楃
*to = hexit(from[1])*16 + hexit(from[2]);
// 绉昏繃宸茬粡澶勭悊鐨勪袱涓瓧绗?%21鎸囬拡鎸囧悜1),琛ㄨ揪寮?鐨?+from杩樹細鍐嶅悜鍚庣Щ涓€涓瓧绗?
from += 2;
}
else
{
*to = *from;
}
}
*to = '\0';
}

//16杩涘埗鏁拌浆鍖栦负10杩涘埗, return 0涓嶄細鍑虹幇
int hexit(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;

return 0;
}

// "缂栫爜"锛岀敤浣滃洖鍐欐祻瑙堝櫒鐨勬椂鍊欙紝灏嗛櫎瀛楁瘝鏁板瓧鍙?_.-~浠ュ鐨勫瓧绗﹁浆涔夊悗鍥炲啓銆?
// strencode(encoded_name, sizeof(encoded_name), name);
void strencode(char* to, size_t tosize, const char* from)
{
int tolen;

for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from)
{
if (isalnum(*from) || strchr("/_.-~", *from) != (char*)0)
{
*to = *from;
++to;
++tolen;
}
else
{
sprintf(to, "%%%02x", (int) *from & 0xff);
to += 3;
tolen += 3;
}
}
*to = '\0';
}

libevent.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef _LIBEVENT_HTTP_H
#define _LIBEVENT_HTTP_H

#include <event2/event.h>

void conn_eventcb(struct bufferevent *bev, short events, void *user_data);

void conn_readcb(struct bufferevent *bev, void *user_data);

const char *get_file_type(char *name);

int hexit(char c);

void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data);

int response_http(struct bufferevent *bev, const char *method, char *path);

int send_dir(struct bufferevent *bev,const char *dirname);

int send_error(struct bufferevent *bev);

int send_file_to_http(const char *filename, struct bufferevent *bev);

int send_header(struct bufferevent *bev, int no, const char* desp, const char *type, long len);

void signal_cb(evutil_socket_t sig, short events, void *user_data);

void strdecode(char *to, char *from);

void strencode(char* to, size_t tosize, const char* from);

#endif

示例操作

1
make  # lab3文件夹里