收藏 分销(赏)

利用Delphi编写Socket通信程序.doc

上传人:二*** 文档编号:4742743 上传时间:2024-10-11 格式:DOC 页数:36 大小:292KB 下载积分:5 金币
下载 相关 举报
利用Delphi编写Socket通信程序.doc_第1页
第1页 / 共36页
本文档共36页,全文阅读请下载到手机保存,查看更方便
资源描述
Indy10Install All packages are followed by X0 (Where X is your Delphi verison). 1. Download source from the Development Snapshot. Delphi / BCB Installation 1. Open and compile in the following order: 2. 1. IndySystem (in Lib\System) 2. IndyCore (in Lib\Core) 3. IndyProtocols (in Lib\Protocols) 4. IndySuperCore (in Lib\SuperCore) If you are not using SuperCore, then you do not need to compile this package. 3. Now open and click install in the following order 4. 5. 1. dclIndyCore (in Lib\Core) 2. dclIndyProtocols (in Lib\Protocols) 3. dclSuperCore (in Lib\SuperCore) Only install this if you have compiled SuperCore. 利用Delphi编写Socket通信程序 2004-08-24 人气:5925 出处:csdn 作者: dudunono [原作] 一、Delphi与Socket   计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协议。TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登陆BBS,用的 就是TCP协议;UDP是无连接的,通信双方都不保持对方的状态,浏览器访问Internet时使用的HTTP协议就是基于UDP协议的。TCP和UDP协议都非常复杂,尤其是TCP协议,为了保证网络传输的 正确性和有效性,必须进行一系列复杂的纠错和排序等处理。   Socket是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范,最初是由美国加州Berkley大学提出,它定义两台计算机间进行通信的规范(也是一种编程规范),如果说两台计算 机是利用一个“通道“进行通信,那么这个“通道“的两端就是两个套接字。套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的计 算机之间的通信成为可能。   微软的Windows Socket规范(简称winsock)对Berkley的套接字规范进行了扩展,利用标准的Socket的方法,可以同任何平台上的Socket进行通信;利用其扩展,可以更有效地实现在 Windows平台上计算机间的通信。在Delphi中,其底层的Socket也应该是Windows的Socket。Socket减轻了编写计算机间通信软件的难度,但总的说来还是相当复杂的(这一点在后面具体会讲 到);Inprise在Delphi中对Windows Socket进行了有效的封装,使得用户可以很方便地编写网络通信程序。下面我们实例解读在Delphi中如何利用Socket编写通信程序。 二、利用Delphi编写Socket通信程序。   下面是一个简单的Socket通信程序,其中客户机和服务机是同一个程序,当客户机(服务器)在一个memo1中输入一段文字然后敲入回车,该段文字就可以显示在服务器(客户机)的memo2 中,反之亦成立。具体步骤如下:   1、新建一个form,任意命名,不妨设之为chatForm;放上一个MainMenu(在Standard栏中),建立ListenItem、ConnectItem、Disconnect和Exit菜单项;在从Internet栏中选择 TServerSocket、TClientSocket添加到chatForm中,其中把TClientSocket的名字设为ClientSocket, port设为1025,默认的active为false;把TServerSocket的名字设为ServerSocket, port设为1025,默认的active为false,其他的不变;再放入两个memo,一个命名为memo1,另外一个命名为memo2,其中把memo2的color设置为灰色,因为主要用来显示对方的输入。下面我们 一边编写代码一边解释原因。   2、双击ListemItem。写入如下代码: procedure TChatForm.ListenItemClick(Sender: TObject); begin ListenItem.Checked := not ListenItem.Checked; if ListenItem.Checked then begin ClientSocket.Active := False; ServerSocket.Active := True; end else begin if ServerSocket.Active then ServerSocket.Active := False; end; end;   该程序段的说明如下:当用户选择ListemItem时,该ListenItem取反,如果选中的话,说明处于Listen状态,读者要了解的是:listen是Socket作为Server时一个专有的方法,如果处于 listen,则ServerSocket设置为活动状态;否则,取消listen,则关闭ServerSocket。实际上,只有用户一开始选择该菜单项,表明该程序用作Server。反之,如果用户选择ConnectItem,则 必然作为Client使用。   3、双击ConnectItem,敲入以下代码。 procedure TChatForm.ConnectItemClick(Sender: TObject); begin if ClientSocket.Active then ClientSocket.Active := False; if InputQuery('Computer to connect to', 'Address Name:', Server) then if Length(Server) > 0 then with ClientSocket do begin Host := Server; Active := True; ListenItem.Checked := False; end; end;   这段程序的主要功能就是当用户选择ConnectItem菜单项时,设置应用程序为客户机,弹出input框,让用户输入服务器的地址。这也就是我们不一开始固定ClientSocket的host的原因, 这样用户可以动态地连接不同的服务器。读者需要了解的是主机地址只是Socket作为客户机时具有的一个属性,Socket作为服务器时“一般“不用地址,因为它同本机绑定。   4、在memo1的keydown方法中写入如下代码: procedure TChatForm.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_Return then if IsServer then ServerSocket.Socket.Connections[0].SendText(Memo1.Lines[Memo1.Lines.Count - 1]) else ClientSocket.Socket.SendText(Memo1.Lines[Memo1.Lines.Count - 1]); end;   该段代码的作用很明显,就是开始发消息了。其中如果是Server的话,它只向第一个客户机发消息,由于一个服务器可以连接多个客户机,而同客户机的每一个连接都由一个Socket来维 持,因此ServerSocket.Socket.Connnections数组中存储的就是同Client维持连接的Socket。在标准Socket中,服务器方的Socket通过accept()方法的返回值获取维持同客户机连接的 Socket,而发送、接受消息的方法分别为send(sendto)和recv(recvfrom), Delphi对此进行了封装。   5、其余代码的简要介绍。 procedure TChatForm.ServerSocketAccept(Sender: TObject; Socket: TCustomWinSocket); begin IsServer := True; end;   ServerSocket的Accept方法,当客户机第一次连接时完成,通过其参数可以认为,它是在标准的accept方法后执行的,因为有TCustomWinSocket这个参数类型,它应该是标准Server方 Socket的返回值。 procedure TChatForm.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Add(Socket.ReceiveText); end; procedure TChatForm.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Add(Socket.ReceiveText); end;   这两段代码分别是服务器方和客户机方在收到对方的消息时,由Delphi触发的,作用是在memo2中显示收到的消息。其中,ClientSocketRead中的Socket实际上就是Socket本身,而在 ServerSocketClientRead中的Socket实际上是ServerSocket.Socket.Connection[]中的某个Socket。不过在Delphi中,对服务器方的Socket进行了有效的封装。 procedure TChatForm.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin Memo2.Lines.Clear; end; procedure TChatForm.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin ListenItemClick(nil); end;   这两段比较简单。其中ServerSocketClientConnect在ServerSocket收到一个新的连接时触发。而ClientSocketDisconnect在ClientSocket发出Disconncet时触发。 procedure TChatForm.Exit1Click(Sender: TObject); begin ServerSocket.Close; ClientSocket.Close; Close; end; procedure TChatForm.Disconnect1Click(Sender: TObject); begin ClientSocket.Active := False; ServerSocket.Active := True; end;   第一段为关闭应用程序。在标准Socket中,每个Socket在关闭时,必须调用closesocket()方法,否则系统不会释放资源。而在ServerSockt.Close和ClientSocket.Close中,系统内部肯 定调用了closesocket()方法。 三、标准Socket与Delphi中的Socket。 标准的Socket的应用程序框架如下: Server方: Socket()[ 新建一个Socket]--Bind()[ 同服务器地址邦定 ]--Listen() --Accept()--block wait--read()[接受消息,在windows平台中,方法为send(TCP),或者是 sendto(UDP)]--处理服务请求--Write()[发送消息,在windows平台中,方法为send(TCP), 或者为sendto(UDP)。 Client方相对简单:Socket()--Connect()[通过一定的port连接特定的服务器,这是与服务器建立连接]--Write()--Read()。   Socket可以是基于TCP的,也可以是基于UDP,同时Socket甚至建立在其他的协议,比如IPX/SPX,DECNet等。在新建一个Socket时,可以指定新建何类Socket。Bind()用来同服务器的地址 邦定,如果一个主机只有一个IP地址,实际上邦定的作用就相对多余了。Listen()开始监听网络,Accept()用于接受连接,其返回值是保持同客户机联系的Socket。   在Delphi中,对于Windows中的Socket进行了有效的封装。在Delphi中,按其继承关系,可以分层两类: 一、TComponent--TAbstractSocket--TCustomSocket--TCustomServerSocket--TServerSocket TComponent--TAbstractSocket--TCustomSocket--TClientSocket 二、直接从TObject继承过来: TObject--TCustomWinSocket--TServerWinSocket TObject--TCustomWinSocket--TClientWinSocket TObject--TCustomWinSocket--TServerClientWinSocket   可以看出第一类建立在TCustomSocket基础上,第二类建立在TCustomWinSocket的基础上。第一类建立在TComponet的基础上,第二类直接构建在TObject基础上。因此如果用户非常熟悉 Socket并且想要编写控制台程序时,可以使用TCustomWinScoket类。   同uses中可以看出,它们都在ScktComp.pas中实现,而在schtComp.pas中,则包含了winsock.pas文件,如果继续深入winsock文件,在其中可以发现所有的Windows Socket的基本方法。   实际上,如果你了解了标准Socket的应用程序框架,对于使用Delphi编写Socket应用程序也就得心应手了;这不是说你必须了解复杂的Socket中的标准函数,也没有必要,因为Delphi已 经为你做了很好的封装了,这也正是Delphi的强势所在,你只要了解那么一点点的基本框架。   这是我对Delphi中的Socket应用的理解,不足之处希望大家指正。同时也乐于为大家解答Delphi中有关Socket的问题。 用Indy组件开发Socket应用程序 创建时间:2004-3-16 文章属性:转载 文章提交:绝对零度 今日浏览:23 总共次数:648 文章作者:虚度 文章出处:yesky   笔者在前一段的工作中,需要开发一套简单的网络数据传输程序。由于平时常用Delphi做点开发,故此次也不例外。Delphi 7中带有两套TCP Socket组件:Indy Socket组件 (IdTCPClient和IdTCPServer)和Delphi原生的TCP Socket组件(ClientSocket和ServerSocket)。但是,Borland已宣称ClientSocket和ServerSocket组件即将被废弃,建议用相应的Indy 组件来代替。因此,笔者使用了Indy。本文在对Indy进行简要介绍的基础上,创建了一组简单的TCP Socket数据传输应用来演示了Indy的使用方法。   开放源代码的Internet组件集——Internet Direct(Indy)Internet Direct(Indy)是一组开放源代码的Internet组件,涵盖了几乎所有流行的Internet协议。Indy用Delphi编写,被 包含在Delphi 6,Kylix 1和C++ Builder 6及以上各个版本的Borland开发环境中。Indy曾经叫做WinShoes(双关于WinSock——Windows的Socket库),是由Chad Z. Hower领导的一群开发者 构建的,可以从Indy的站 10也进入了Beta测试阶段。   Delphi 7中所带的是Indy 9。在其的组件面板上,一共安装有100多个Indy组件。使用这些组件你可以开发基于各种协议的TCP客户和服务器应用程序,并处理相关的编码和安全问题。你 可以通过前缀Id来识别Indy组件。   Indy是阻塞式(Blocking)的   当你使用Winsock开发网络应用程序时,从Socket中读取数据或者向Socket写入数据都是异步发生的,这样就不会阻断程序中其它代码的执行。在收到数据时,Winsock会向应用程序发送 相应的消息。这种访问方式被称作非阻塞式连接,它要求你对事件作出响应,设置状态机,并通常还需要一个等待循环。   与通常的Winsock编程方法不同的是,Indy使用了阻塞式Socket调用方式。阻塞式访问更像是文件存取。当你读取数据,或是写入数据时,读取和写入函数将一直等到相应的操作完成后 才返回。比如说,发起网络连接只需调用Connect方法并等待它返回,如果该方法执行成功,在结束时就直接返回,如果未能成功执行,则会抛出相应的异常。同文件访问不同的是,Socket 调用可能会需要更长的时间,因为要读写的数据可能不会立即就能准备好(在很大程度上依赖于网络带宽)。   阻塞式Socket并非恶魔(Evil)   长期以来,阻塞式Socket都遭到了毫无理由的攻击。其实阻塞式Socket并非如通常所说的那样可怕。这还要从Winsock的发展说起。   当Socket被从Unix移植到Windows时,一个严重的问题立即就出现了。Unix支持fork,客户程序和服务器都能够fork新的进程,并启动这些进程,从而能够很方便地使用阻塞式Socket。 而Windows 3.x既不支持fork也不支持多线程,当使用阻塞式Socket时,用户界面就会被“锁住”而无法响应用户输入。   为克服Windows 3.x的这一缺陷,微软在Winsock中加入了异步扩展,以使Winsock不会“锁住”应用程序的主线程(也是唯一的线程)。然而,这需要了一种完全不同的编程方式。于是 有些人为了掩饰这一弱点,就开始强烈地诽谤阻塞式Socket。   当Win32出现的时候,它能够很好地支持线程。但是既成的观念已经很难更改,并且说出去的话也无法收回,因此对阻塞式Socket的诽谤继续存在着。   事实上,阻塞式Socket仍然是Unix实现Socket的唯一方式,并且它工作得很好。   阻塞式Socket的优点   归结起来,在Windows上使用阻塞式Socket开发应用程序具有如下优点:   编程简单——阻塞式Socket应用程序很容易编写。所有的用户代码都写在同一个地方,并且顺序执行。 容易向Unix移植——由于Unix也使用阻塞式Socket,编写可移植的代码就变得比较容易。Indy就是利用这一点来实现其多平台支持而又单一源代码的设计。   很好地利用了线程技术——阻塞式Socket是顺序执行的,其固有的封装特性使得它能够很容易地使用到线程中。   阻塞式Socket的缺点   事物都具有两面性,阻塞式Socket也不例外。它的一个主要的缺点就是使客户程序的用户界面“冻结”。当在程序的主线程中进行阻塞式Socket调用时,由于要等待Socket调用完成并返 回,这段时间就不能处理用户界面消息,使得Update、Repaint以及其它消息得不到及时响应,从而导致用户界面被“冻结”。   使用TIdAntiFreeze对抗“冻结”   Indy使用一个特殊的组件TIdAntiFreeze来透明地解决客户程序用户界面“冻结”的问题。TIdAntiFreeze在Indy内部定时中断对栈的调用,并在中断期间调用 Application.ProcessMessages方法处理消息,而外部的Indy调用继续保存阻塞状态,就好像TIdAntiFreeze对象不存在一样。你只要在程序中的任意地方添加一个TIdAntiFreeze对象,就能 在客户程序中利用到阻塞式Socket的所有优点而避开它的一些显著缺点。   Indy使用了线程技术   阻塞式Socekt通常都采用线程技术,Indy也是如此。从最底层开始,Indy的设计都是线程化的。因此用Indy创建服务器和客户程序跟在Unix下十分相似,并且Delphi的快速开发环境和 Indy对WinSock的良好封装使得应用程序创建更加容易。   Indy服务器模型   一个典型的Unix服务器有一个或多个监听进程,它们不停地监听进入的客户连接请求。对于每一个需要服务的客户,都fork一个新进程来处理该客户的所有事务。这样一个进程只处理一 个客户连接,编程就变得十分容易。   Indy服务器工作原理同Unix服务器十分类似,只是Windows不像Unix那样支持fork,而是支持线程,因此Indy服务器为每一个客户连接分配一个线程。   图1显示了Indy服务器的工作原理。Indy服务器组件创建一个同应用程序主线程分离的监听线程来监听客户连接请求,对于接受的每一个客户,都创建一个新的线程来为该客户提供服务 ,所有与这一客户相关的事务都由该线程来处理。   使用组件TIdThreadMgrPool,Indy还支持线程池。 上一页 1 2 3 4 下一页   线程与Indy客户程序   Indy客户端组件并未使用线程。但是在一些高级的客户程序中,程序员可以在自定义的线程中使用Indy客户端组件,以使用户界面更加友好。   简单的Indy应用示例   下面将创建一个简单的TCP客户程序和一个简单的TCP服务器来演示Indy的基本使用方法。客户程序使用TCP协议同服务器连接,并向服务器发送用户所输入数据。服务器支持两条命令: DATA和QUIT。在DATA命令后跟随要发送的数据,并用空格将命令字DATA和数据分隔开。   表单布局   建立一个项目组,添加一个客户程序项目和一个服务器项目。客户程序和服务器程序的表单布局如同2和图3所示。客户程序表单上放置了TIdTCPClient组件,服务器程序表单上放置了 TIdTCPServer组件。为防止客户程序“冻结”,还在其表单上放置TIdAntiFreeze组件。   客户程序和服务器程序的表单上都放置有TListBox组件,用来显示通信记录。   客户程序代码   客户程序片断如代码列表1所示。   代码列表1 procedure TFormMain.BtnConnectClick(Sender: TObject); begin IdTCPClient.Host := EdtHost.Text; IdTCPClient.Port := StrToInt(EdtPort.Text); LbLog.Items.Add('正在连接 ' + EdtHost.Text + '...'); with IdTCPClient do begin try Connect(5000); try LbLog.Items.Add(ReadLn()); BtnConnect.Enabled := False; BtnSend.Enabled := True; BtnDisconnect.Enabled := True; except LbLog.Items.Add('远程主机无响应!'); IdTCPClient.Disconnect(); end;//end try except LbLog.Items.Add('无法建立到' + EdtHost.Text + '的连接!'); end;//end try end;//end with end; procedure TFormMain.BtnSendClick(Sender: TObject); begin LbLog.Items.Add('DATA ' + EdtData.Text); with IdTCPClient do begin try WriteLn('DATA ' + EdtData.Text); LbLog.Items.Add(ReadLn()) except LbLog.Items.Add('发送数据失败!'); IdTCPClient.Disconnect(); LbLog.Items.Add('同主机 ' + EdtHost.Text + ' 的连接已断开!'); BtnConnect.Enabled := True; BtnSend.Enabled := False; BtnDisconnect.Enabled := False; end;//end try end;//end with end; procedure TFormMain.BtnDisconnectClick(Sender: TObject); var Received: string; begin LbLog.Items.Add('QUIT'); try IdTCPClient.WriteLn('QUIT'); finally IdTCPClient.Disconnect(); LbLog.Items.Add('同主机 ' + EdtHost.Text + ' 的连接已断开!'); BtnConnect.Enabled := True; BtnSend.Enabled := False; BtnDisconnect.Enabled := False; end;//end try end;   在“连接”按钮事件响应过程中,首先根据用户输入设置IdTCPClient的主机和端口,并调用IdTCPClient的Connect方法向服务器发出连接请求。然后调用ReadLn方法读取服务器应答数 据。   在“发送”按钮事件响应过程中,调用WriteLn方法写DATA命令,向服务器发送数据。   在“断开”按钮事件响应过程中,向服务器发送QUIT命令,并调用Disconnect方法断开连接。   程序中还包含有通信信息记录和异常处理的代码。   服务器程序代码   服务器程序片断如代码列表2所示。   代码列表2 procedure TFormMain.BtnStartClick(Sender: TObject); begin IdTCPServer.DefaultPort := StrToInt(EdtPort.Text); IdTCPServer.Active := True; BtnStart.Enabled := False; BtnStop.Enabled := True; LbLog.Items.Add('服务器已成功启动!'); end; procedure TFormMain.BtnStopClick(Sender: TObject); begin IdTCPServer.Active := False; BtnStart.Enabled := True; BtnStop.Enabled := False; LbLog.Items.Add('服务器已成功停止!'); end; procedure TFormMain.IdTCPServerConnect(AThread: TIdPeerThread); begin LbLog.Items.Add('来自主机 ' + AThread.Connection.Socket.Binding.PeerIP + ' 的连接请求已被接纳!'); AThread.Connection.WriteLn('100: 欢迎连接到简单TCP服务器!'); end; procedure TFormMain.IdTCPServerExecute(AThread: TIdPeerThread); var sCommand: string; begin with AThread.Connection do begin sCommand := ReadLn(); FLogEntry := sCommand + ' 来自于主机 ' + AThread.Connection.Socket.Binding.PeerIP; AThread.Synchronize(AddLogEntry); if AnsiStartsText('DATA ', sCommand) then begin FReceived := RightStr(sCommand, Length(sCommand)-5); WriteLn('200: 数据接收成功!'); AThread.Synchronize(DisplayData); end else if SameText(sCommand, 'QUIT') then begin FLogEntry := '断开同主机 ' + AThread.Connection.Socket.Binding.PeerIP + ' 的连接!'; AThread.Synchronize(AddLogEntry); Disconnect; end else begin WriteLn('500: 无法识别的命令!'); FLogEntry := '无法识别命令:' + sCommand; AThread.Synchronize(AddLogEntry); end;//endif end; end; procedure TFormMain.DisplayData(); begin EdtData.Text := FReceived; end; procedure TFormMain.AddLogEntry(); begin LbLog.Items.Add(FLogEntry); end;   “启动”按钮设置IdTCPServer 的Active属性为True来启动服务器,“停止”按钮设置Active属性为False来关闭服务器。   IdTCPServerConnect方法作为IdTCPServer 的OnCorrect事件响应过程,向客户端发送欢迎信息。OnCorrect事件在一个客户连接请求被接受时发生,为该连接创建的线程AThread被作为 参数传递给IdTCPServerConnect方法。   IdTCPServerExecute方法是IdTCPServer 的OnExecute事件响应过程。OnExecute事件在TIdPeerThread对象试图执行其Run方法时发生。OnExecute事件与通常的事件有所不同,其响应过 程是在某个线程上下文中执行的,参数AThread就是调用它的线程。这一点很重要,它意味着可能有多个OnExecute事件响应过程被同时执行。在连接被断开或中断前,OnExecute事件响应过 程会被反复执行。   在IdTCPServerExecute方法中,首先读入一条指令,然后对指令进行判别。如果是DATA指令,就解出数据并显示它。如果收到的是QUIT指令,则断开连接。需要特别指出的是,由于 IdTCPServerExecute方法在某一线程上下文中执行,因此显示数据和添加事件记录都是将相应的方法传递给Synchronize调用来完成的。   运行程序   运行客户端和服务器程序,按如下流程进行操作:   1. 按服务器程序的“启动”按钮启动服务器;   2. 按客户程序的“连接”按钮,建立同服务器的连接;   3. 在客户程序的待发送数据编辑框中输入“Hello, Indy!”,并按“发送”按钮发送数据;   4. 按客户程序的“断开”按钮,断开同服务器的连接;   5. 按服务器程序的“停止”按钮停止服务器。 用Socket接收和转换数字和字符串数据【大 中 小】【打印】【加入收藏】【关闭】浏览字号:日期:2005-06-13 人气:60 出处: 作者: 7nightlove 很多时候远程系统在执行并发任务的时候,会把它接收到数据的长度以数字的形式发送出去。但用socket发送和接收数字型数据的时候,要考虑到一个问题:要根据网络另一端机器的类型转换数据。尤其需要知道怎样把要发送的数据格式(网络格式)从本地机器的格式(主机格式)转换成为行业标准格式。 使用IPAddress.NetworkToHostOrder可以把数据从网络规则转换为主机格式,下面的ReceiveHeader函数说明了它的用法,ReceiveHeader函数实现过程如下: 1 用Socket.Receive从远程机器接收数据。 2 验证接收到的字节数是4。 3 Socket.Receive返回一个字节型数组,BitConvert.ToInt32把它转换成数字型数值。 4 最后,IPAddress.NetworkToHostOrder把长数值转换为主机格式。 public int ReceiveHeader(Socket socket) { int dataSize = -1; // error byte [] buffer = new byte[4]; int bytesRead = socket.Receive(buffer, 4, System.Net.Sockets.SocketFlags.None); if (4 == bytesRead) { dataSize = BitConverter.ToInt32(buffer, 0); dataSize = IPA
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服