1、网络编程基础网络聊天室n范例演示n需要解决的问题1)如何与对方机器进行连接2)如何找到对应的应用程序3)对收到的信息怎样实现是广播还是一对一?网络编程基础知识解决问题1和2nInternet上互相通信的计算机采用的协议是TCP协议或者UDP协议,它们的结构类似于:应用层(HTTP FTP TELNET.)传输层(TCP UDP.)网络层(IP.)数据链路层网络编程基础知识解决问题1和2nTCP是传输控制协议,也称为“基于数据流的套接字”,根据该协议的设计宗旨,它具有高度的可靠性,而且能保证数据顺利到达目的地。换言之,它允许重传那些由于各种原因半路“走失”的数据。而且收到字节的顺序与它们发出的顺
2、序是一样的。TCP的高可靠性需要付出的代价是:高开销(需要有很多的信息用于控制信息)。n而UDP称为用户数据报协议,它并不刻意追求数据报会完全发送出去,也不能担保抵达的顺序与它们发出时一样。因此,UDP被认为是一种不可靠协议。但是,它的速度快,对于某些应用来说(例如声音),如果速度并质量更重要,就可以采用UDP协议。大多数互联网游戏也是采用UDP协议。网络编程基础知识解决问题1和2n机器标识机器标识 n为了分辨出网络上的每一台机器,必须有一种机制能独一无二地标识出网络内的每台机器。这可以通过IP地址来实现。IP地址以两种形式存在:n直接IP地址(如10.1.2.5)n域名(http:/)。网络
3、编程基础知识解决问题1和2n服务器和客户机服务器和客户机 n网络最基本的精神是让两台计算机连接在一起,并相互“沟通”。一旦两台计算机发现了对方,就可以开始沟通,但它们怎样才能“发现”对方呢?n两台计算机要发现对方,通常需要其中一台扮演“服务器”的角色,另一台扮演“客户机”的角色。n客户机用来发出连接请求,而服务器用来等待连接请求。网络编程基础知识解决问题1和2n客户机发出连接请求到服务器(通过IP地址),请求信息在网络上传输,当服务器在接到连接请求并确认后,建立与客户机的连接。n一旦连接建立好,服务器和客户机之间就变成了一种双向通信,那么无论是对服务器端还是对客户机端来说,连接就变成了一个IO
4、数据流对象。从这是开始,我们就可以象读写一个普通的文件一样来对待连接。网络编程基础知识解决问题1和2n端口端口 n有些时候,一个IP地址并不足以完整标识一个服务器。这是由于在一台计算机中,往往运行着多个服务器(即不同的网络应用程序)。为了标识是哪个服务器,就需要用到一个端口。例如,通常来说HTTP采用的是80端口,FTP采用的是21端口。n端口并不是机器上一个物理上存在的场所,而是一种软件抽象(主要是为了表达的方便)。客户程序知道如何通过机器的IP地址同服务器连接,但怎样才能同自己真正需要的那种服务连接呢(一般每个端口都运行着一种服务,一台机器可能提供了多种服务,例如HTTP、FTP)?端口在
5、这里扮演了重要的角色,它是必需的一种二级定址措施。也就是说,我们请求一个特定的端口,就是请求与那个端口编号关联的服务。n系统保留了使用端口1到端口1024的权利,所以,在我们设计网络通信程序时,一般不应站用这些端口。网络编程基础知识解决问题1和2n套接字套接字 Socket套接字也是一种软件形式的抽象。用于表达两台机器间一个连接的“通道”。针对一个特定的连接,每台机器上都有一个“套接字”,通过“套接字”,两台机器之间就形成了一条“虚拟”的通道。Java的网络编程类的网络编程类nJDK提供了1个包,在该包中,主要包含如下的几个类:1Java中IP地址的表示(InetAddress类)包中提供了一
6、个InetAddress类,该类用于表示一个IP地址。它常用的方法有:(1)public static InetAddress getByName(Stringhost)返回字符串host所表示的IP地址。(2)public static InetAddress getLocalHost()返回本机IP地址(如果你的机器设置了IP地址,则返回你自己的IP,否则返回默认的IP地址:127.0.0.1)。Java的网络编程类的网络编程类2服务器端口打开(ServerSocket类)ServerSocket类用于在服务器端打开某一个端口,等待客户端的连接请求,它常用的方法有:(1)构造器方法publ
7、ic ServerSocket(intport)用于打开服务器port端口。(2)public Socket accept()用于等待客户连接,当连接成功时,形成一个套接字对象。(3)public void close()关闭用户连接。Java的网络编程类的网络编程类3套接字建立(Socket类)Socket类用于套接字的建立。它常用的方法有:(1)构造器方法public Socket(InetAddressaddress,intport)用于客户端与指定的服务器IP地址及服务器端口进行套接字连接。(2)public void close()关闭用户连接。(3)public InputStre
8、am getInputStream()从套接字返回一个输入流(4)public OutputStream getOutputStream()从套接字返回一个输出流服务器的连接过程服务器的连接过程 服务器的工作就是侦听来自客户机的连接请求并建立连接。因此,对于服务器端来说,网络编程的步骤是:(1)创建ServerSocket对象:ServerSocket server=new ServerSocket(PORT),其中,PORT是指服务器打开哪个端口来等待和建立连接。(2)等待连接:Socket socket=server.accept(),连接形成是以套接字来表示的,一旦连接成功,就会在服务器
9、客户机之间形成一个套接字。(3)一旦连接建立好(即服务器客户机套接字建立),我 们 就 可 以 使 用 S o c k e t 类 提 供 的 两 个 方 法getInputStream()和getOutputStream()来作为输入输出设备设备,实现服务器与客户机之间的信息交互。客户端的连接过程客户端的连接过程 客户机的工作就是要向服务器发出连接请求并建立连接。对客户机来说,它需要给出要连接的服务器的IP地址(以便找到该服务器)以及服务器的端口号(以便找到需要连接的服务)。至于客户机到底用哪个端口与服务器的端口建立连接,是客户机上的Java系统决定的。因此,对于客户机来说,网络编程的步骤是
10、:(1)创建InetAddress对象,指定服务器的IP地址:InetAddress addr=InetAddress.getByName(服务器的IP地址)(2)创建与服务器的指定端口的连接:Socket socket=new Socket(addr,PORT)简单聊天系统的实现 n1、演示简单聊天系统-服务器端成员变量设计ServerSocket server=null;Socket socket=null;BufferedReader cin=null;PrintStream cout=null;简单聊天系统-服务器端事件处理1.打开服务器端口2.等待客户连接3.构建输入输出通道4.读入
11、客户端发送的信息简单聊天系统-客户端成员变量设计 Socket socket=null;BufferedReader cin=null;PrintStream cout=null;简单聊天系统-客户端事件处理1.获得服务器IP2.获得服务器端口3.与服务器连接4.构建输入输出通道5.向服务器发送一条信息简单聊天系统-运行与问题n运行程序n发现:无法收发信息。n原因:信息未发送。n解决1:客户端发送按钮简单聊天系统-运行与问题n仍然存在问题n原因:服务器未进行多次接收n解决:对服务器进行10次信息收发简单聊天系统-运行与问题n任意多次信息收发的解决?n假死问题出现?n 必须发送10条信息,才能显
12、示。n 对任意多条,必定永远假死。n 假死问题的原因?独占资源n解决办法:n 多线程。n 演示Java的多线程机制 n我们都很熟悉多任务的概念,通常来说,多任务是指通过CPU时间分片的方式,让几个程序同时运行。既然程序和程序之间可以同时运行,而程序也无非是一些代码,那么,我们可以自然而然地推理出,应该也可以通过CPU时间分片的方式,让一个程序的几段代码同时运行,这就是多线程(Multi Threads)。Java作为一种网络编程语言,支持多线程机制。n上例中出现假死的原因就是因为CPU资源独占的缘故。如果我们能够将造成“假死”的那段代码采用多线程机制来进行,那么就可以让服务器不停读套接字端口的
13、同时,原来的程序照预定方式继续运行。线程的创建线程的创建要在程序中对某些程序片段采用多线程,就必须遵照Java的多线程编程机制。1线程的运行机制 线程实际上是程序中的一条执行路径。而多线程则是指程序包含多条执行路径。这样,在“同一”时间内,程序可以存在多条执行路径,“同步”执行,以提高程序的执行效率。2线程的创建 在Java中,系统提供了一个Thread类,用户通过继承Thread类的方式,可以将需要的程序段以线程的机制来运行。其通常做法是:(1)用户自定义类,该类继承自Thread类。(2)覆盖(Override)Thread类的run方法。在该方法中实现需要与“主”程序“同步”运行的功能。
14、线程的生命周期 n对一个具体的线程对象,在其生命周期内,根据状态的不同,通常可以区分为:n1创建状态(new)n2可运行状态(Runnable)n3阻塞状态(Blocked)n4终止状态线程的实现 n目标:针对上例,我们希望服务器端能多次接收客户端发送的信息,直到接收到“QUIT”信息才终止连接。n解决办法:我们就可以采用线程的机制来实现 n在SingleServer工程的MainFrame类中,添加一个内部类,该内部类的作用是自定义线程类,主要实现服务器端对客户端信息的接收功能。内部线程类将需要循环执行的语句块在线程的run方法中实现服务器端监听按钮事件将实现收发功能的代码用线程来实现简单聊
15、天系统的实现-运行n运行,客户端可以发送多条信息,服务器端显示。简单聊天系统-实战演习 在上例中,我们已经实现服务器端不停地接收信息,请对这两个工程做修改,实现如下功能:(1)服务器在接收到来自客户端的非退出信息时,发送一条“服务器反馈信息XX”信息到客户端。(2)服务器在接收到来自客户端的退出信息时,发送一条“QUIT”信息到客户端。(3)客户端能实时接收服务器发送的信息,当接收到“QUIT”信息时,则关闭套接字连接。简单聊天系统的实现-问题n问题:多个客户端怎样同时与服务器联系?多客户多客户-服务器信息交互系统服务器信息交互系统n上例中之所以只能实现一个客户的连接,是因为服务器端的套接字打
16、开后,只允许了一个客户的连接(serverSocket.accept()只被运行了1次)。要实现多个客户的连接,最显然的方式是让“等待连接连接建立接收信息”程序段重复运行。n因此,我们就可以考虑采用多线程的机制来实现。n演示客户连接线程的实现 n由于针对多用户连接,每个用户与服务器之间都必须形成一条自己的“通道”,以进行信息的“收发”。n因此,在服务器的客户连接线程中,必须建立服务器与每个客户的套接字。在套接字建立后,通过创建收发信息线程,在收发信息线程中实现信息的接收与发送。n为实现信息的接收与发送,就必须在收发信息线程中建立套接字的输入流和输出流。因此,需要将客户连接线程中建立的套接字作为
17、参数传递到收发信息线程中。n所以,为实现多客户连接,就必须在上例中添加如下的客户连接线程类(内部类):class ConnectSocket extends Threadn public void run()/将多用户连接过程在run方法中实现nwhile(true)/多个客户连接循环ntrynsocket=server.accept();/等待客户连接ncatch(IOException e)n jT1.append(用户连接服务器出错n);nnif(socket!=null)n/创建收发信息线程对象n ReadMessageThread readThread=new ReadMessage
18、Thread(socket);n/激活线程n readThread.start();nn n 客户连接线程的实现 收发信息线程的实现 n 由于涉及到多个用户与服务器的信息收发,因此,在收发信息线程中,至少需要三个成员变量:套接字、输入流、输出流,用于构建信息收发的通道。所以,对上例中的ReadMessageThread类应做如下的更改:nclass ReadMessageThread extends ThreadnBufferedReader cin;/输入流成员变量nPrintStream cout;/输出流成员变量nSocket socket;/套接字成员变量n/*构造器方法,实现从用户连
19、接线程中获得套接字,n 并利用该套接字实现输入流和输出流的建立*/nReadMessageThread(Socket socket)nthis.socket=socket;ntryn cin=new BufferedReader(new InputStreamReader(this.socket.getInputStream();n cout=new PrintStream(this.socket.getOutputStream();n catch(IOException e)n jT1.append(输入输出流建立异常n);nnnpublic void run()nString str=;n
20、while(true)ntrynstr=cin.readLine();ncout.println(服务器反馈信息+str);ncatch(IOException e)n jT1.append(输入输出异常n);nnif(str.equals(QUIT)ntrynsocket.close();ncatch(IOException e)n jT1.append(套接字关闭异常n);nnbreak;n elsenjT1.append(“从客户端读入如下的信息:”+n str+n);nnnn收发信息线程的实现 多客户多客户-服务器信息交互系统服务器信息交互系统-运行与问题运行与问题n运行.n问题:真正
21、系统有用户信息,相互之间可以发送信息n构建真正的多客户信息广播系统n演示:multiserver2/multiclient2多客户信息广播系统多客户信息广播系统n本节我们就来实现客户之间通过服务器进行信息广播的功能。“张三”发送信息到服务器,服务器将信息转发到所有与之连接的客户(“张三”、“李四”),通过这种形式,就可以实现客户之间的信息广播。n在技术上,我们已经实现了客户-服务器之间信息的交互。因此,要实现上述功能,其关键是如何约定双方的信息格式,让服务器根据接收的信息来进行不同的处理。多客户信息广播系统多客户信息广播系统-格式约定格式约定n对服务器来说,其可能接收到的信息有:n(1)客户请
22、求服务器的连接信息n(2)客户向服务器发送的聊天信息n(3)客户想服务器发送的断开请求信息n服务器在接收到上述信息后,应做不同的处理。n如果接收到的信息是客户连接信息,则需要提取客户的名称,并更新连接客户列表,并将连接客户列表广播到所有客户端。n如果接收到的信息是客户聊天信息,则需要将信息转发到每个客户端。n如果接收到的信息是断开连接信息,则需要发送同意断开连接信息到对应的客户端,并关闭对应的连接套接字,更新连接用户列表,并将连接客户列表广播到所有客户端。n对客户端来说,其可能接收到的信息有:n(1)服务器发送的连接客户列表信息n(2)服务器发送的聊天信息n(3)服务器发送的同意断开连接信息。
23、n客户端在接收到上述信息后,应做不同的处理。n如果是连接客户列表信息,则将其显示在客户端的连接客户列表信息显示处。n如果是聊天信息,则将其显示在客户端的聊天信息显示处。n如果是同意断开信息,则关闭对应的连接套接字。多客户信息广播系统多客户信息广播系统-格式约定格式约定n依据,我们知道,客户与服务器之间需要有如下的信息格式约定:n1客户向服务器发送连接请求的信息格式客户向服务器发送连接请求的信息格式nPEOPLE:客户名称n其中,PEOPLE是关键区分字,其后面紧跟客户名称,以分号分隔。n2客户向服务器发送的聊天信息的信息格式客户向服务器发送的聊天信息的信息格式nMSG:客户名称:聊天信息n其中
24、,MSG是关键区分字,其后面紧跟客户的聊天信息,以分号分隔。n3客户向服务器发送的请求断开连接的信息格式客户向服务器发送的请求断开连接的信息格式nQUITn其中,QUIT是关键区分字。n4服务器向客户端发送的连接客户列表的信息格式服务器向客户端发送的连接客户列表的信息格式nPEOPLE:客户名称1:客户名称2:.:客户名称nn其中,PEOPLE是关键区分字,其后面紧跟所有名称,以分号分隔。n5服务器向客户端广播的聊天信息的信息格式n MSG:客户名称:聊天信息n6服务器向客户端发送的同意断开连接的信息格式nQUIT多客户信息广播系统多客户信息广播系统-预备知识预备知识(信息的分离、存储与显示)
25、服务器(或客户端)在接收到相应信息后,必须根据信息的种类,做相应的处理。因此,如何对收到的信息进行分离,是首先需要解决的问题。此外,对服务器来说,需要将连接用户列表、聊天信息广播到每一个客户端,因此,必须要将每一个客户的名称、与之对应的套接字保存,并提供遍历的方式,以实现上述功能。要实现将客户列表信息显示在客户端,通常的做法可以用JTextArea来实现,但如果要实现将信息发送到指定客户,就需要选择客户名称,这是JTextArea无法实现的。多客户信息广播系统多客户信息广播系统-信息分离信息分离n1字符串令字符串令StringTokenizern在JDK1.4中,系统在java.util包中,
26、提供了一个字符串令StringTokenizer类,其主要作用是对字符串进行信息分离。n(1)构造器方法public StringTokenizer(Stringstr,Stringdelim)将字符串str按给定分隔符号delim进行信息分离,构成字符串令对象。n(2)成员方法public boolean hasMoreTokens()如果字符串令还有元素,则返回真值。n(3)成员方法public String nextToken()返回字符串令的下一个元素。n(4)成员方法public String nextToken(Stringdelim)返回字符串令重新以按给定分隔符号delim进行
27、信息分离的下一个元素。多客户信息广播系统多客户信息广播系统-用户存储用户存储n在JDK1.4中,系统在java.util包中,提供了一个向量Vector类,其主要作用是可以存储若干的元素(可以是任何类型),并提供若干方法,以实现对这些元素的遍历。n(1)构造器方法public Vector()构造一个向量类对象,其可以存储任意多个元素(依据机器的内存空间)。n(2)成员方法public int size()返回向量类对象中存储的元素个数。n(3)成员方法public Object elementAt(int i)返回向量类对象中第i元素。n(4)成员方法public Object firstE
28、lement()返回向量类对象中第1元素。n(5)成员方法public Object lastElement()返回向量类对象中最后1个元素。n(6)成员方法public void removeElementAt(i)移除向量类对象中第i元素。n(7)成员方法public void addElement(Object c)将c添加到向量类对象。n(8)成员方法public String toString()将向量类中的每个元素以字符串形式返回。多客户信息广播系统多客户信息广播系统-用户显示用户显示n在JDK1.4中,系统在javax.swing包中,提供了一个下拉列表JList类,其主要作用是
29、实现按条显示信息,并能返回所选择的信息内容。n(1)构造器方法public JList()构造一个下拉列表类对象,其可以存储任意多个元素(依据机器的内存空间)。n(2)成员方法public void setListData(Vector vs)将向量vs的每个元素作为下拉列表的显示项。n(3)成 员 方 法 p u b l i c O b j e c t getSelectedValue()返回被选择的对象。多客户信息广播系统多客户信息广播系统-服务器端功能结构服务器端功能结构 n1多线程机制多线程机制n在服务器端,由于需要建立多个用户的连接,因此,需要有一个ConnectSocket线程,其
30、主要工作流程如图1所示:建立客户连接线程有客户连接?客户信息添加到客户列表客户重名?激活线程断开连接向客户端更新客户信息列表是是否n 服务器与客户连接后,需要不断进行信息的收发,因此,客户与服务器进行信息交互也需要用Client线程来实现。n在Client的构造器中,主要实现客户与服务器的连接通道(输入流和输出流),并分离客户的信息,并将连接信息显示在服务器端。n Client接下来的工作就是不停扫描套接字端口,对读入的信息做相应的处理。其主要工作流程如图2所示:有信息吗?读入信息是聊天信息?将信息转发到所有客户端断开当前连接更新客户列表结束当前线程否是多客户信息广播系统多客户信息广播系统-服
31、务器端功能结构服务器端功能结构 n对服务器来说,必须将客户信息保存,以实现服务器与每个客户的信息交互。那么,对服务器来说,到底需要保存客户的哪些信息呢?n显然,客户名称是需要保存的,此外,服务器与客户的信息通道(输入流和输出流)也需要保存,这样,才能实现服务器通过访问客户列表,实现向每个客户发送消息。n因此,我们可以将服务器客户信息交互线程作为用户列表信息保存起来。n客户连接信息的存储客户连接信息的存储多客户信息广播系统多客户信息广播系统-服务器服务器-客户连接线程客户连接线程nclass ConnectSocket extends ThreadnSocket socket;npublic v
32、oid run()n while(true)n tryn socket=server.accept();/等待客户连接n catch(IOException e2)n jT1.append(客户连接失败n);n n /*客户连接成功,定义并实例化一个Client线程,n每一个线程对应一个客户连接*/n Client c=new Client(socket);n /*将连接的客户添加到客户列表存储clients中*/n clients.addElement(c);n /*测该用户名称是否存在,如不存在,则启动线程,n实现客户与服务器的信息交互通道*/n if(checkName(c)n /启动线
33、程n c.start();n/*向每个客户端更新客户列表信息*/n notifyRoom();n elsen disconnect(c);n nnn多客户信息广播系统多客户信息广播系统-服务器服务器-客户端信息交互线程客户端信息交互线程n见教材多客户信息广播系统多客户信息广播系统-客户名检测成员方法客户名检测成员方法 n该方法作为MultiServer1类的成员方法,主要功能是检测登陆的客户名是否已被其他已登陆的客户所占用。n由于用户登陆成功后,将所有信息已保存在Client线程对象中。因此,我们可以比较新建立的Client线程对象的客户名称是否和已存在的Client线程对象的客户名称是否重复
34、而返回boolean值,供调用该方法的程序段进行处理。n(见教材)多客户信息广播系统多客户信息广播系统-客户列表信息发送成员方法客户列表信息发送成员方法 n该方法作为MultiServer1类的成员方法,主要功能是将已连接的客户名称发送到所有客户端。n(见教材)多客户信息广播系统多客户信息广播系统-息广播成员方法息广播成员方法 n该方法作为MultiServer1类的成员方法,主要功能是将要发送的信息发送到所有客户端。n(见教材)多客户信息广播系统多客户信息广播系统-断开服务器断开服务器-客户连接方法客户连接方法 n该方法作为MultiServer1类的成员方法,主要功能是断开某一个特定的服务
35、器客户交互信息线程,并清除与之对应的用户列表信息。n(见教材)多客户信息广播系统多客户信息广播系统-客户器端功能结构 n客户端按钮功能客户端按钮功能n客户端有3个按钮,每个按钮都响应不同的功能,其主要工作流程如图所示:获取服务器IP、端口、客户名称连接服务器创建输入、输出流发送客户连接信息到服务器创建“读服务器发送信息线程”连接按钮获取发送信息按约定格式构建信息发送信息到服务器发送断开按钮n读取服务器发送信息线程读取服务器发送信息线程n客户端在读取信息后,必须对信息的类型进行分析,根据不同信息类型做相应处理,其主要工作流程如图5所示:读取信息并分离QUIT,表示服务器同意断开关闭当前套接字结束
36、当前线程PEOPLE,表示接收到客户连接列表将客户名称分离其它,表示服务器广播信息提取客户名称及聊天信息显示信息将其显示在下拉列表多客户信息广播系统多客户信息广播系统-读取服务器发送信息线程读取服务器发送信息线程nReadMessageThread类作为MultiClient类的内部类,主要实现如图所示的功能 n(见教材)多客户信息交流系统多客户信息交流系统n请对上例进行修改,实现“基于TCP协议的多客户信息交流系统”。n(1)客户端界面如图6所示。n(2)客户端发送信息可以选择广播或特定客户。n(3)如选择广播,则将信息转发到所有客户端。n(4)如选择“特定客户”,则将信息只发送到选择的客户
37、。n提示:要实现广播和特定发送,就需要对客户端发送到服务器端的信息协议做调整,可以约定为:n若为广播,则信息格式为“MSG:BROAD:发送客户名称:发送的信息”;n若为特定发送,则信息格式为“MSG:SINGLE:接收客户名称:发送客户名称:发送的信息”。多客户信息交流系统多客户信息交流系统UDP协议基础-UDP协议与TCP协议的异同nUDP不存在“真实”连接nUDP不打开端口等待连接UDP协议基础-UDP服务器端的连接过程(1)在服务器的指定端口上创建DatagramSocket数据报套接字对象,该对象用于收发数据报。DatagramSocket socket=new DatagramSo
38、cket(INPORT)(2)创建DatagramPacket数据报包对象,用来存储接收到的数据报包。DatagramPacket dp=new DatagramPacket(buf,buf.length),其中,buf为字节数组(3)要接收数据,通过数据报套接字对象的receive()方法接收1个数据包到创建的数据报包对象dp中。socket.receive(dp)(4)要发送数据,将数据组合成数据报包对象,通过数据报套接字对象的send()发放将数据发送。注意,数据组合成数据报包对象时,要给出对方的IP地址和端口号。UDP协议基础-UDP客户端的连接过程(1)在客户机上创建Datagram
39、Socket数据报套接字对象,该对象用于收发数据报。系统将决定采用哪个端口。DatagramSocket socket=new DatagramSocket()(2)创建DatagramPacket数据报包对象,用来存储接收到的数据报包。DatagramPacket dp=new DatagramPacket(buf,buf.length),其中,buf为字节数组(3)要接收数据,通过数据报套接字对象的receive()方法接收1个数据包到创建的数据报包对象dp中。socket.receive(dp);(4)要发送数据,将数据组合成数据报包对象,通过数据报套接字对象的send()发放将数据发送。注意,数据组合成数据报包对象时,要给出对方的IP地址和端口号。