使用 Socket 通信实现 FTP 客户端程序
用Socket编程实现FTP

格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout); 参数:readfds:指向要做读检测的指针 writefds:指向要做写检测的指针 exceptfds:指向要检测是否出错的指针 timeout:最大等待时间
连接管理:
数据连接有 3 大用途: (1) 从客户向服务器发送一个文件 (2) 从服务器向客户发送一个文件 (3) 从服务器向客户发送文件或目录列表。
每一个数据连接对传输一个文件或目录序列都要建立一个新的连接。 (1) 客户发出命令要求建立数据连接 (2) 客户在客户主机上未数据连接选择一个固定的端口号 (3) 客户使用 PORT 命令从控制连接上把端口号发给服务器。 (4) 服务器在控制连接上接收端口号,并向客户端主机上的端口发出主动打开,服务器的数据连接 使用端口 21。
服务器端程序则持续的监听网络。当接受到客户端的 Socket ,服务器程序提供相应的服务。网络通 信模块使用 POP3 控件来实现客户端与服务器的信息交流。
函数功能和流程如下:(1)首先创建一个 CFtpclient 的类的实例。 (2)用 LogOnToServer()函数登录到指定的 FTP 服务器,允许非匿名用户和匿名两种登录方式,默认 的端口为 21. (3)使用 MoveFile()函数来上传下载数据文件,其中第一个参数是本地地址,第二个参数是远程地 址,文件传输选用二进制模式。注意,文件传输使用同步模式。 (4)可以使用 Ftpcommand()函数来执行 FTP 指令,包括常用的“CWD/home/mydir”来改变远程服务 器上的地址,并处理服务器返回的应答。当这种方式不适用的时候,还可以使用 WriteStr()函数和 ReadStr() 函数向远程服务器发送指令,并自己解释返回的应答。 (5)当所有的文件传输完成之后,使用 LogOffServer 函数来断开与远程服务器的连接。
python学习之路(三)使用socketserver进行ftp断点续传

python学习之路(三)使⽤socketserver进⾏ftp断点续传最近学习python到socketserver,本着想试⼀下⽔的深浅,采⽤Python3.6.⽬录结构如下:receive_file和file为下载或上传⽂件存放⽬录,ftp_client为ftp客户端,ftp_server为server端。
server端源码:#!/usr/bin/env python# -*- coding:utf-8 -*-import socketserverimport oserror_code = {'400':'FILE IS NOT EXISTS'}file_path = os.path.join(os.path.abspath('.'),'file') #获取⽂件⽬录路径'''服务端采⽤socketserver⽅式'''class MyTCPHandler(socketserver.BaseRequestHandler):def handle(self):while True:# print('new conn',self.client_address)data = self.request.recv(100) #接收客户端请求if not data.decode():breakelif data.decode().split()[0] == 'get': #server判断是下载还是上传⽂件,get是下载offset = data.decode().split('|')[1] #取出偏移量file = data.decode().split()[1].split('|')[0] #取出要下载的⽂件名filename = os.path.join(file_path,file)read_len = 0if os.path.exists(filename) : #判断是否有资源with open(filename,'rb') as fd:while True:send_data = fd.read(1024)read_len += len(send_data) #记录读取数据长度if send_data and read_len > int(offset): #达到偏移量发送数据ack_msg = "SEND SIZE|%s" % len(send_data)self.request.send(ack_msg.encode())client_ack = self.request.recv(50)if client_ack.decode() =="CLIENT_READY_TO_RECV":self.request.send(send_data)elif read_len <= int(offset):continueelse:send_data ='END'self.request.send(send_data.encode()) #数据传输完毕发送finally信号breakelse:msg = '400'self.request.send(msg.encode())elif data.decode().split()[0] == 'put': #判断客户端是不是上传⾏为file = data.decode().split()[1] #获取需要上传的⽂件名filename = os.path.join(file_path,file) #定义⽂件路径log = "%s.%s" % (file,'log') #指定记录偏移⽇志⽂件名logname = os.path.join(file_path,log) #定义⽇志路径if os.path.exists(filename) and os.path.exists(logname): #如果要上传的⽂件和⽇志⽂件同时存在,说明需要进⾏续传with open(logname) as f:offset = f.read().strip() #读取偏移量else:offset = 0 #表⽰不需要进⾏续传,直接从头开始传server_syn_msg = "offset %s" % offset #把偏移信息发送给客户端self.request.send(server_syn_msg.encode())total_len = int(offset) #获取已传输完的⽂件长度,即从这个位置开始接收新的数据while True:receive_ack = self.request.recv(100) #客户端接收到偏移信息后通知服务端要发送数据的长度信息,相当于⼀个ackres_msg = receive_ack.decode().split('|')if receive_ack.decode() == 'END': #判断⽂件是否上传完成,完成后删掉偏移⽇志os.remove(logname)breakelif res_msg[0].strip() =='SEND SIZE': #如果服务端收到了客户端发送过来的ack,给客户端返回⼀个syn信息,表⽰可以开始传数据了res_size = res_msg[1]self.request.send(b'CLIENT_READY_TO_RECV')recv_data = self.request.recv(1024) #接收数据total_len += len(recv_data) #记录接收数据长度with open(filename,'ab') as fd: #以追加的⽅式写⼊⽂件fd.write(recv_data)with open(logname,'w') as f: #把已接收到的数据长度写⼊⽇志f.write(str(total_len))if__name__ == '__main__':host,port = "localhost",5000server = socketserver.ThreadingTCPServer((host,port),MyTCPHandler)server.serve_forever() #开启服务端客户端源码:#!/usr/bin/env python# -*- coding:utf-8 -*-import socketimport os,sysreceive_file_path = os.path.abspath(os.path.join(os.path.abspath('.'),'receive_file')) #指定⽂件⽬录路径error_code = {'400':'FILE IS NOT EXISTS'}'''使⽤类的⽅式,⽅便反射'''class SOCKET(object):def__init__(self,ip,port):self.ip = ipself.port = portdef socket_obj(self):sk = socket.socket()sk.connect((self.ip,self.port))return skdef get(self): #get表⽰从服务端下载⽂件到本地conn = self.socket_obj() #⽣成对象user_input = input('get filename:') #指定输⼊命令格式 get filename# print(msg,type(msg))filename = user_input.split()[1] #获取⽂件名file = os.path.join(receive_file_path,filename) #下载⽂件的绝对路径logname = '%s.%s' % (filename,'log') #⽣成⽇志名log = os.path.join(receive_file_path,logname) #偏移量⽇志的绝对路径if os.path.exists(log) and os.path.exists(file): #判断是否需要续传,如果需要就读出偏移量with open(log) as f:offset = f.read().strip()else:offset = 0 # 否则偏移量置0msg = "%s|%s" %(user_input,offset)conn.send(msg.encode())total_length = int(offset) #记录传输完成了多少while True:server_ack_msg = conn.recv(100) #接收第⼀个ackif server_ack_msg.decode().strip() == '400': #如果ftp服务器没有这个资源,返回错误print('400', error_code['400'])conn.close()breakelif server_ack_msg.decode().strip() == 'END': #传输完成,ftp server返回字段,并删除偏移量⽇志conn.close()os.remove(log)breakres_msg = server_ack_msg.decode().split('|') #接收server的syn和传输数据⼤⼩的信息if res_msg[0].strip() == "SEND SIZE":res_size = int(res_msg[1])conn.send(b'CLIENT_READY_TO_RECV') #给server返回ackreceive_data = conn.recv(1024) #接收server的数据total_length += len(receive_data) #记录接收到了多少数据# print(receive_data.decode())# print(total_length)with open(file,'ab') as fd: #以追加的⽅式写⽂件fd.write(receive_data)with open(log,'w') as f: #把已接收数据长度写进⽇志f.write(str(total_length))def put(self): #put表⽰上传⽂件⾄服务端conn = self.socket_obj() #⽣成对象msg = input('put filename:') #指定命令输⼊格式,put filenamefilename = os.path.join(receive_file_path, msg.split()[1]) #⽣成上传⽂件路径if os.path.exists(filename): #判断⽂件存在与否,不存在返回错误conn.send(msg.encode()) #发送⽂件⾏为与⽂件名⾄服务端server_syn_msg = conn.recv(100) #接收服务端发送的偏移量信息offset = server_syn_msg.decode().split()[1]read_length = 0 #重置需要读取⽂件的长度with open(filename,'rb') as fd:while True:send_data = fd.read(1024) #开始读取⽂件,每次读取1024字节read_length += len(send_data) #记录读取数据长度if send_data and read_length> int(offset): #和服务端发送的偏移量进⾏⽐较,只有数据不为空和读到超过偏移量才会发送数据ack_msg = "SEND SIZE|%s" %len(send_data) #给服务端发送本次要发送数据的长度,相当于⼀个synconn.send(ack_msg.encode())client_ack = conn.recv(100) #接收到服务端发送的ack确认信息,收到之后开始传输数据if client_ack.decode() =='CLIENT_READY_TO_RECV':conn.send(send_data)elif read_length <= int(offset): #如果读取到的数据长度没到偏移量就继续循环读取⽂件continueelse:send_data = 'END'#⽂件已经读完,表⽰已经全部发送完成,给服务端发送信息说明客户端已经发送完成conn.send(send_data.encode())breakelse:print('400', error_code['400'])if__name__ == '__main__':c = SOCKET('127.0.0.1',5000)if hasattr(c,sys.argv[1]):func = getattr(c,sys.argv[1])func()由于时间原因,存在在传输的过程中有些⽂件⾥⾯涉及到中⽂的可能会报错的bug,只是功能基本实现,给⼤家分享⼀下我的思路,⽅便交流。
使用socket进行服务端与客户端传文件的方法

