大并发服务器不得不说的技术--connect 异步

本文探讨了服务器间的异步连接处理方式,重点介绍了如何通过设置socket为非阻塞模式,并利用epoll进行事件通知,实现高效的跨服务器访问。

前面我们谈的大多是服务端与客户端的技术,服务器开发其实有时还会涉及到跨服务器的访问,比如腾讯的拍拍服务器需要知道登录的会员信息,

就需要访问会员服务器。

跨务器访问会涉及到很多的技术,比如访问权限控制,数据同步等,这里主要来学习一下传输层。

为了更容易理解,我们将访问端服务器称为客户端,被访问端服务器称为服务端。

客户端发起一个连接的过程:

socket_fd = socket( AF_INET,SOCK_STREAM,0 );
ret = connect( socket_fd, (sockaddr*)&addr, sizeof(addr) );


问题就出在第二步,connect 如果是同步的,如果服务端很忙,客户端就会一直阻塞在这里,最多有可能会耗上几十秒,开玩笑,不会吧,客户端也是个服务器唉,

敢情这块不就成了瓶颈吗。

我们来理清下思路:

就像你跟人家姑娘表白了,姑娘说今天不告诉你答案,明天打电话再告诉你,你是不是要一直不吃不喝在她家楼下等呢,还是回去好好休息等她电话。

够明白了么,我建议还是回家休息去吧,因为你还有很多很重要的事做呢。

同样的思路,客户端需要想办法把这个差事放进某个通知队列中。

解决这个问题就是在connect 前先要将 socket_fd 设为异步的(nonblocking):

 

fcntl(socket_fd, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);


连接的时候如果暂时连不上,connect 返回-1,好家伙,人家姑娘害羞呢,但又不表示拒绝。

那行吧,在家等电话,先要把人家姑娘号码存起来吧。

在咱们这里就是将 socket_fd 放到通知队列中,以epoll 为例, 

struct epoll_event ee;
ee.events = EPOLLOUT | EPOLLET;
ee.data.fd = socket_fd;
epoll_ctl( epfd,  EPOLL_CTL_ADD, socket_fd,  &ee );


现在该干什么干什么去吧。

电话来了, 再做相应处理,应该高兴还是悲伤,表情函数预备下,免得到时大脑真空。

不同的是如果姑娘告诉你她答应你了事情就算完了,而epoll 回调告诉你有事件了你还需要确认一下:

getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if( !error )
{
//OK
}


或者也可以使用 getsockname或getpeername,确认OK了才表示连接成功了。

