来源,华清远见嵌入式学院实验手册,代码来源:华清远见曾宏安
实现的功能:
编写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: }