来源,华清远见嵌入式学院实验手册,代码来源:华清远见曾宏安
实现的功能:
编写TCP文件服务器和客户端。客户端可以上传和下载文件
客户端支持功能如下:
1.支持一下命令
help 显示客户端所有命令和说明
list 显示服务器端可以下载的文件列表
get <filename> 下载文件
put <filename> 上传文件
quit 退出客户端
服务器端功能(单进程)
解析客户端的命令并提供相应的服务
服务器流程:
服务器端的代码:
1: #include <stdio.h>
2: #include <stdlib.h>
3: #include <unistd.h>
4: #include <string.h>
5: #include <fcntl.h>
6: #include <dirent.h>
7: #include <sys/socket.h>
8: #include <netinet/in.h>
9: #include <arpa/inet.h>
10:
11: #define N 256
12:
13: typedef struct sockaddr SA; //
14:
15: void do_list(int connfd)
16: {
17: DIR *mydir;
18: struct dirent *dp;
19:
20: if ((mydir = opendir(".")) == NULL) //打开当前目录
21: {
22: perror("fail to opendir");
23: return;
24: }
25: while ((dp = readdir(mydir)) != NULL) //读目录,每次返回一项, 读完时,返回空
26: {
27: if (dp->d_name[0] != '.') //将.和..以及隐藏文件跳过
28: {
29: send(connfd, dp->d_name, N, 0); //为了便于客户端将每次发送的目录项区分开,服务器每次发N个,相应的客户端也受N个
30: }
31: }
32: closedir(mydir); //关闭目录
33: }
34:
35: void do_get(int connfd, char fname[]) //下载请求处理函数
36: {
37: int fd, nbyte;
38: char buf[N];
39:
40: if ((fd = open(fname, O_RDONLY)) < 0) //以只读方式打开,假如不存在,报错
41: {
42: perror("fail to open");
43: send(connfd, "N", 1, 0); //向客户端发送‘N',表示文件不存在,发送1个,客户端也收1个
44: return; 退出下载请求处理函数,注:服务器不能退出
45: }
46: send(connfd, "Y", 1, 0); //向客户端发送Y,表示文件存在
47: while ((nbyte = read(fd, buf, N)) > 0) //读文件,读完了返回空
48: {
49: send(connfd, buf, nbyte, 0); //发送文件原则:读多少,就发多少!
50: }
51: close(fd);
52: }
53:
54: void do_put(int connfd, char fname[]) //上传请求处理函数
55: {
56: int fd, nbyte;
57: char buf[N];
58:
59: if ((fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) //不存在创建,存在报错
60: {
61: perror("fail to open");
62: send(connfd, "N", 1, 0); //向客户端发送‘N',表示文件已存在,发送1个,客户端也收1个
63: return;
64: }
65: send(connfd, "Y", 1, 0);//向客户端发送‘N',表示文件不存在,可以上传。发送1个,客户端也收1个
66: while ((nbyte = recv(connfd, buf, N, 0)) > 0)
67: {
68: write(fd, buf, nbyte); //写文件原则:接收多少,就写多少
69: }
70: close(fd);
71: }
72:
73: int main(int argc, char *argv[])
74: {
75: int listenfd, connfd;
76: char command[N];
77: struct sockaddr_in myaddr, peeraddr;
78: socklen_t peerlen = sizeof(peeraddr);
79:
80: if (argc < 3)
81: {
82: printf("Usage : %s <ip> <port>\n", argv[0]);
83: return -1;
84: }
85:
86: // XXX int socket(int domain, int type, int protocol);
87: if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
88: {
89: perror("fail to socket");
90: exit(-1);
91: }
92:
93: bzero(&myaddr, sizeof(myaddr));
94: myaddr.sin_family = PF_INET;
95: myaddr.sin_port = htons(atoi(argv[2]));
96: myaddr.sin_addr.s_addr = inet_addr(argv[1]);
97: // XXX int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
98: if (bind(listenfd, (SA *)&myaddr, sizeof(myaddr)) < 0)
99: {
100: perror("fail to bind");
101: exit(-1);
102: }
103:
104: if (listen(listenfd, 5) < 0)
105: {
106: perror("fail to listen");
107: exit(-1);
108: }
109:
110: while ( 1 )
111: {
112: // XXX int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
113: if ((connfd = accept(listenfd, (SA *)&peeraddr, &peerlen)) < 0)
114: {
115: perror("fail to accept");
116: exit(-1);
117: }
118: printf("connection from [%s:%d]\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
119: recv(connfd, command, N, 0); // recv command from client,简单处理,客户端发N个,服务器也应该收N个
120: switch ( command[0] ) //因为客户端发来的命令第一个字符就是命令类型
121: {
122: case 'L': //客户端的列表请求
123: printf("do_list\n");
124: do_list(connfd);
125: break;
126: case 'G': //客户端的下载请求
127: do_get(connfd, command+1);
128: break;
129: case 'P': //客户端的上传请求
130: do_put(connfd, command+1);
131: break;
132: }
133: close(connfd); //关闭本次的连接套接字
134: }
135:
136: return 0;
137: }
客户端流程:
客户端的代码:
1: #include <stdio.h>
2: #include <stdlib.h>
3: #include <unistd.h>
4: #include <string.h>
5: #include <fcntl.h>
6: #include <sys/socket.h>
7: #include <netinet/in.h>
8: #include <arpa/inet.h>
9:
10: #define N 256
11:
12: typedef struct sockaddr SA;
13:
14: void do_help()
15: {
16: printf(" help : display help info\n");
17: printf(" list : get file list from server\n");
18: printf("get <file> : download <file> from server\n");
19: printf("put <file> : upload <file> to server\n");
20: printf(" quit : exit\n");
21: }
22:
23: void do_list(struct sockaddr_in servaddr) //list命令处理函数
24: {
25: int sockfd;
26: char buf[N];
27:
28: if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
29: {
30: perror("fail to socket");
31: exit(-1);
32: }
33:
34: // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
35: if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
36: {
37: perror("fail to connect");
38: exit(-1);
39: }
40:
41: buf[0] = 'L'; //向服务器发送字符'L',表示list命令
42: send(sockfd, buf, N, 0); // send command to server,发送字节数N最好与服务器那边对应起来,发多少,收多少
43: while (recv(sockfd, buf, N, 0) > 0) //简单处理,服务器每次发N个,那么客户端每次也应该收N个,这样就把每个目录项区分开了
44: {
45: printf(" %s\n", buf);
46: }
47: close(sockfd); //关闭套接字
48: }
49:
50: void do_get(struct sockaddr_in servaddr, char fname[]) //下载处理函数
51: {
52: int sockfd, fd, nbyte;
53: char buf[N];
54:
55: if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
56: {
57: perror("fail to socket");
58: exit(-1);
59: }
60:
61: // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
62: if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
63: {
64: perror("fail to connect");
65: exit(-1);
66: }
67:
68: sprintf(buf, "G%s", fname); //将上传标识符'G'和文件名格式化输出到缓冲区buf中,想法很好!
69: send(sockfd, buf, N, 0); // send command to server //将客户端的上传命令连同文件名一块发给服务器。
70: //等待服务器的确认回复,因为要下载的文件名可能在服务器上不存在,服务器发送了1个,所以客户端也应该接收至少1个。
71: recv(sockfd, buf, 1, 0); // recv reply from server
72: if (buf[0] == 'N') //服务器返回N,说明服务器上没有客户端要下载的文件
73: {
74: printf("can't open %s on server\n", fname);
75: close(sockfd); //关闭套接字
76: return; //退出下载处理函数
77: }
78: if ((fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) //不存在创建,存在清除
79: {
80: perror("fail to open");
81: close(sockfd);
82: return;
83: }
84: while ((nbyte = recv(sockfd, buf, N, 0)) > 0) //接收服务器发送的文件内容
85: {
86: write(fd, buf, nbyte); //写文件原则:收多少,就写到少!
87: }
88: close(fd); //关闭文件描述符
89: close(sockfd);
90: }
91:
92: void do_put(struct sockaddr_in servaddr, char fname[]) //上传处理函数
93: {
94: int sockfd, fd, nbyte;
95: char buf[N];
96:
97: if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
98: {
99: perror("fail to socket");
100: exit(-1);
101: }
102:
103: // XXX int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
104: if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0)
105: {
106: perror("fail to connect");
107: exit(-1);
108: }
109:
110: sprintf(buf, "P%s", fname);//将下载标识符'P'和文件名格式化输出到缓冲区buf中,想法很好!
111: send(sockfd, buf, N, 0); // send command to server//将客户端的下载命令连同文件名一块发给服务器。
112: //等待服务器的确认回复,因为要上传的文件名可能在服务器上已经存在。服务器发送了1个,所以客户端也应该接收至少1个。
113: recv(sockfd, buf, 1, 0); // recv reply from server
114: if (buf[0] == 'N') //服务器返回N,说明服务器上有客户端要上传的文件
115: {
116: printf("%s exsit on server,\n", fname);
117: close(sockfd);
118: return;
119: }
120: if ((fd = open(fname, O_RDONLY)) < 0)
121: {
122: perror("fail to open");
123: close(sockfd);
124: return;
125: }
126: while ((nbyte = read(fd, buf, N)) > 0)
127: {
128: send(sockfd, buf, nbyte,0); //发送文件原则:从文件流中读多少,就向服务器发多少!
129: }
130: close(fd);
131: close(sockfd);
132: }
133:
134: int main(int argc, char *argv[])
135: {
136: int sockfd;
137: char command[N];
138: struct sockaddr_in servaddr;
139:
140: if (argc < 3)
141: {
142: printf("Usage : %s <ip> <port>\n", argv[0]);
143: return -1;
144: }
145:
146: bzero(&servaddr, sizeof(servaddr));
147: servaddr.sin_family = PF_INET;
148: servaddr.sin_port = htons(atoi(argv[2]));
149: servaddr.sin_addr.s_addr = inet_addr(argv[1]);
150:
151: #if 0
152: // XXX int socket(int domain, int type, int protocol);
153: #endif
154: while ( 1 )
155: {
156: printf("client > ");
157: fgets(command, N, stdin);
158: command[strlen(command)-1] = '\0'; //将'\0'前面的'\n'用'\0'覆盖。
159: if (strncmp(command, "help", 4) == 0)
160: {
161: do_help();
162: }
163: else if (strncmp(command, "list", 4) == 0)
164: {
165: do_list(servaddr);
166: }
167: else if (strncmp(command, "get", 3) == 0)
168: {
169: do_get(servaddr, command+4);
170: }
171: else if (strncmp(command, "put", 3) == 0)
172: {
173: do_put(servaddr, command+4);
174: }
175: else if (strncmp(command, "quit", 4) == 0)
176: {
177: printf("bye\n");
178: exit(0);
179: }
180: else
181: {
182: printf(" invalid command %s\n", command);
183: do_help();
184: }
185: }
186:
187: return 0;
188: }