程序需求:
- 程序分爲server程序和client程序,通訊由client主動發起請求一個指定的文件,由server傳文件給client。
- 一個server可以支持多個client通信。
- 數據包要有校驗機制。
- 有丟包重傳機制。
- 支持斷點續傳。
- 能正常處理各種異常現象。
實現過程
服務器
一、制定UDP通信協議
設計一個UDP報頭
type |
len |
id |
check |
data |
1、type(數據類型):char
給出這個UDP包的數據類型及服務器的處理方式。我現在把它分爲不同的幾個值:文件傳輸申請,客戶端收到一個數據包後的確認(重傳、繼續)
斷點續傳(掉線後重新建立連接並接着以前的傳輸),文件件傳輸結束(服務器到客戶端),文件傳輸錯誤,等。
2、len(數據包長度):int
3、check(校驗和):int
對包進行CRC校驗和,防止數據傳輸過程中錯誤。
4、id(標識):long int
對文件傳輸時對數據包進行標號,在斷點續傳時標識文件斷點位置,等(根據type需求改變)
二、服務器事件處理機制
一開始我準備使用一個鏈表來接收保存客戶端的請求、再用一個鏈表隊列來處理相應的請求(另一個處理線程)、還有一個超時處理隊列。最後發現變得很複雜(對鏈表操作不是很熟練)。決定只用一個全局數組來代替上面三個鏈表的功能。
開兩個線程一個用於循環接收客戶端發來的數據,並進行分類標記放入數組中,另一個線程用於處理數組中待處理的事件。(一個客戶端一個數組元素)
每個客戶端分配一個結構體並放入數組中。
客戶 結構體:flag (標識)、clinet_addr(客戶端地址)、timer(時間)、struct file_info(文件信息)、等
flag:標記客戶端狀態(等待傳輸、服務器等待反映、已經超時、空客戶端等)
timer:記錄客戶端最後反映的時刻、用於判斷超時。
file_info:(文件路徑,文件指針,文件偏移量)記錄文件傳輸情況。
主線程中循環判斷是否有客戶端連接超時。
客戶端
向服務器申請文件(先判斷是否斷點),當斷點重傳時,用fseek函數找到斷點,並把斷點信息發給服務器端,服務器重斷點處重新讀取文件開始傳輸。發出請求後等待服務器答覆,服務器答覆後開始計數接受文件包。服務器給每個包編上編號,客戶端每接收一個udp包放到緩衝區中,當緩衝區慢後就按包編號順序寫到文件中記錄編號。當沒有要寫的編號時,給服務器發送重發這個包ACK。
頭文件
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <strings.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
/*udp_datapack->type*/
#define REQ_FILE 0
#define ACK_TRUE 1
#define ACK_FAIL 2
#define ACK_RECONNECT 3
#define FILE_DATE 4
/*udp_req->flag udp類型*/
#define CLINET_EMPTY 0
#define CLINET_FIRST_REQ 1
#define CLINET_TIME_OUT 2
#define CLINET_LINK_ACK_WAIT 3
#define CLINET_LINK_ACK_TRUE 4
#define CLINET_LINK_ACK_FAIL 5
/*send_ack flag. 當udp爲ACK時 udp頭中label的值*/
/*udp_datapack->type=ACK_TRUE | ACK_FAIL ===> udp_datapack->label */
#define SERVER_CLINET_FULL 0
#define FIND_FILE 1
#define UNFIND_FILE 2
#define FILE_OVER 3
#define TYPE_UNRECOGNIZED 4
#define CRC_FAIL 5
#define FILE_READ_FAIL 6
/*clinet file flag(file_pack_num) */
#define FILE_COMPLETE -1
#define FILE_INCOMPLETE -2
#define SERVER_BUSY -3
#define FILE_NOT_EXIST -4
#define FILE_SERV_READ_FAIL -5
#define RE_APPLY -6
#define SERV_PORT 8888 /*端口號*/
#define MAX 1024 /*udp數據部分大小*/
#define MAX_UDP_CLINET 10 /*服務器處理客戶端最大量*/
#define UDP_HEAD_LEN 13 /*udp頭大小*/
#define TIMEVAL 45 /*超時時間*//*udp包格式定義*/
struct udp_datapack{
char type; /*數據包類型*/
long int label; /*文件包號、請求包文件位置信息*/
int size; /*包大小*/
int check; /*CRC*/
char data[MAX];
};
/*傳輸的文件信息*/
struct file_infor{
char *file_path;
FILE * file_fp;
off_t seek_flag;
};
/*服務器保存的客戶端信息*/
struct udp_req{
char flag; /*客戶端狀態*/
int fsend_label; /*跟據flag變化*/
struct sockaddr_in req_addr; /*客戶端地址*/
struct file_infor req_file; /*客戶所請求的文件信息*/
struct timeval timeout; /*超時計時*/
};
/*本地socket信息*/
struct local_infor{
int sockfd;
struct sockaddr_in local_addr;
};