完成了服務端的基本功能,把客戶端也完善了一些,服務端功能實現:
開發環境: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;