使⽤socket进⾏服务端与客户端传⽂件的⽅法逻辑:1.客户端将需要查找的⽂件名以流的形式传给服务端2.服务端接受客户端的连接,把流转化为字符串,进⾏⼀个⽬录的遍历,查找是否存在需要的⽂件,若未找到,则输出未找到,若找到,则将⽂件转化为流,传给客户端3.客户端准备接受,将服务端传过来的流转化为⽂件,存储下载。
4,⾄此,完成⼀个简单的客户端与服务端传输⽂件的⼩栗⼦~Client.Javapackage com.ysk;import java.io.BufferedReader;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import .ServerSocket;import .Socket;import .UnknownHostException;public class Client {public static void main(String[] args) {try {Socket socket = new Socket("127.0.0.1", 5555);OutputStream os = socket.getOutputStream();// 字节输出流PrintWriter pw = new PrintWriter(os);pw.write("aa.txt");//输⼊需要搜索的⽂件名pw.flush();socket.shutdownOutput();BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String a = "";String temp = "";while ((temp = in.readLine()) != null) {a += temp;}PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream("src\\asb.txt")));out.write(a);out.flush();out.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {}}}Server.javapackage com.ysk;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import .ServerSocket;import .Socket;public class Server {static ServerSocket serverSocket;public static void main(String[] args) {try {serverSocket = new ServerSocket(5555);System.out.println("***服务器即将启动,等待客户端的连接***");Socket socket = serverSocket.accept();BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String file = br.readLine();findFile("src", file);if (result) {System.out.println("已找到" + file);File f = new File(file);// File copyfile = new File("src\\file", "bb.txt");BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filepath + file)));System.out.println(filepath + file);String a = "";String temp = "";while ((temp = in.readLine()) != null) {a += temp;}PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));out.write(a);out.flush();socket.shutdownOutput();} else {System.out.println("未找到⽂件");}} catch (IOException e) {e.printStackTrace();}}需⾃备aa.txt,这种资源⽂件,以便测试。
PythonSocketserver实现FTP文件上传下载代码实例

PythonSocketserver实现FTP⽂件上传下载代码实例⼀、Socketserver实现FTP,⽂件上传、下载 ⽬录结构1、socketserver实现ftp⽂件上传下载,可以同时多⽤户登录、上传、下载 效果图:⼆、上⾯只演⽰了下载,上传也是⼀样的,来不及演⽰了,上代码1、客户端import socket,hashlib,os,json,sys,timeclass Ftpclient(object):def __init__(self):self.client = socket.socket()def connect(self,ip,port):self.client.connect((ip, port))def help(self):msg='''lspwdcd ..get filenameput filename'''print(msg)def interactive(self):"""客户端⼊⼝:return:"""while True:verify = self.authenticate() #服务器端认证if verify:while True:cmd = input('输⼊命令 >>').strip()if len(cmd) == 0:continuecmd_str = cmd.split()[0]if hasattr(self,'cmd_%s' %cmd_str):func = getattr(self,'cmd_%s' %cmd_str)func(cmd)else:self.help()def cmd_put(self,*args):"""上传⽂件:param args::return:"""cmd_solit = args[0].split()start_time = self.alltime() # 开始时间if len(cmd_solit) > 1:filename = cmd_solit[1]if os.path.isfile(filename):filesize = os.stat(filename).st_sizemsg_dic = {'filename':filename,'size':filesize,'overridden':True,'action':cmd_solit[0]}self.client.send( json.dumps(msg_dic).encode('utf-8'))server_respinse=self.client.recv(1024) #防⽌粘包,等服务器确认返回print('⽂件开始上传',server_respinse)client_size = 0f = open(filename,'rb')for line in f:client_size += self.client.send(line)self.processBar(client_size,filesize) #进度条else:print('⽂件传输完毕,⼤⼩为 %s'%client_size)end_time = self.alltime() # 结束时间print('本次上传花费了%s 秒'%self.alltime(end_time,start_time))f.close()else:print(filename,'⽂件不存在')else:print('输⼊有误!')def cmd_get(self,*args):"""下载⽂件:param args::return:"""cmd_solit = args[0].split()start_time = self.alltime() # 开始时间filename = cmd_solit[1]if len(cmd_solit) > 1:msg_dic = {'filename': filename,'size': '','overridden': True,'action': cmd_solit[0],'file_exist':''}self.client.send(json.dumps(msg_dic).encode('utf-8'))self.data = self.client.recv(1024).strip()cmd_dic = json.loads(self.data.decode('utf-8'))print(cmd_dic)if cmd_dic['file_exist']:if os.path.isfile(filename):f = open(filename + '.new', 'wb')else:f = open(filename, 'wb')self.client.send(b'200 ok') #防⽌粘包,等服务器确认返回client_size = 0filesize = cmd_dic['size']m = hashlib.md5()while client_size < filesize:data=self.client.recv(1024)f.write(data)client_size +=len(data)m.update(data)self.processBar(client_size, filesize)else:print('下载完毕')end_time = self.alltime() # 结束时间print('本次下载花费了%s 秒' % self.alltime(end_time, start_time)) f.close()new_file_md5 = m.hexdigest()server_file_md5 = self.client.recv(1024)print('MD5', server_file_md5,new_file_md5)else:print('下载的 %s⽂件不存在'%filename)else:print('输⼊有误!')def cmd_dir(self,*arge):cmd_solit = arge[0].split()if len(cmd_solit) > 0:msg_dic = {'action': cmd_solit[0]}self.client.send(json.dumps(msg_dic).encode())cmd_dir = self.client.recv(1024)print(cmd_dir.decode())else:print('输⼊错误!')def alltime(self,*args):"""计算上传、下载时间:param args::return:"""if args:return round(args[0] - args[1])else:return time.time()def processBar(self,num, total):"""进度条:param num:⽂件总⼤⼩:param total: 已存⼊⽂件⼤⼩:return:"""rate = num / totalrate_num = int(rate * 100)if rate_num == 100:r = '\r%s>%d%%\n' % ('=' * int(rate_num /3), rate_num,)else:r = '\r%s>%d%%' % ('=' * int(rate_num /3), rate_num,)sys.stdout.write(r)sys.stdout.flushdef authenticate(self):"""⽤户加密认证:return:"""username = input('输⼊⽤户名:>>')password = input('输⼊密码:>>')m = hashlib.md5()if len(username) > 0 and len(password) >0:username = ''.join(username.split())password = ''.join(password.split())m.update(username.encode('utf-8'))m.update(password.encode('utf-8'))m = {'username':username,'password':password,'md5':m.hexdigest()}self.client.send(json.dumps(m).encode('utf-8'))server_user_md5 = self.client.recv(1024).strip()print(server_user_md5.decode())if server_user_md5.decode() == 'success':print('登录成功!')return 'ok'print('⽤户名密码错误!')else:print('请输⼊⽤户名密码')f = Ftpclient()f.connect('localhost',9999)f.interactive()2、服务器端import socketserver,json,os,hashlib,sys,paramikoimport settingsclass Mysocketserver(socketserver.BaseRequestHandler):def put(self,*args):'''接受客户端上传⽂件:return:'''cmd_dic = args[0]filename = cmd_dic['filename'] #获取⽂件名filesize= cmd_dic['size'] #获取⽂件⼤⼩(字节)if os.path.isfile(filename): #判断⽂件是否存在f = open(filename + '.new','wb')else:f = open(filename, 'wb')self.request.send(b'200 ok') #防⽌粘包print('%s ⽂件开始上传' % self.client_address[0])received_size = 0while received_size < filesize:data = self.request.recv(1024)f.write(data)received_size += len(data)else:print('⽂件传输完毕',filename)def get(self, *args):'''客户端下载⽂件:return:'''msg_dic = {'filename': '','size': '','overridden': True,'action': '','file_exist': ''}cmd_solit = args[0]filename = cmd_solit['filename']file_exist = os.path.isfile(filename)msg_dic['file_exist'] = file_existprint(file_exist)if file_exist:filesize = os.stat(filename).st_sizemsg_dic['filename'] = filenamemsg_dic['size'] = filesizemsg_dic['action'] = cmd_solit['action']self.request.send(json.dumps(msg_dic).encode('utf-8'))server_respang = self.request.recv(1024) #防⽌粘包print('开始传输',server_respang)f = open(filename,'rb')m = hashlib.md5()for lien in f:m.update(lien)self.request.send(lien)else:print('传输完成')f.close()self.request.send(m.hexdigest().encode())else:print('⽂件不存在')self.request.send(json.dumps(msg_dic).encode('utf-8'))def client_authentication(self):"""客户端认证:return:"""self.client_user= self.request.recv(1024).strip()client_xinxi = json.loads(self.client_user.decode('utf-8'))try:with open(settings.school_db_file + client_xinxi['username'],'rb') as f:data = json.load(f)if data['md5'] == client_xinxi['md5']: #判断⽤户输⼊是否和服务器端MD5是否⼀致 print('验证成功!')self.request.send('success'.encode())return 'success'else:self.request.send('error'.encode())except Exception as e:print('没有此⽤户',e)self.request.send('error'.encode())def dir(self,*args):"""查看⽬录:param args::return:"""cmd_split = args[0]dd=cmd_split['action']result_os = os.popen(dd).read()self.request.send(result_os.encode())def handle(self):"""服务器端⼊⼝:return:"""try:success = self.client_authentication()if success:self.data=self.request.recv(1024).strip()cmd_dic = json.loads(self.data.decode('utf-8'))action = cmd_dic['action']if hasattr(self,action):func = getattr(self,action)func(cmd_dic)except ConnectionResetError as e:print('连接断开',self.client_address[0])breakif __name__ == '__main__':HOST,PORT='localhost',9999server=socketserver.ThreadingTCPServer((HOST,PORT),Mysocketserver)server.serve_forever()settings.py ⽂件import osBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))DB_FILE = os.path.join(BASE_DIR, "data\\")school_db_file = os.path.join(DB_FILE)print(school_db_file)data⾥两个做测试的⽂件,alex ⽂件内容:{"username": "alex", "password": "123456", "md5": "94e4ccf5e2749b0bfe0428603738c0f9"}kml123456⽂件内容:{"username": "kml123456", "password": "123456","md5": "a791650e70ce08896e3dafbaa7598c26"}到这⾥差不多就没了,以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
C# socket方式 FTP上传下载方法集

类使用:FTP myftp = new FTP([ftp主机], [主机目录], [ftp用户名], [ftp用户密码], [ftp端口]); myftp.Put( [本地文件全路径]); // 上传到服务器中myftp.Get([ftp文件名称(可以是通配符表示)], [本地存放路径]); //下载到本地择自互联网csdn网站.C# codeusing System;using ;using .Sockets;using System.Text;using System.IO;public class FTP{private string strRemoteHost;private int strRemotePort;private string strRemotePath;private string strRemoteUser;private string strRemotePass;private Boolean bConnected;#region内部变量///<summary>///服务器返回的应答信息(包含应答码)///</summary>private string strMsg;///<summary>///服务器返回的应答信息(包含应答码)///</summary>private string strReply;///<summary>///服务器返回的应答码///</summary>private int iReplyCode;///<summary>///进行控制连接的socket///</summary>private Socket socketControl;///<summary>///传输模式///</summary>private TransferType trType;///<summary>///传输模式:二进制类型、ASCII类型///</summary>public enum TransferType{///<summary>/// Binary///</summary>Binary,///<summary>/// ASCII///</summary>ASCII};///<summary>///接收和发送数据的缓冲区///</summary>private static int BLOCK_SIZE = 3072;Byte[] buffer = new Byte[BLOCK_SIZE];///<summary>///编码方式///</summary>Encoding ASCII = Encoding.Default;#endregion#region内部函数#region构造函数///<summary>///缺省构造函数///</summary>/* public FTP(){strRemoteHost = "";strRemotePath = "";strRemoteUser = "";strRemotePass = "";strRemotePort = 21;bConnected = false;}*////<summary>///构造函数///</summary>///<param name="remoteHost"></param>///<param name="remotePath"></param>///<param name="remoteUser"></param>///<param name="remotePass"></param>///<param name="remotePort"></param>public FTP(string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort){strRemoteHost = remoteHost;strRemotePath = remotePath;strRemoteUser = remoteUser;strRemotePass = remotePass;strRemotePort = remotePort;Connect();}#endregion#region登陆///<summary>/// FTP服务器IP地址///</summary>public string RemoteHost{get{return strRemoteHost;}set{strRemoteHost = value;}}///<summary>/// FTP服务器端口///</summary>public int RemotePort{get{return strRemotePort;}set{strRemotePort = value; }}///<summary>///当前服务器目录///</summary>public string RemotePath{get{return strRemotePath; }set{strRemotePath = value; }}///<summary>///登录用户账号///</summary>public string RemoteUser{set{strRemoteUser = value; }}///<summary>///用户登录密码///</summary>public string RemotePass{set{strRemotePass = value; }}///<summary>///是否登录///</summary>public bool Connected{get{return bConnected;}}#endregion#region链接///<summary>///建立连接///</summary>public void Connect(){socketControl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint ep = new IPEndPoint(IPAddress.Parse(RemoteHost), strRemotePort);// 链接try{socketControl.Connect(ep);}catch (Exception){throw new IOException("Couldn't connect to remote server");}// 获取应答码ReadReply();if (iReplyCode != 220){DisConnect();throw new IOException(strReply.Substring(4));}// 登陆SendCommand("USER " + strRemoteUser);if (!(iReplyCode == 331 || iReplyCode == 230)){CloseSocketConnect();//关闭连接throw new IOException(strReply.Substring(4));}if (iReplyCode != 230){SendCommand("PASS " + strRemotePass);if (!(iReplyCode == 230 || iReplyCode == 202)){CloseSocketConnect();//关闭连接throw new IOException(strReply.Substring(4)); }}bConnected = true;// 切换到目录ChDir(strRemotePath);}///<summary>///关闭连接///</summary>public void DisConnect(){if (socketControl != null){SendCommand("QUIT");}CloseSocketConnect();}#endregion#region传输模式///<summary>///设置传输模式///</summary>///<param name="ttType">传输模式</param>public void SetTransferType(TransferType ttType){if (ttType == TransferType.Binary){SendCommand("TYPE I");//binary类型传输}else{SendCommand("TYPE A");//ASCII类型传输}if (iReplyCode != 200){throw new IOException(strReply.Substring(4));}else{trType = ttType;}}///<summary>///获得传输模式///</summary>///<returns>传输模式</returns>public TransferType GetTransferType(){return trType;}#endregion#region文件操作///<summary>///获得文件列表///</summary>///<param name="strMask">文件名的匹配字符串</param>///<returns></returns>public string[] Dir(string strMask){// 建立链接if (!bConnected){Connect();}//建立进行数据连接的socketSocket socketData = CreateDataSocket();//传送命令SendCommand("NLST " + strMask);//分析应答代码if (!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226)) {throw new IOException(strReply.Substring(4));//获得结果strMsg = "";while (true){int iBytes = socketData.Receive(buffer, buffer.Length, 0); strMsg += ASCII.GetString(buffer, 0, iBytes);if (iBytes < buffer.Length){break;}}char[] seperator = { '\n' };string[] strsFileList = strMsg.Split(seperator);socketData.Close();//数据socket关闭时也会有返回码if (iReplyCode != 226){ReadReply();if (iReplyCode != 226){throw new IOException(strReply.Substring(4));}}return strsFileList;}///<summary>///获取文件大小///</summary>///<param name="strFileName">文件名</param>///<returns>文件大小</returns>private long GetFileSize(string strFileName){if (!bConnected){Connect();}SendCommand("SIZE " + Path.GetFileName(strFileName));long lSize = 0;if (iReplyCode == 213){lSize = Int64.Parse(strReply.Substring(4));else{throw new IOException(strReply.Substring(4));}return lSize;}///<summary>///删除///</summary>///<param name="strFileName">待删除文件名</param>public void Delete(string strFileName){if (!bConnected){Connect();}SendCommand("DELE " + strFileName);if (iReplyCode != 250){throw new IOException(strReply.Substring(4));}}///<summary>///重命名(如果新文件名与已有文件重名,将覆盖已有文件)///</summary>///<param name="strOldFileName">旧文件名</param>///<param name="strNewFileName">新文件名</param>public void Rename(string strOldFileName, string strNewFileName) {if (!bConnected){Connect();}SendCommand("RNFR " + strOldFileName);if (iReplyCode != 350){throw new IOException(strReply.Substring(4));}// 如果新文件名与原有文件重名,将覆盖原有文件SendCommand("RNTO " + strNewFileName);if (iReplyCode != 250){throw new IOException(strReply.Substring(4));}}#endregion#region上传和下载///<summary>///下载一批文件///</summary>///<param name="strFileNameMask">文件名的匹配字符串</param>///<param name="strFolder">本地目录(不得以\结束)</param>public void Get(string strFileNameMask, string strFolder){if (!bConnected){Connect();}string[] strFiles = Dir(strFileNameMask);foreach (string strFile in strFiles){if (!strFile.Equals(""))//一般来说strFiles的最后一个元素可能是空字符串 {Get(strFile, strFolder, strFile);}}}///<summary>///下载一个文件///</summary>///<param name="strRemoteFileName">要下载的文件名</param>///<param name="strFolder">本地目录(不得以\结束)</param>///<param name="strLocalFileName">保存在本地时的文件名</param>public void Get(string strRemoteFileName, string strFolder, string strLocalFileName){if (!bConnected){Connect();}SetTransferType(TransferType.Binary);if (strLocalFileName.Equals("")){strLocalFileName = strRemoteFileName;}if (!File.Exists(strLocalFileName)){Stream st = File.Create(strLocalFileName);st.Close();}FileStream output = newFileStream(strFolder + "\\" + strLocalFileName, FileMode.Create);Socket socketData = CreateDataSocket();SendCommand("RETR " + strRemoteFileName);if (!(iReplyCode == 150 || iReplyCode == 125|| iReplyCode == 226 || iReplyCode == 250)){throw new IOException(strReply.Substring(4));}while (true){int iBytes = socketData.Receive(buffer, buffer.Length, 0);output.Write(buffer, 0, iBytes);if (iBytes <= 0){break;}}output.Close();if (socketData.Connected){socketData.Close();}if (!(iReplyCode == 226 || iReplyCode == 250)){ReadReply();if (!(iReplyCode == 226 || iReplyCode == 250)){throw new IOException(strReply.Substring(4));}}}///<summary>///上传一批文件///</summary>///<param name="strFolder">本地目录(不得以\结束)</param>///<param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param>public void Put(string strFolder, string strFileNameMask){string[] strFiles = Directory.GetFiles(strFolder, strFileNameMask);foreach (string strFile in strFiles){//strFile是完整的文件名(包含路径)Put(strFile);}}///<summary>///上传一个文件///</summary>///<param name="strFileName">本地文件名</param>public void Put(string strFileName){if (!bConnected){Connect();}Socket socketData = CreateDataSocket();SendCommand("STOR " + Path.GetFileName(strFileName));if (!(iReplyCode == 125 || iReplyCode == 150)){throw new IOException(strReply.Substring(4));}FileStream input = newFileStream(strFileName, FileMode.Open);int iBytes = 0;while ((iBytes = input.Read(buffer, 0, buffer.Length)) > 0){socketData.Send(buffer, iBytes, 0);}input.Close();if (socketData.Connected){socketData.Close();}if (!(iReplyCode == 226 || iReplyCode == 250)){ReadReply();if (!(iReplyCode == 226 || iReplyCode == 250)){throw new IOException(strReply.Substring(4)); }}}#endregion#region目录操作///<summary>///创建目录///</summary>///<param name="strDirName">目录名</param>public void MkDir(string strDirName){if (!bConnected){Connect();}SendCommand("MKD " + strDirName);if (iReplyCode != 257){throw new IOException(strReply.Substring(4));}}///<summary>///删除目录///</summary>///<param name="strDirName">目录名</param>public void RmDir(string strDirName){if (!bConnected){Connect();}SendCommand("RMD " + strDirName);if (iReplyCode != 250){throw new IOException(strReply.Substring(4));}}///<summary>///改变目录///</summary>///<param name="strDirName">新的工作目录名</param>public void ChDir(string strDirName){if (strDirName.Equals(".") || strDirName.Equals("")) {return;}if (!bConnected){Connect();}SendCommand("CWD " + strDirName);if (iReplyCode != 250){throw new IOException(strReply.Substring(4));}this.strRemotePath = strDirName;}#endregion///<summary>///将一行应答字符串记录在strReply和strMsg///应答码记录在iReplyCode///</summary>private void ReadReply(){strMsg = "";strReply = ReadLine();iReplyCode = Int32.Parse(strReply.Substring(0, 3)); }///<summary>///建立进行数据连接的socket///</summary>///<returns>数据连接socket</returns>private Socket CreateDataSocket(){SendCommand("PASV");if (iReplyCode != 227){throw new IOException(strReply.Substring(4));}int index1 = strReply.IndexOf('(');int index2 = strReply.IndexOf(')');string ipData =strReply.Substring(index1 + 1, index2 - index1 - 1);int[] parts = new int[6];int len = ipData.Length;int partCount = 0;string buf = "";for (int i = 0; i < len && partCount <= 6; i++){char ch = Char.Parse(ipData.Substring(i, 1));if (Char.IsDigit(ch))buf += ch;else if (ch != ','){throw new IOException("Malformed PASV strReply: " +strReply);}if (ch == ',' || i + 1 == len){try{parts[partCount++] = Int32.Parse(buf);buf = "";}catch (Exception){throw new IOException("Malformed PASV strReply: " + strReply);}}}string ipAddress = parts[0] + "." + parts[1] + "." +parts[2] + "." + parts[3];int port = (parts[4] << 8) + parts[5];Socket s = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPEndPoint ep = newIPEndPoint(IPAddress.Parse(ipAddress), port);try{s.Connect(ep);}catch (Exception){throw new IOException("Can't connect to remote server");}return s;}///<summary>///关闭socket连接(用于登录以前)///</summary>private void CloseSocketConnect(){if (socketControl != null){socketControl.Close();socketControl = null;}bConnected = false;}///<summary>///读取Socket返回的所有字符串///</summary>///<returns>包含应答码的字符串行</returns>private string ReadLine(){while (true){int iBytes = socketControl.Receive(buffer, buffer.Length, 0);strMsg += ASCII.GetString(buffer, 0, iBytes);if (iBytes < buffer.Length){break;}}char[] seperator = { '\n' };string[] mess = strMsg.Split(seperator);if (strMsg.Length > 2){strMsg = mess[mess.Length - 2];//seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串,//但也会分配为空字符串给后面(也是最后一个)字符串数组,//所以最后一个mess是没用的空字符串//但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格 }else{strMsg = mess[0];}if (!strMsg.Substring(3, 1).Equals(" "))//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串){return ReadLine();}return strMsg;}///<summary>///发送命令并获取应答码和最后一行应答字符串///</summary>///<param name="strCommand">命令</param>private void SendCommand(string strCommand){Byte[] cmdBytes = ASCII.GetBytes((strCommand + "\r\n").ToCharArray());socketControl.Send(cmdBytes, cmdBytes.Length, 0);ReadReply();}#endregion}。
java 不同系统之间传输数据的方法

java 不同系统之间传输数据的方法Java是一种强大且广泛应用的编程语言,用于开发各种类型的应用程序。
在实际开发中,经常需要在不同的系统之间传输数据。
本文将介绍一些常用的方法来实现Java不同系统之间的数据传输。
1. 使用Socket通信Socket通信是一种常用的网络通信方式,可以实现不同系统之间的数据传输。
通过Socket,我们可以在客户端和服务器之间建立一条双向通道进行数据交换。
在Java中,可以使用Java的原生Socket库来实现Socket通信。
客户端和服务器端通过准确的IP地址和端口号来建立连接。
客户端可以使用Socket类来与服务器进行通信,而服务器则使用ServerSocket类监听并接受客户端连接。
2. 使用HTTP协议HTTP协议是一种应用层协议,常用于Web应用程序中。
通过HTTP协议,不同系统之间可以通过发送和接收HTTP请求和响应来进行数据传输。
在Java中,可以使用Java的HttpURLConnection类或者第三方库,如Apache 的HttpClient来实现HTTP通信。
通过发送HTTP请求,可以将数据以请求参数或JSON/XML等格式发送到目标系统,并接收目标系统的HTTP响应。
3. 使用WebServiceWebService是一种通过网络进行通信的软件系统。
它可以使不同系统之间的应用程序通过Web服务接口进行数据传输和交互。
在Java中,可以使用Java的JAX-WS和JAX-RPC等API来开发和使用WebService。
通过定义WebService接口和实现相应的服务端和客户端,可以在不同系统之间轻松地传输数据。
4. 使用消息队列消息队列是一种常用的异步通信方式,允许不同系统之间以消息的形式传递数据。
消息队列将数据发送方发送的消息存储在队列中,接收方从队列中接收并处理消息。
在Java中,可以使用ActiveMQ、RabbitMQ等消息中间件来实现消息队列。
socket网络编程【实现FTP文件上传和下载】
socket⽹络编程【实现FTP⽂件上传和下载】有四个基本的具体服务器类:class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)这使⽤Internet TCP协议,它在客户端和服务器之间提供连续的数据流。
如果bind_and_activate为true,构造函数将⾃动尝试调⽤server_bind()和server_activate()。
其他参数传递到BaseServer基类。
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True) 这使⽤数据报,其是可能在运输中不按顺序到达或丢失的信息的离散分组。
参数与TCPServer相同。
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)这些更常⽤的类与TCP和UDP类类似,但使⽤Unix域套接字;它们在⾮Unix平台上不可⽤。
参数与TCPServer相同。
⽰例:使⽤SocketServer⽹络服务框架实现FTP⽂件上传和下载功能服务端:1# !/usr/bin/python2# -*- coding:utf-8 -*-3'''4_author_=Captain5'''6import SocketServer7import os89class Myserver(SocketServer.BaseRequestHandler):10def handle(self):11 base_path="D:\\temp"#⽂件保存路径12 conn=self.request13 client_address=self.client_address14print"客户端:"+str(client_address)+"connected..."15 conn.send("服务端已准备接收...")16while True:17 pre_data1,pre_data2=conn.recv(1024).split('|')18if pre_data1=='put': #如果客户端是要上传⽂件19 file_name,file_size=pre_data2.split('-') #对客户端传递过来的⽂件名和⽂件⼤⼩两个值分割开20 recv_size=021 file_dir=os.path.join(base_path,file_name) #⽣成⽂件保存的路径22 with open(file_dir,'wb') as f:23 Flag = True24while Flag:25if int(file_size)>recv_size:26 data = conn.recv(1024)27 recv_size += len(data)28 f.write(data)29else:30 recv_size = 031 Flag = False32 conn.send("upload successed")3334elif pre_data1 == 'get': #如果客户端是要下载⽂件35 file_size = os.stat(pre_data2).st_size36 file_path,file_name = os.path.split(pre_data2)37 conn.send(file_name + '|' + str(file_size)) #将⽂件名和计算出的⽂件⼤⼩发给客户端38 send_size = 039 with open(pre_data2, 'rb') as f:40 Flag = True41while Flag:42if send_size + 1024 > file_size: #如果⽂件⼤⼩不是1024的倍数,即最后的数据43 data = f.read(file_size - send_size)44 Flag = False45else:46 data = f.read(1024)47 send_size += 102448 conn.send(data)495051if__name__ == '__main__':52 instance = SocketServer.ThreadingTCPServer(('127.0.0.1',8866), Myserver) #使⽤异步多进程53 instance.serve_forever()View Code客户端:1# !/usr/bin/python2# -*- coding:utf-8 -*-3'''4_author_=Captain5'''6import socket7import sys8import os910 ip_port=('127.0.0.1',8866)11 sk=socket.socket()12 sk.connect(ip_port)13print sk.recv(1024) #接收服务端提⽰建⽴连接的消息14while True:15 cmd,file_dir = raw_input('path:').split() #对输⼊的命令进⾏分割,如:put c:\xx\11.txt16if cmd=='put':17 file_path, file_name = os.path.split(file_dir)18 file_size=os.stat(file_dir).st_size #计算⽂件⼤⼩19 sk.send(cmd + '|' + file_name + '-' + str(file_size))20 send_size = 021 with open(file_dir,'rb') as f:22 Flag = True23while Flag:24if send_size + 1024 > file_size:25 data = f.read(file_size - send_size)26 Flag = False27else:28 data = f.read(1024)29 send_size += 102430 sk.send(data)31print sk.recv(1024) # 接收服务端返回⽂件上传成功的消息3233elif cmd=='get':34 sk.send(cmd + '|' + file_dir) #把要下载服务端某个⽂件的具体路径发送到服务端35 file_name,file_size=sk.recv(1024).split('|') #接收服务端返回的⽂件名和⽂件⼤⼩36 file_dir = os.path.join(os.getcwd(), file_name)37 recv_size = 038 with open(file_dir, 'wb') as f:39 Flag = True40while Flag:41if int(file_size) > recv_size:42 data = sk.recv(1024)43 recv_size += len(data)44 f.write(data)45else:46 recv_size = 047 Flag = False48print'download successed,⽂件已保存到 %s' % file_dir49else:50print"请输⼊put或get命令,确认是上传还是下载⽂件"5152 sk.close()View Code。
使用Socket 通信实现 FTP 客户端程序
FTP 概述文件传输协议〔FTP〕作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。
FTP的目标是提高文件的共享性和可靠高效地传送数据。
在传输文件时,FTP 客户端程序先与效劳器建立连接,然后向效劳器发送命令。
效劳器收到命令后给予响应,并执行命令。
FTP 协议与操作系统无关,任何操作系统上的程序只要符合FTP 协议,就可以相互传输数据。
本文主要基于LINUX 平台,对FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的FTP 客户端。
回页首FTP 协议相比其他协议,如协议,FTP 协议要复杂一些。
与一般的C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个Socket 连接,这个连接同时处理效劳器端和客户端的连接命令和数据传输。
而FTP协议中将命令与数据分开传送的方法提高了效率。
FTP 使用2 个端口,一个数据端口和一个命令端口〔也叫做控制端口〕。
这两个端口一般是21 〔命令端口〕和20 〔数据端口〕。
控制Socket 用来传送命令,数据Socket 是用于传送数据。
每一个FTP 命令发送之后,FTP 效劳器都会返回一个字符串,其中包括一个响应代码和一些说明信息。
其中的返回码主要是用于判断命令是否被成功执行了。
命令端口一般来说,客户端有一个Socket 用来连接FTP 效劳器的相关端口,它负责FTP 命令的发送和接收返回的响应信息。
一些操作如“登录〞、“改变目录〞、“删除文件〞,依靠这个连接发送命令就可完成。
数据端口对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个Socket来完成。
如果使用被动模式,通常效劳器端会返回一个端口号。
客户端需要用另开一个Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。
如果使用主动模式,通常客户端会发送一个端口号给效劳器端,并在这个端口监听。
效劳器需要连接到客户端开启的这个数据端口,并进行数据的传输。
linux开发板C实现ftp客户端
linux开发板C实现ftp客户端由于需要和windows服务器连接,使⽤的是ftp通信协议,windows上使⽤filezilla作为服务器会有linux开发板的ftp⼯具,但是实际上也是socket,所有使⽤socket就可以做到ftp⽂件传输,这样也可以根据实际情况⽐较好的控制,所以我使⽤C实现了ftp的客户端程序。
以下是登陆代码代码int login(){//初始化端⼝信息struct sockaddr_in serv_addr;char senddate,recvdate;char sendline[MAXSIZE],recvline[MAXSIZE];struct hostent *host;//获取hostent中相关参数char name[MAXSIZE],password[MAXSIZE];printf("please enter the hostname\n");printf("ftp->");fflush(stdout);//创建socketif( (control_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);return -1 ;//exit(0);}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERV_PORT);serv_addr.sin_addr.s_addr=INADDR_ANY;//点分⼗进制转化为⼆进制整数地址if(inet_pton(AF_INET, FTP_CONNECT_IP, &serv_addr.sin_addr) <= 0){printf("inet_pton error for %s\n", FTP_CONNECT_IP);close(control_sockfd);return -1 ;//exit(0);}//调⽤connect函数发起连接if((connect(control_sockfd,(SA*)&serv_addr,sizeof(serv_addr)))<0){printf("连接失败\n");login_yes=0;}///printf("连接到 %d--->>>/n",&serv_addr.sin_addr);recvdate=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvdate==-1){printf("recvdate is connect error/n");login_yes=0;}else if(strncmp(recvline,"220",3)==0){printf("220 连接成功,请输⼊⽤户名\n");login_yes=1;}else{printf("220 connect is error!");login_yes=0;}//ftp⽤户登录主体部分int sendbytes,recvbytes;zeromery(name,1024);zeromery(password,1024);zeromery(recvline,1024);zeromery(sendline,1024);//printf("⾃动登录⽤户名:windows-ftp\n") ;strcat(sendline,"USER ");strcat(sendline,"windows-ftp");strcat(sendline,"\r\n");printf("--->%s\n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("send is wrong\n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"331",3)==0){//printf("331 please specify the password./n");printf("331 请输⼊密码\n");}else{printf("recv date is error./n");login_yes=0;}zeromery(sendline,1024);zeromery(recvline,1024);sleep(1) ;printf("密码:111111\n") ;strcat(sendline,"PASS ");strcat(sendline,"111111");strcat(sendline,"\r\n");printf("--->%s\n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("pass send is error\n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"230",3)==0){printf("登录成功!\n");login_yes=1;}else{printf("pass recv is error\n");login_yes=0;}if(login_yes==0){close(control_sockfd); //登陆失败关闭接⼝return -1 ;}//进⼊到对应⼦路径//////ftp_changdir("001",control_sockfd); //cdreturn control_sockfd;}实现了登陆,就意味着可以和service进⾏通信了,接下来的事情九四read 和 write 或者send和rev的事情。
Java通过Socket实现TCPIP协议的通信(客户端)
Java通过Socket实现TCPIP协议的通信(客户端)1.先创建Socket对象,并连接服务器的IP和端⼝号2.连接建⽴后,通过map格式输出流向服务器端发送请求报⽂3.通过输⼊流获取服务器响应的报⽂4.关闭相关资源代码如下:package com.demo.util;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import .Socket;import java.util.HashMap;import java.util.Map;import com.alibaba.fastjson.JSON;public class SocketClient {public static void main(String[] args) {InputStreamReader isr;BufferedReader br;OutputStreamWriter osw;BufferedWriter rw;try {Socket socket = new Socket("IP","端⼝号");Map bodyMap = new HashMap();Map headMap = new HashMap();headMap.put("报⽂头", "报⽂头");bodyMap.put("报⽂体", "报⽂体");Map sendMap = new HashMap();sendMap.put("head", headMap);sendMap.put("body", bodyMap);ByteArrayOutputStream baos = new ByteArrayOutputStream();String json = JSON.toJSONString(sendMap);System.out.println("send message:" + json);byte[] content = json.getBytes("UTF-8");String tmp = ("00000000" + String.valueOf(content.length));String length = tmp.substring(tmp.length() - 8);baos.write(length.getBytes());baos.write(content);try{writeStream(socket.getOutputStream(), baos.toByteArray());Object result = readStream(socket.getInputStream());System.out.println(result);}catch (Exception e){}finally{if (socket != null) {try {socket.close();} catch (IOException e) {socket = null;System.out.println("客户端 finally 异常:" + e.getMessage());}}}} catch (Exception e) {// TODO: handle exception}}protected static void writeStream(OutputStream out, byte[] sndBuffer) throws IOException{out.write(sndBuffer);out.flush();}protected static Object readStream(InputStream input) throws Exception {// TODO Auto-generated method stubint headLength = 8;byte[] headBuffer = new byte[headLength];for (int offset = 0; offset < headLength;) {int length = input.read(headBuffer, offset, headLength - offset);if (length < 0) {throw new RuntimeException("invalid_packet_head");}offset += length;}int totalLength = Integer.parseInt(new String(headBuffer, "UTF-8"));byte[] resultBuffer = new byte[totalLength];int offset = 0;while (offset < totalLength) {int realLength = input.read(resultBuffer, offset, totalLength - offset);if (realLength >= 0) {offset += realLength;} else {System.err.println("the length of packet should be :" + totalLength + " but encounter eof at offset:" + offset);throw new RuntimeException("invalid_packet_data");}}String recvStr = new String(resultBuffer, "UTF-8");System.out.println(recvStr);return recvStr.getBytes();}}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
编程标记本文!发布日期: 2011 年4 月07 日级别:初级访问情况: 16502 次浏览评论: 0 (查看 | 添加评论 - 登录)平均分(50个评分)为本文评分FTP 概述文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。
FTP的目标是提高文件的共享性和可靠高效地传送数据。
在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。
服务器收到命令后给予响应,并执行命令。
FTP 协议与操作系统无关,任何操作系统上的程序只要符合FTP 协议,就可以相互传输数据。
本文主要基于LINUX 平台,对FTP 客户端的实现原理进行详尽的解释并阐述如何使用C 语言编写一个简单的FTP 客户端。
回页首FTP 协议相比其他协议,如HTTP 协议,FTP 协议要复杂一些。
与一般的C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。
而FTP协议中将命令与数据分开传送的方法提高了效率。
FTP 使用2 个端口,一个数据端口和一个命令端口(也叫做控制端口)。
这两个端口一般是21 (命令端口)和20 (数据端口)。
控制Socket 用来传送命令,数据Socket 是用于传送数据。
每一个FTP 命令发送之后,FTP 服务器都会返回一个字符串,其中包括一个响应代码和一些说明信息。
其中的返回码主要是用于判断命令是否被成功执行了。
命令端口一般来说,客户端有一个Socket 用来连接FTP 服务器的相关端口,它负责FTP 命令的发送和接收返回的响应信息。
一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。
数据端口对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个Socket来完成。
如果使用被动模式,通常服务器端会返回一个端口号。
客户端需要用另开一个Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。
如果使用主动模式,通常客户端会发送一个端口号给服务器端,并在这个端口监听。
服务器需要连接到客户端开启的这个数据端口,并进行数据的传输。
下面对FTP 的主动模式和被动模式做一个简单的介绍。
主动模式(PORT)主动模式下,客户端随机打开一个大于1024 的端口向服务器的命令端口P,即21 端口,发起连接,同时开放N +1 端口监听,并向服务器发出“port N+1” 命令,由服务器从它自己的数据端口(20) 主动连接到客户端指定的数据端口(N+1)。
FTP 的客户端只是告诉服务器自己的端口号,让服务器来连接客户端指定的端口。
对于客户端的防火墙来说,这是从外部到内部的连接,可能会被阻塞。
被动模式(PASV)为了解决服务器发起到客户的连接问题,有了另一种FTP 连接方式,即被动方式。
命令连接和数据连接都由客户端发起,这样就解决了从服务器到客户端的数据端口的连接被防火墙过滤的问题。
被动模式下,当开启一个FTP 连接时,客户端打开两个任意的本地端口(N > 1024 和N+1) 。
第一个端口连接服务器的21 端口,提交PASV 命令。
然后,服务器会开启一个任意的端口(P > 1024 ),返回如“227 entering passive mode (127,0,0,1,4,18)”。
它返回了227 开头的信息,在括号中有以逗号隔开的六个数字,前四个指服务器的地址,最后两个,将倒数第二个乘256 再加上最后一个数字,这就是FTP 服务器开放的用来进行数据传输的端口。
如得到227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口号是p1*256+p2,ip 地址为h1.h2.h3.h4。
这意味着在服务器上有一个端口被开放。
客户端收到命令取得端口号之后, 会通过N+1 号端口连接服务器的端口P,然后在两个端口之间进行数据传输。
主要用到的FTP 命令FTP 每个命令都有3 到4 个字母组成,命令后面跟参数,用空格分开。
每个命令都以"\r\n"结束。
要下载或上传一个文件,首先要登入FTP 服务器,然后发送命令,最后退出。
这个过程中,主要用到的命令有USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。
USER: 指定用户名。
通常是控制连接后第一个发出的命令。
“USER gaoleyi\r\n”:用户名为gaoleyi 登录。
PASS: 指定用户密码。
该命令紧跟USER 命令后。
“PASS gaoleyi\r\n”:密码为gaoleyi。
SIZE: 从服务器上返回指定文件的大小。
“SIZE file.txt\r\n”:如果file.txt 文件存在,则返回该文件的大小。
CWD: 改变工作目录。
如:“CWD dirname\r\n”。
PASV: 让服务器在数据端口监听,进入被动模式。
如:“PASV\r\n”。
PORT: 告诉FTP 服务器客户端监听的端口号,让FTP 服务器采用主动模式连接客户端。
如:“PORT h1,h2,h3,h4,p1,p2”。
RETR: 下载文件。
“RETR file.txt \r\n”:下载文件file.txt。
STOR: 上传文件。
“STOR file.txt\r\n”:上传文件file.txt。
REST: 该命令并不传送文件,而是略过指定点后的数据。
此命令后应该跟其它要求文件传输的FTP 命令。
“REST 100\r\n”:重新指定文件传送的偏移量为100 字节。
QUIT: 关闭与服务器的连接。
FTP 响应码客户端发送FTP 命令后,服务器返回响应码。
响应码用三位数字编码表示:第一个数字给出了命令状态的一般性指示,比如响应成功、失败或不完整。
第二个数字是响应类型的分类,如2 代表跟连接有关的响应,3 代表用户认证。
第三个数字提供了更加详细的信息。
第一个数字的含义如下:1 表示服务器正确接收信息,还未处理。
2 表示服务器已经正确处理信息。
3 表示服务器正确接收信息,正在处理。
4 表示信息暂时错误。
5 表示信息永久错误。
第二个数字的含义如下:0 表示语法。
1 表示系统状态和信息。
2 表示连接状态。
3 表示与用户认证有关的信息。
4 表示未定义。
5 表示与文件系统有关的信息。
Socket 编程的几个重要步骤Socket 客户端编程主要步骤如下:1. socket() 创建一个Socket2. connect() 与服务器连接3. write() 和read() 进行会话4. close() 关闭SocketSocket 服务器端编程主要步骤如下:1. socket() 创建一个Socket2. bind()3. listen() 监听4. accept() 接收连接的请求5. write() 和read() 进行会话6. close() 关闭Socket回页首实现FTP 客户端上传下载功能下面让我们通过一个例子来对FTP 客户端有一个深入的了解。
本文实现的FTP 客户端有下列功能:1. 客户端和FTP 服务器建立Socket 连接。
2. 向服务器发送USER、PASS 命令登录FTP 服务器。
3. 使用PASV 命令得到服务器监听的端口号,建立数据连接。
4. 使用RETR/STOR 命令下载/上传文件。
5. 在下载完毕后断开数据连接并发送QUIT 命令退出。
本例中使用的FTP 服务器为filezilla。
在整个交互的过程中,控制连接始终处于连接的状态,数据连接在每传输一个文件时先打开,后关闭。
客户端和FTP 服务器建立Socket 连接当客户端与服务器建立连接后,服务器会返回220 的响应码和一些欢迎信息。
图1. 客户端连接到服务器端清单1. 客户端连接到FTP 服务器,接收欢迎信息SOCKET control_sock;struct hostent *hp;struct sockaddr_in server;memset(&server, 0, sizeof(struct sockaddr_in));/* 初始化socket */control_sock = socket(AF_INET, SOCK_STREAM, 0);hp = gethostbyname(server_name);memcpy(&server.sin_addr, hp->h_addr, hp->h_length);(not logged in) (127.0.0.1)> USER gaoleyi(not logged in) (127.0.0.1)> 331 Password required for gaoleyi(not logged in) (127.0.0.1)> PASS *********gaoleyi (127.0.0.1)> 230 Logged ongaoleyi (127.0.0.1)> PWDgaoleyi (127.0.0.1)> 257 "/" is current directory.gaoleyi (127.0.0.1)> SIZE file.txtgaoleyi (127.0.0.1)> 213 4096gaoleyi (127.0.0.1)> PASVgaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67)gaoleyi (127.0.0.1)> RETR file.txtgaoleyi (127.0.0.1)> 150 Connection acceptedgaoleyi (127.0.0.1)> 226 Transfer OKgaoleyi (127.0.0.1)> QUITgaoleyi (127.0.0.1)> 221 Goodbye首先,服务器准备就绪后返回220。
客户端接收到服务器端返回的响应码后,相继发送“USER username” 和“PASS password” 命令登录。
随后,服务器返回的响应码为230 开头,说明客户端已经登入了。
这时,客户端发送PASV 命令让服务器进入被动模式。
服务器返回如“227 Entering Passive Mode (127,0,0,1,13,67)”,客户端从中得到端口号,然后连接到服务器的数据端口。
接下来,客户端发送下载命令,服务器会返回响应码150,并从数据端口发送数据。
最后,服务器返回“226 transfer complete”,表明数据传输完成。