完成了服務端的基本功能,把客戶端也完善了一些,服務端功能實現:
開發環境:Delphi XE8 + superobject + DIOCP
選擇DIOCP的原因:這兩年,這個框架的作者很勤勞,這兩年一直在不斷的完善,算是Delphi這個圈子裡面比較成熟的通訊框架了。
服務端寫的還比較粗,文檔校驗、文檔版本管理這些功能都沒做,用是可以用了,但是還是要繼續完善。沒有截圖,來兩段代碼吧。
1 const 2 MSG_SERVICE_USER = 0; 3 MSG_SERVICE_FILE = 1; 4 5 const 6 MSG_CONNECTFAIL = 404; //連接失敗 7 8 const 9 MSG_USER_SUCCESSFUL = 1000; 10 MSG_USER_LOGIN = 1001; 11 MSG_USER_REGISTER = 1002; 12 13 const 14 MSG_FILE_REQ_UPLOAD = 2001; 15 MSG_FILE_READYUPLOAD = 2002; 16 MSG_FILE_UPLOADING = 2003; 17 MSG_FILE_REQ_DOWNLOAD = 2004; 18 MSG_FILE_READYDOWNLOAD = 2005; 19 MSG_FILE_DOWNLOADING = 2006; 20 MSG_FILE_GETFILESIZE = 2007; 21 MSG_FILE_DELETEFILE = 2008; 22 23 const 24 MSG_ERR_UNKNOWN = 4001; 25 MSG_ERR_INVALIDUSER = 4002; //無效用戶 26 MSG_ERR_USERNOTFOUND = 4003; //用戶不存在 27 MSG_ERR_WRONGPASSWORD = 4004; //密碼錯誤 28 MSG_ERR_USEREXIST = 4005; //用戶名已經存在(注冊新用戶業務) 29 MSG_ERR_USERTOOMUCH = 4006; //用戶數量已經達到上限(注冊新用戶業務) 30 MSG_ERR_SPACEFULL = 4007; //空間已滿 31 MSG_ERR_NOTAUTHUPLOAD = 4008; //沒有上傳權限 32 MSG_ERR_NOTAUTHDOWNLOAD = 4009; //沒有下載權限 33 MSG_ERR_FILENOTFOUND = 4010; //文件不存在
1 unit snt.client.net.filetrans;
2
3 interface
4
5 uses
6 System.SysUtils, System.Classes, System.Math, diocp.coder.tcpClient,
7 utils.simpleMsgPack, snt.net.coder.stream, snt.net.msgcode;
8
9 type
10 TSNTFileTransContext = class(TObject)
11 private
12 FCoderTcpClient: TDiocpCoderTcpClient;
13 FDiocpContext: TIocpCoderRemoteContext;
14 FCMDStream: TMemoryStream;
15 FCMDMsgPack: TSimpleMsgPack;
16 procedure OnRecvObject(pvObject:TObject);
17 procedure SendCMDObject(pvCMDObject: TSimpleMsgPack);
18 function GetFileSize(const pvFile: string): Int64;
19 private
20 FFileStream: TFileStream;
21 FLocalFile, FRemoteFile: string;
22 FFileSize: Int64;
23 function upload(const pvRemoteFile, pvToken: string): Int64;
24 procedure uploadNextBlock(const pvToken: string = '');
25 function download(const pvRemoteFile: string; const pvFileSize: Int64): Int64;
26 procedure downloadNextBlock();
27 public
28 constructor Create(pvCoderTcpClient: TDiocpCoderTcpClient);
29 destructor Destroy; override;
30
31 procedure init(const pvHost: string; const pvPort: Integer);
32 procedure requestUpload(const pvLocalFile, pvToken: string);
33 procedure requestDownload(const pvLocalFile, pvToken: string);
34 end;
35
36 implementation
37
38 uses utils.safeLogger;
39
40 const
41 SEC_SIZE = 1024 * 1024;
42
43 { TSNTFileTransContext }
44
45 constructor TSNTFileTransContext.Create(pvCoderTcpClient: TDiocpCoderTcpClient);
46 begin
47 inherited Create;
48 FCoderTcpClient:= pvCoderTcpClient;
49 FCMDStream := TMemoryStream.Create;
50 FCMDMsgPack := TSimpleMsgPack.Create;
51 end;
52
53 destructor TSNTFileTransContext.Destroy;
54 begin
55 if Assigned(FFileStream) then FFileStream.Free;
56 FCMDStream.Free;
57 FCMDMsgPack.Free;
58 inherited;
59 end;
60
61 procedure TSNTFileTransContext.init(const pvHost: string; const pvPort: Integer);
62 begin
63 FDiocpContext:= TIocpCoderRemoteContext(FCoderTcpClient.Add);
64 FDiocpContext.RegisterCoderClass(TIOCPStreamDecoder, TIOCPStreamEncoder);
65 FDiocpContext.OnContextAction:= OnRecvObject;
66 if not FDiocpContext.Active then
67 begin
68 FDiocpContext.Host:= pvHost;
69 FDiocpContext.Port:= pvPort;
70 FDiocpContext.Connect;
71 end;
72 end;
73
74 procedure TSNTFileTransContext.SendCMDObject(pvCMDObject: TSimpleMsgPack);
75 var
76 lvCMDStream:TMemoryStream;
77 begin
78 lvCMDStream := TMemoryStream.Create;
79 try
80 pvCMDObject.EncodeToStream(lvCMDStream);
81 FDiocpContext.WriteObject(lvCMDStream);
82 finally
83 lvCMDStream.Free;
84 end;
85 end;
86
87 function TSNTFileTransContext.GetFileSize(const pvFile: string): Int64;
88 var
89 lvFileStream: TFileStream;
90 begin
91 lvFileStream:= TFileStream.Create(FLocalFile, fmOpenRead);
92 try
93 result:= lvFileStream.Size;
94 finally
95 lvFileStream.Free;
96 end;
97 end;
98
99 procedure TSNTFileTransContext.requestUpload(const pvLocalFile, pvToken: string);
100 var
101 lvMsgData: TSimpleMsgPack;
102 begin
103 lvMsgData:= TSimpleMsgPack.Create;
104 try
105 FLocalFile:= pvLocalFile;
106 lvMsgData.I['service.type'] := MSG_SERVICE_FILE;
107 lvMsgData.I['msg.code'] := MSG_FILE_REQ_UPLOAD;
108 lvMsgData.S['params.filename'] := ExtractFileName(pvLocalFile);
109 lvMsgData.I['params.filesize'] := GetFileSize(pvLocalFile);
110 lvMsgData.S['params.token'] := pvToken;
111 SendCMDObject(lvMsgData);
112 finally
113 FreeAndNil(lvMsgData);
114 end;
115 end;
116
117 procedure TSNTFileTransContext.requestDownload(const pvLocalFile, pvToken: string);
118 var
119 lvMsgData: TSimpleMsgPack;
120 begin
121 lvMsgData:= TSimpleMsgPack.Create;
122 try
123 FLocalFile:= pvLocalFile;
124 lvMsgData.I['service.type'] := MSG_SERVICE_FILE;
125 lvMsgData.I['msg.code'] := MSG_FILE_REQ_DOWNLOAD;
126 lvMsgData.S['params.filename'] := ExtractFileName(pvLocalFile);
127 lvMsgData.S['params.token'] := pvToken;
128 SendCMDObject(lvMsgData);
129 finally
130 FreeAndNil(lvMsgData);
131 end;
132 end;
133
134 function TSNTFileTransContext.upload(const pvRemoteFile, pvToken: string): Int64;
135 begin
136 FFileStream:= TFileStream.Create(FLocalFile, fmOpenRead);
137 FRemoteFile:= pvRemoteFile;
138 uploadNextBlock(pvToken);
139 end;
140
141 procedure TSNTFileTransContext.uploadNextBlock(const pvToken: string);
142 var
143 lvPosition, lvSize: Int64;
144 begin
145 lvPosition:= FFileStream.Position;
146 if (FFileStream.Position = FFileStream.Size) then exit;
147
148 FCMDMsgPack.clear();
149 FCMDMsgPack.I['service.type'] := MSG_SERVICE_FILE;
150 FCMDMsgPack.I['msg.code'] := MSG_FILE_UPLOADING;
151 FCMDMsgPack.S['params.filename']:= FRemoteFile;
152 FCMDMsgPack.S['params.token'] := pvToken;
153 FCMDMsgPack.I['params.start'] := lvPosition;
154 lvSize:= Min(SEC_SIZE, FFileStream.Size- FFileStream.Position);
155 FCMDMsgPack.ForcePathObject('params.data').LoadBinaryFromStream(FFileStream, lvSize);
156 if (FFileStream.Position = FFileStream.Size) then
157 FCMDMsgPack.B['params.isend'] := True;
158 //
159 FCMDStream.Clear;
160 FCMDMsgPack.EncodeToStream(FCMDStream);
161 FDiocpContext.WriteObject(FCMDStream);
162 end;
163
164
165 function TSNTFileTransContext.download(const pvRemoteFile: string;
166
167 const pvFileSize: Int64): Int64;
168
169 begin
170 FFileStream:= TFileStream.Create(FLocalFile, fmCreate or fmShareDenyWrite);
171 FRemoteFile:= pvRemoteFile;
172 FFileSize := pvFileSize;
173 downloadNextBlock();
174 end;
175
176 procedure TSNTFileTransContext.downloadNextBlock;
177 var
178 lvPosition, lvSize: Int64;
179 lvBytes: TBytes;
180 begin
181 lvBytes := FCMDMsgPack.ForcePathObject('data').AsBytes;
182 FFileStream.Write(lvBytes[0], Length(lvBytes));
183 if FFileStream.Size = FFileSize then exit;
184
185 FCMDMsgPack.clear();
186 FCMDMsgPack.I['service.type'] := MSG_SERVICE_FILE;
187 FCMDMsgPack.I['msg.code'] := MSG_FILE_DOWNLOADING;
188 FCMDMsgPack.S['params.filename'] := FRemoteFile;
189 FCMDMsgPack.I['params.start'] := FFileStream.Position;
190 FCMDStream.Clear;
191 FCMDMsgPack.EncodeToStream(FCMDStream);
192 FDiocpContext.WriteObject(FCMDStream);
193 end;
194
195 procedure TSNTFileTransContext.OnRecvObject(pvObject: TObject);
196 var
197 lvMsgData: TSimpleMsgPack;
198 begin
199 lvMsgData:= TSimpleMsgPack.Create;
200 try
201 lvMsgData.DecodeFromStream(TMemoryStream(pvObject));
202 // server error
203 if lvMsgData.I['result.code'] = -1 then
204 begin
205 sfLogger.logMessage(lvMsgData.S['result.msg']);
206 exit;
207 end;
208 sfLogger.logMessage(lvMsgData.S['msg.code']);
209 // logic
210 case lvMsgData.I['msg.code'] of
211 MSG_FILE_READYUPLOAD:
212 upload(lvMsgData.S['params.filename'],lvMsgData.S['params.token']);
213 MSG_FILE_UPLOADING:
214 uploadNextBlock();
215 MSG_FILE_READYDOWNLOAD:
216 download(lvMsgData.S['params.filename'],lvMsgData.I['params.filesize']);
217 MSG_FILE_DOWNLOADING:
218 downloadNextBlock();
219 MSG_ERR_NOTAUTHUPLOAD:
220 ;//沒有上傳權限
221 MSG_ERR_SPACEFULL:
222 ;//空間不足
223 MSG_ERR_NOTAUTHDOWNLOAD:
224 ;//沒有下載權限
225 MSG_ERR_FILENOTFOUND:
226 ;//文件不存在
227 end;
228 finally
229 lvMsgData.Free;
230 end;
231 end;