<think>好的,我现在需要帮助用户实现基于原始socket的FTP客户端和服务器端程序,支持基本命令和图片上传下载。首先,我得理清楚用户的需求。用户提到要使用原始socket,也就是需要自己处理TCP连接,而不是使用现成的FTP库。这意味着需要手动实现FTP协议的部分功能,比如命令的发送和接收,以及文件传输的数据通道。 首先,FTP协议通常使用两个通道:控制连接和数据连接。控制连接用于发送命令和接收响应,通常是端口21;数据连接用于传输文件,可以是主动模式或被动模式。用户需要支持基本命令如get、put、pwd、dir、cd,这些命令在FTP协议中有对应的指令,比如RETR、STOR、XPWD、LIST、CWD等。因此,服务器和客户端需要解析这些命令并执行相应的操作。接下来,图片上传下载涉及到二进制传输模式。FTP在传输文件时有ASCII和二进制模式的区别,图片必须使用二进制模式,否则会损坏。所以在实现文件传输时,需要设置正确的传输类型,即TYPE I命令。然后,考虑如何用原始socket实现。服务器端需要监听控制连接的端口,接受客户端连接,然后处理客户端发来的命令。对于数据连接,如果是被动模式,服务器会监听一个端口,并告知客户端该端口,让客户端连接。主动模式则是客户端监听端口,服务器主动连接,但可能因为防火墙问题,被动模式更常用。 对于客户端,需要建立控制连接到服务器的21端口,然后根据用户输入的命令,发送相应的FTP指令。例如,当用户输入putimage.jpg时,客户端需要发送STOR命令,并建立数据连接传输文件数据。同样,下载文件需要发送RETR命令,接收数据。现在需要考虑如何管理数据连接。每次传输文件时都需要建立新的数据连接,这可能涉及到端口的选择和管理。例如,在被动模式下,服务器会返回一个IP和端口,客户端需要连接到该端口进行数据传输。代码结构方面,服务器端可能需要多线程处理多个客户端,每个客户端连接在一个独立的线程中处理命令。客户端则需要循环接收用户输入,解析命令并发送对应的FTP指令。 另外,图片的上传下载需要注意二进制数据的正确读写。在发送文件时,需要以二进制模式读取文件内容,并通过数据连接发送。接收时同样需要以二进制模式写入本地文件。测试时可能会遇到防火墙或端口阻塞的问题,需要确保服务器和客户端使用的端口在防火墙中是开放的,或者使用被动模式来避免客户端防火墙的问题。 现在,需要将上述思路转化为具体的步骤,包括socket编程中的创建、绑定、监听、接受连接,以及命令的解析和响应处理。同时,要处理并发连接,可能需要使用多线程或异步IO。可能遇到的问题包括:正确处理数据连接的建立,确保命令和数据的同步,处理大文件的分块传输,以及错误处理,比如文件不存在的情况。此外,编码时需要确保发送和接收的数据正确解析,特别是对于不同的操作系统,路径分隔符可能不同,需要统一处理。最后,根据用户提供的引用,特别是引用[2]和[3],FTP的实现需要遵循协议规范,确保命令和响应的格式正确。例如,每个FTP命令以CRLF结尾,响应码符合标准,如200表示成功,550表示文件不可用等。 总结下来,实现步骤大致分为:搭建控制连接,实现命令解析和处理,管理数据连接,处理文件传输的二进制模式,以及错误处理和并发支持。接下来需要将这些步骤详细写出,并给出示例代码框架,帮助用户开始编写代码。</think>### 分步骤实现FTP客户端/服务器程序 #### 一、基础架构设计 1. **控制连接**:使用TCP协议,默认端口21(服务器监听) 2. **数据连接**:支持主动模式(PORT)和被动模式(PASV)[^2] 3. **命令规范**:每个命令以`\r\n`结尾,响应格式为`状态码+描述`[^3] #### 二、服务器端实现步骤 ```python # 伪代码示例(需补充协议处理逻辑) import socket import threading class FTPServer: def __init__(self): self.control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.control_socket.bind(('0.0.0.0', 21)) self.control_socket.listen(5) def handle_client(self, client_sock): client_sock.send(b"220 Welcome to My FTP Server\r\n") while True: cmd = client_sock.recv(1024).decode().strip() if cmd.startswith("USER"): client_sock.send(b"230 Login successful\r\n") elif cmd.startswith("RETR"): # 下载文件 filename = cmd.split()[1] # 建立数据连接传输文件 elif cmd.startswith("STOR"): # 上传文件 filename = cmd.split()[1] # 接收数据连接的文件内容 # 启动线程处理多客户端 while True: client, addr = self.control_socket.accept() threading.Thread(target=self.handle_client, args=(client,)).start() ``` #### 三、客户端实现关键点 1. **命令交互流程**: - 建立控制连接 - 发送`USER`/`PASS`进行认证 - 发送`TYPE I`设置二进制模式(图片传输必须使用) 2. **文件传输示例(下载)**: ```python def download_file(filename): # 发送PASV命令获取数据端口 control_sock.send(b"PASV\r\n") response = parse_pasv_response(control_sock.recv(1024)) # 解析返回的IP和端口 # 建立数据连接 data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) data_sock.connect((response.ip, response.port)) # 发送RETR命令 control_sock.send(f"RETR {filename}\r\n".encode()) with open(filename, 'wb') as f: while True: chunk = data_sock.recv(4096) if not chunk: break f.write(chunk) ``` #### 四、图片传输特殊处理 1. 必须使用`TYPE I`命令设置二进制模式 2. 传输完成后校验文件哈希值 3. 限制最大传输文件尺寸(防止内存溢出) #### 五、协议增强建议 1. 实现`ABOR`命令中断传输 2. 支持`REST`断点续传 3. 添加`MLSD`现代目录列表格式
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值