了解網(wǎng)絡(luò)傳輸協(xié)議的人都知道,采用TCP實(shí)現(xiàn)文件傳輸很簡(jiǎn)單。相對(duì)于TCP,由于UDP是面向無(wú)連接、不可靠的傳輸協(xié)議,所以我們需要考慮丟包和后發(fā)先至(包的順序)的問(wèn)題,所以我們想要實(shí)現(xiàn)UDP傳輸文件,則需要解決這兩個(gè)問(wèn)題。方法就是給數(shù)據(jù)包編號(hào),按照包的順序接收并存儲(chǔ),接收端接收到數(shù)據(jù)包后發(fā)送確認(rèn)信息給發(fā)送端,發(fā)送端接收確認(rèn)數(shù)據(jù)以后再繼續(xù)發(fā)送下一個(gè)包,如果接收端收到的數(shù)據(jù)包的編號(hào)不是期望的編號(hào),則要求發(fā)送端重新發(fā)送。
下面展示的是基于linux下C語(yǔ)言實(shí)現(xiàn)的一個(gè)示例程序,該程序定義一個(gè)包的結(jié)構(gòu)體,其中包含數(shù)據(jù)和包頭,包頭里包含有包的編號(hào)和數(shù)據(jù)大小,經(jīng)過(guò)測(cè)試后,該程序可以成功傳輸一個(gè)視頻文件。
具體實(shí)現(xiàn)代碼如下:
server端代碼如下:
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
|
/************************************************************************* > File Name: server.c > Author: SongLee ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h> #define SERVER_PORT 8000 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 /* 包頭 */ typedef struct { int id; int buf_size; }PackInfo; /* 接收包 */ struct SendPack { PackInfo head; char buf[BUFFER_SIZE]; } data; int main() { /* 發(fā)送id */ int send_id = 0; /* 接收id */ int receive_id = 0; /* 創(chuàng)建UDP套接口 */ struct sockaddr_in server_addr; bzero(&server_addr, sizeof (server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); /* 創(chuàng)建socket */ int server_socket_fd = socket(AF_INET, SOCK_DGRAM, 0); if (server_socket_fd == -1) { perror ( "Create Socket Failed:" ); exit (1); } /* 綁定套接口 */ if (-1 == (bind(server_socket_fd,( struct sockaddr*)&server_addr, sizeof (server_addr)))) { perror ( "Server Bind Failed:" ); exit (1); } /* 數(shù)據(jù)傳輸 */ while (1) { /* 定義一個(gè)地址,用于捕獲客戶端地址 */ struct sockaddr_in client_addr; socklen_t client_addr_length = sizeof (client_addr); /* 接收數(shù)據(jù) */ char buffer[BUFFER_SIZE]; bzero(buffer, BUFFER_SIZE); if (recvfrom(server_socket_fd, buffer, BUFFER_SIZE,0,( struct sockaddr*)&client_addr, &client_addr_length) == -1) { perror ( "Receive Data Failed:" ); exit (1); } /* 從buffer中拷貝出file_name */ char file_name[FILE_NAME_MAX_SIZE+1]; bzero(file_name,FILE_NAME_MAX_SIZE+1); strncpy (file_name, buffer, strlen (buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE: strlen (buffer)); printf ( "%s\n" , file_name); /* 打開(kāi)文件 */ FILE *fp = fopen (file_name, "r" ); if (NULL == fp) { printf ( "File:%s Not Found.\n" , file_name); } else { int len = 0; /* 每讀取一段數(shù)據(jù),便將其發(fā)給客戶端 */ while (1) { PackInfo pack_info; if (receive_id == send_id) { ++send_id; if ((len = fread (data.buf, sizeof ( char ), BUFFER_SIZE, fp)) > 0) { data.head.id = send_id; /* 發(fā)送id放進(jìn)包頭,用于標(biāo)記順序 */ data.head.buf_size = len; /* 記錄數(shù)據(jù)長(zhǎng)度 */ if (sendto(server_socket_fd, ( char *)&data, sizeof (data), 0, ( struct sockaddr*)&client_addr, client_addr_length) < 0) { perror ( "Send File Failed:" ); break ; } /* 接收確認(rèn)消息 */ recvfrom(server_socket_fd, ( char *)&pack_info, sizeof (pack_info), 0, ( struct sockaddr*)&client_addr, &client_addr_length); receive_id = pack_info.id; } else { break ; } } else { /* 如果接收的id和發(fā)送的id不相同,重新發(fā)送 */ if (sendto(server_socket_fd, ( char *)&data, sizeof (data), 0, ( struct sockaddr*)&client_addr, client_addr_length) < 0) { perror ( "Send File Failed:" ); break ; } /* 接收確認(rèn)消息 */ recvfrom(server_socket_fd, ( char *)&pack_info, sizeof (pack_info), 0, ( struct sockaddr*)&client_addr, &client_addr_length); receive_id = pack_info.id; } } /* 關(guān)閉文件 */ fclose (fp); printf ( "File:%s Transfer Successful!\n" , file_name); } } close(server_socket_fd); return 0; } |
client端代碼如下:
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
|
/************************************************************************* > File Name: client.c > Author: SongLee ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h> #define SERVER_PORT 8000 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512 /* 包頭 */ typedef struct { int id; int buf_size; }PackInfo; /* 接收包 */ struct RecvPack { PackInfo head; char buf[BUFFER_SIZE]; } data; int main() { int id = 1; /* 服務(wù)端地址 */ struct sockaddr_in server_addr; bzero(&server_addr, sizeof (server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr( "127.0.0.1" ); server_addr.sin_port = htons(SERVER_PORT); socklen_t server_addr_length = sizeof (server_addr); /* 創(chuàng)建socket */ int client_socket_fd = socket(AF_INET, SOCK_DGRAM, 0); if (client_socket_fd < 0) { perror ( "Create Socket Failed:" ); exit (1); } /* 輸入文件名到緩沖區(qū) */ char file_name[FILE_NAME_MAX_SIZE+1]; bzero(file_name, FILE_NAME_MAX_SIZE+1); printf ( "Please Input File Name On Server: " ); scanf ( "%s" , file_name); char buffer[BUFFER_SIZE]; bzero(buffer, BUFFER_SIZE); strncpy (buffer, file_name, strlen (file_name)>BUFFER_SIZE?BUFFER_SIZE: strlen (file_name)); /* 發(fā)送文件名 */ if (sendto(client_socket_fd, buffer, BUFFER_SIZE,0,( struct sockaddr*)&server_addr,server_addr_length) < 0) { perror ( "Send File Name Failed:" ); exit (1); } /* 打開(kāi)文件,準(zhǔn)備寫入 */ FILE *fp = fopen (file_name, "w" ); if (NULL == fp) { printf ( "File:\t%s Can Not Open To Write\n" , file_name); exit (1); } /* 從服務(wù)器接收數(shù)據(jù),并寫入文件 */ int len = 0; while (1) { PackInfo pack_info; if ((len = recvfrom(client_socket_fd, ( char *)&data, sizeof (data), 0, ( struct sockaddr*)&server_addr,&server_addr_length)) > 0) { if (data.head.id == id) { pack_info.id = data.head.id; pack_info.buf_size = data.head.buf_size; ++id; /* 發(fā)送數(shù)據(jù)包確認(rèn)信息 */ if (sendto(client_socket_fd, ( char *)&pack_info, sizeof (pack_info), 0, ( struct sockaddr*)&server_addr, server_addr_length) < 0) { printf ( "Send confirm information failed!" ); } /* 寫入文件 */ if ( fwrite (data.buf, sizeof ( char ), data.head.buf_size, fp) < data.head.buf_size) { printf ( "File:\t%s Write Failed\n" , file_name); break ; } } else if (data.head.id < id) /* 如果是重發(fā)的包 */ { pack_info.id = data.head.id; pack_info.buf_size = data.head.buf_size; /* 重發(fā)數(shù)據(jù)包確認(rèn)信息 */ if (sendto(client_socket_fd, ( char *)&pack_info, sizeof (pack_info), 0, ( struct sockaddr*)&server_addr, server_addr_length) < 0) { printf ( "Send confirm information failed!" ); } } else { } } else { break ; } } printf ( "Receive File:\t%s From Server IP Successful!\n" , file_name); fclose (fp); close(client_socket_fd); return 0; } |
感興趣的朋友可以動(dòng)手測(cè)試一下該程序,相信會(huì)對(duì)大家的Linux下C語(yǔ)言網(wǎng)絡(luò)編程帶來(lái)一定的幫助。