Presentation is loading. Please wait.

Presentation is loading. Please wait.

网络程序设计 第二章 客户端Socket用法详解.

Similar presentations


Presentation on theme: "网络程序设计 第二章 客户端Socket用法详解."— Presentation transcript:

1 网络程序设计 第二章 客户端Socket用法详解

2 概述 在客户/服务器通信模式中,客户端需要主动创建与服务器连接的Socket(套接字),服务器段收到客户端的连接请求,也会创建与客户连接的Socket。 Socket可以看作是通信连接两端的收发器,服务端和客户端都是通过Socket来收发数据。 注意:这里的Socket是指客户端应用编程的Socket类,而服务器端的是ServerSocket.

3 本章的内容 1、Socket类介绍,构造方法/成员方法 2、Socket对象选项设置,控制建立与服务器端的连接,以及收发数据的行为。
3、介绍一个SMTP客户程序,它利用Socket连接到SMTP邮件发送服务器,然后请求服务器发送一封邮件。

4 重点理解 Socket建立连接的过程及其控制 Socket进行数据输入和输出的过程及其控制 请仔细阅读本章小结p51

5 问题

6 2.1 构造Socket Socket的构造方法: 1、Socket()
2、Socket(InetAddress address,int port) 3、Socket(InetAddress address,int port, InetAddress localAddr,int localport) 4、 Socket(String host,int port) 5、 Socket(String host,int port, InetAddress localAddr,int localport)

7 2.1 InetAddress 在这里InetAddress 表示服务器的IP地址。
InetAddress addr = InetAddress.getLocalHost(); //返回代表“ ”的IP地址 InetAddress addr = InetAddress.getByName(“ ”); //返回域名为 的IP地址: InetAddress addr = InetAddress.getByName(“

8 2.1 设置服务器地址 在这里除了第一个不带参数的构造方法,其他构造方法都需要在参数中设定服务器的IP地址或主机名,以及端口:第2和第4构造函数

9 2.1 设置客户端地址 在一个Socket对象中既包含远程服务器的IP地址和端口信息,也包含本机客户端的IP地址和端口信息。默认情况下,客户端IP地址来自于客户程序所在的主机,而其端口则由操作系统随机分配。 Socket类有两个构造方法允许显式设定客户端的IP地址或主机名和端口:3,5

10 2.1 使用无参构造函数 当客户端Socket构造方法请求与服务器连接时,可能需要等待一段时间,如果希望等待连接的时间,我们使用第一个无参数的构造函数,如下: Socket socket = new Socket(); SocketAddress remoteAddr = new InetSocketAddress(“localhost”,8000); Socket.connect(remoteAddr,60000); 其中connect(SocketAddress endpoint,int timeout)方法负责连接服务器。

11 2.1 客户连接服务器时可能抛出的异常 public void connect(String host,int port){
SocketAddress remoteAddr=new InetSocketAddress(host,port); Socket socket=null; String result=""; try { long begin=System.currentTimeMillis(); socket = new Socket(); socket.connect(remoteAddr,1000); //超时时间为1秒钟 long end=System.currentTimeMillis(); result=(end-begin)+"ms"; //计算连接所花的时间 }

12 2.1 客户连接服务器时可能抛出的异常 catch (BindException e) {
result="Local address and port can't be binded"; }catch (UnknownHostException e) { result="Unknown Host"; }catch (ConnectException e) {//如果ServerSocket对象设定连接请求队列长度 result="Connection Refused"; }catch (SocketTimeoutException e) { result="TimeOut"; }catch (IOException e) { result="failure"; } finally { try { if(socket!=null)socket.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println(remoteAddr+" : "+result); }

13 2.2获取Socket的信息 1、前面提到了,一个Socket对象同时包含了服务器端和客户端的IP地址和端口信息。
如何编程呢?

14 2.2获取Socket的信息 getInetAddress(); getPort(); getLocalAddress();
getLocalPort(); getInputStream();可以使用shutdownInput()关闭输入流 getOutputStream();可以使用shutdownOutput()关闭输出流

15 2.2HTTPClient展示输入输出流 public class HTTPClient {
String host="localhost"; int port=6888; Socket socket; public void createSocket()throws Exception{ socket=new Socket("localhost",6888); }

16 2.2HTTPClient展示输入输出流 public void communicate()throws Exception{
StringBuffer sb=new StringBuffer("GET "+"/crm/About.jsp"+" HTTP/1.1\r\n"); sb.append("Host: sb.append("Accept: */*\r\n"); sb.append("Accept-Language: zh-cn\r\n"); sb.append("Accept-Encoding: gzip, deflate\r\n"); sb.append("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n"); sb.append("Connection: Keep-Alive\r\n\r\n"); //发出HTTP请求 OutputStream socketOut=socket.getOutputStream(); socketOut.write(sb.toString().getBytes()); socket.shutdownOutput(); //关闭输出流

17 2.2HTTPClient展示输入输出流 //接收响应结果
InputStream socketIn=socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(socketIn)); String Data ; while ((Data=br.readLine())!=null) System.out.println(Data); socket.close(); }

18 2.2HTTPClient展示输入输出流 getInetAddress(); getPort(); getLocalAddress();
getLocalPort(); getInputStream(); getOutputStream();

19 2.3关闭Socket 当客户端与服务端通信结束,应该及时关闭Socket,以释放Socket占用的包括端口在内的各种资源。
Socket的close()方法负责关闭Socket,当Socket对象被关闭,就不能对其输入输出流进行操作,否则会导致IOException 参考前面编程的方式关闭Socket

20 2.3测试Socket的连接状态 Socket类提供3个状态测试方法:
1、isClosed():如果Socket已经连接到远程主机,但调用了close(),则返回true,否则为false. 2、isConnected():如果Socket曾经连接到远程主机,则返回true,否则false. 3、isBound():如果Socket已经与一个本地端口绑定,则返回true,否则false. 4、如要判断一个Socket对象当前是否处于连接状态,可以如下: Boolean isConnected = socket.isConnected() && !socket.isClosed();

21 2.4半关闭Socket 进程A与进程B通过Socket通信,假定进程A输出数据,进程B读入数据,进程A如何告知进程B所有数据输出完毕了呢?
有以下四种方法:

22 2.4半关闭Socket 1、进程A与进程B交换的是字符流,并且都一行一行的读/写数据时,可以事先约定以一个特殊的标志为结束标志,如第一章例子。 2、进程A先发送消息告知进程B所发送的正文的长度,然后再发送正文,进程B只读进相应长度的字符或字节即可。 3、进程A发完所有数据后,关闭Socket。这时进程B读完这些数据后,再执行输入流的read()方法时,该方法返回-1。如果使用BufferdReader的readLine(),则返回null。如下:

23 2.4半关闭Socket ByteArrayOutputStream buffer=new ByteArrayOutputStream();
byte[] buff=new byte[1024]; int len=-1; while((len=socketIn.read(buff))!=-1){ buffer.write(buff,0,len); }

24 2.4半关闭Socket BufferedReader br = new BufferedReader(new InputStreamReader(socketIn)); String Data ; while ((Data=br.readLine())!=null) System.out.println(Data);

25 2.4半关闭Socket 4、当调用Socket的close()方法关闭Socket时,它的输入输出流也都会被关闭。但有时可能仅希望关闭其中之一,可以采用Socket类提供的半关闭方法: shutdownInput():关闭输入流。 shutdownOutput():关闭输出流。 案例HTTPClient采用了半关闭方式

26 2.4半关闭Socket Socket类提供了输入输出流的状态测试方法: isInputShutDown()
isOutputShutDown() 测试:当客户与服务器通信时:如果有一方突然结束程序,或者关闭了Socket,或者单独关闭了输入流或输出流,对另一方会造成什么影响呢?请通过书中实验来理解例程2-6和2-7。

27 2-6 sender.java import java.net.*;import java.io.*;import java.util.*;
public class Sender { private String host="localhost"; private int port=8000; private Socket socket; private static int stopWay=1; //结束通信的方式 private final int NATURAL_STOP=1; //自然结束 private final int SUDDEN_STOP=2; //突然终止程序 private final int SOCKET_STOP=3; //关闭Socket,再结束程序 private final int OUTPUT_STOP=4; //关闭输出流,再结束程序

28 2-6 sender.java public Sender()throws IOException{
socket=new Socket(host,port); } public static void main(String args[])throws Exception{ if(args.length>0)stopWay=Integer.parseInt(args[0]); new Sender().send(); private PrintWriter getWriter(Socket socket)throws IOException{ OutputStream socketOut = socket.getOutputStream(); return new PrintWriter(socketOut,true);

29 2-6 sender.java public void send()throws Exception {
PrintWriter pw=getWriter(socket); for(int i=0;i<20;i++){ String msg="hello_"+i; pw.println(msg); System.out.println("send:"+msg); Thread.sleep(500); if(i==2){ //终止程序,结束通信 if(stopWay==SUDDEN_STOP){ System.out.println("突然终止程序"); System.exit(0); }else if(stopWay==SOCKET_STOP){ System.out.println("关闭Socket并终止程序"); socket.close(); break; }else if(stopWay==OUTPUT_STOP){ socket.shutdownOutput(); System.out.println("关闭输出流并终止程序"); } } } if(stopWay==NATURAL_STOP){ socket.close(); } }

30 2.5设置Socket选项 对Socket网络通信行为的控制可以通过通过以下选项进行设置: 1、TCP_NODELAY:表示立即发送数据。
2、SO_REUSEADDR:表示是否允许重用Socket所绑定的本地地址。 3、SO_TIMEOUT:表示接收数据时的等待超时时间 4、SO_LINGER:表示当执行Socket的close()方法时,是否立即关闭底层的Socket。 5、SO_SNDBUF:表示发送数据的缓冲区的大小

31 2.5设置Socket选项 对Socket网络通信行为的控制可以通过通过以下选项进行设置:
6、SO_RCVBUF:表示接收数据的缓冲区的大小。 7、SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。 8、OOBINLINE:表示是否支持发送一个字节的TCP紧急数据。

32 2.5设置Socket选项 对Socket网络通信行为的控制可以通过以下选项进行设置: 1、TCP_NODELAY:表示立即发送数据。
设置方法:setTcpNoDelay(boolean on) 读取方法:boolean getTcpNoDelay() false适用情形:大批量数据发送,如文件数据 True适用情形:实时传输,如网络游戏

33 2.5设置Socket选项TCP_NODELAY优缺点
TCP_NODELAY的默认值为false,表示采用Negale算法: 该算法是指发送方发送的数据不会立即发出,而是先放到缓冲区内,等缓冲区满了再发出。发送完一批数据后,会等接收方对这批数据的回应,然后再发下一批数据。 适用于发送方发送大批量数据,且接收方及时做出回应的场合,因为缓冲区原因可减少次数大批量来提高效率

34 2.5设置Socket选项TCP_NODELAY优缺点
采用Negale算法的缺点: 如果发送方持续发送小批量数据,且接收方并不一定会立即发送响应数据,那么该算法会使发送方运行很慢。 如果TCP_NODELAY设为true,就会关闭socket的缓冲,确保数据及时发送。 如果socket底层不支持TCP_NODELAY选项,set/get方法就会抛出SocketException异常。

35 2.5设置Socket选项 2、SO_REUSEADDR:表示是否允许重用Socket所绑定的本地地址。
设置方法:setReuseAddress(boolean on) 读取方法:boolean getReuseAddress() 为了确保一个进程关闭Socket后,即使它还没有释放端口(需要确保接收完延迟数据,确保这些数据不被其他绑定到该端口的新进程接收到),同一主机的其他进程还可以立即重用该端口,可以在Socket没有绑定本地端口调用之前调用setReuseAddress(true),然后才调用connect(remoteAddr)。

36 2.5设置Socket选项 3、SO_TIMEOUT:表示接收数据时的等待超时时间
设置方法:setSoTimeout(int milliseconds) 读取方法:int getSoTimeout() 在Socket接收数据前调用setSoTimeout(1000),用于设定接收数据的等待超时时间,单位为毫秒,其默认值为0,表示永不超时。

37 2.5设置Socket选项 3、SO_TIMEOUT:表示接收数据时的等待超时时间
当通过Socket的输入流读数据时,如果还没有数据,就会等待。例如,在代码in.read(buff)方法从输入流中读入1024个字节,如果输入流中没有数据,该方法就会等待发送方发送数据,直到满足以下情况才结束等待:(请使用例程2-8和2-9进行实验)

38 2-8 ReceiveServer import java.io.*;import java.net.*;
public class ReceiveServer { public static void main(String args[])throws Exception { ServerSocket serverSocket = new ServerSocket(8000); Socket s=serverSocket.accept(); //s.setSoTimeout(20000); 设置接收超时 InputStream in=s.getInputStream(); ByteArrayOutputStream buffer=new ByteArrayOutputStream(); byte[] buff=new byte[1024]; int len=-1; do{ try{ len=in.read(buff); if(len!=-1)buffer.write(buff,0,len); }catch(SocketTimeoutException e){ System.out.println("等待读超时"); len=0; } }while(len!=-1); System.out.println(new String(buffer.toByteArray())); //把字节数组转换为字符串 }}

39 2-8 ReceiveServer import java.io.*; import java.net.*;
public class SendClient { public static void main(String args[])throws Exception { Socket s = new Socket("localhost",8000); OutputStream out=s.getOutputStream(); out.write("hello ".getBytes()); out.write("everyone".getBytes()); Thread.sleep(60000); //睡眠1分钟 s.close(); }

40 2.5设置Socket选项 4、SO_LINGER:表示当执行Socket的close()方法时,是否立即关闭底层的Socket。默认会延迟直到发送完所有剩余的数据,才会真正关闭Socket,断开连接。 设置方法:setSoLinger(boolean on,int seconds) 读取方法:int getSoLinger() 执行socket.setSoLinger(true,0):底层Socket立即关闭,剩余数据丢弃。 执行socket.setSoLinger(true,3600):底层Socket已经发送完剩余数据,或者阻塞超过3600秒,才关闭和丢弃剩余数据。

41 2.5设置Socket选项SO_LINGER import java.io.*;import java.net.*;
public class SimpleClient { public static void main(String args[])throws Exception { Socket s = new Socket(“localhost”,8000);//默认延长 //s.setSoLinger(true,0); //Socket关闭后,底层Socket立即关闭 //s.setSoLinger(true,3600); //Socket关闭后,底层Socket延迟3600秒再关闭 OutputStream out=s.getOutputStream(); StringBuffer sb=new StringBuffer(); for(int i=0;i<10000;i++)sb.append(i); out.write(sb.toString().getBytes()); //发送一万个字符 System.out.println("开始关闭Socket"); long begin=System.currentTimeMillis(); s.close(); long end=System.currentTimeMillis(); System.out.println("关闭Socket所用的时间为:"+(end-begin)+"ms"); }}

42 2.5设置Socket选项SO_LINGER import java.io.*;import java.net.*;
public class SimpleServer { public static void main(String args[])throws Exception { ServerSocket serverSocket = new ServerSocket(8000); Socket s=serverSocket.accept(); Thread.sleep(5000); //睡秒5秒后再读输入流 InputStream in=s.getInputStream(); ByteArrayOutputStream buffer=new ByteArrayOutputStream(); byte[] buff=new byte[1024]; int len=-1; do{ len=in.read(buff); if(len!=-1)buffer.write(buff,0,len); }while(len!=-1); System.out.println(new String(buffer.toByteArray())); //把字节数组转换为字符串 }}

43 2.5设置Socket选项 6、SO_RCVBUF:表示接收数据的缓冲区的大小。
一般说来,传输大的连续的数据块(基于http/ftp协议通信)可以使用较大缓冲区,这样减少数据传输的次数,提高传输效率 而对于交互频繁且单次传输数据量比较小的通信方式(telnet/网络游戏),则应采取较小的缓冲区,确保小批量的数据能及时发送给对方。

44 2.5设置Socket选项 5、SO_SNDBUF:表示发送数据的缓冲区的大小
设置方法:setSendBufferSize(int size) 读取方法:int getSendBufferSize () Receive的设置: 设置方法:setReceiveBufferSize(int size) 读取方法:int getReceiveBufferSize () 类似SO_RCVBUF

45 2.5设置Socket选项 7、SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。
当此项为True时,表示底层的TCP实现会监视该连接是否有效,当连接处于空闲状态超过了两小时,本地的tcp实现会发送一个数据包给远程的socket。如果远程socket没有发回响应,tcp实现就会持续尝试11分钟,直到收到响应为止。如果未收到响应,tcp实现就会自动关闭本地Socket,断开连接。 在不同网络平台,时限可能不同。 而当其为false时,表示Tcp实现不会监视连接是否有效,不活动的客户段可能会永久存在,而不会注意到服务器已经崩溃

46 2.5设置Socket选项 8、OOBINLINE:表示是否支持发送一个字节的TCP紧急数据。其默认值为false,这时接收方对紧急数据不做任何处理,直接丢弃。 该项为true时,表示支持发送一个字节的TCP紧急数据,socket.setOOBInline(true)。 Socket类的sendUrgentData(int data)方法用于发送一个字节的TCP紧急数据。

47 2.5设置Socket选项 9、服务类型选项:在internet上传输数据分为不同的服务类型,它们有不同的定价,类比邮局提供的不同服务—普通信、挂号信、快件,区分速度和可靠性。 用户可以选择不同服务类型:例如发送视频,需要高带宽快速送达目的地,发送电子邮件可以使用低带宽慢速到达目的地。

48 2.5设置Socket选项 9、服务类型选项: IP规定4种服务类型,用来定性的描述服务的质量。 1、低成本:发送成本低。
2、高可靠性:保证把数据可靠的送达目的地。 3、最高吞吐量:一次可以接收或发送大批量的数据。 4、最小延迟:传输数据的速度快,把数据快速送达目的地。

49 2.5设置Socket选项 9、服务类型选项:这4种服务类型还可以进行组合。例如:可以同时要求获得高可靠性和最小延迟。Socket类中对应的方法如下: 设置方法:setTrafficClass(int trafficClass) 读取方法:int getTrafficClass() Socket类用4个16进制整数表示服务类型。 低成本:0x02(0000,0010) 高可靠性:0x04(0000,0100) 最高吞吐量:0x08(0000,1000) 最小延迟:0x10(0001,0000)

50 2.5设置Socket选项 9、服务类型选项:这4种服务类型还可以进行组合。例如:可以同时要求获得高可靠性和最小延迟。对应的设置方法如下:
Socket.setTrafficClass(0x04|0x10) 高可靠性设置: Socket.setTrafficClass(0x04)

51 2.5设置Socket选项 10、设定连接时间、延迟和带宽的相对重要性:在jdk1.5中,socket类提供了一个方法:
setPerformancePreferences(int connectionTime,int latency,int bandwidth) 上述方法的3个参数表示网络传输数据的3项指标:最小时间建立连接、最小延迟、最高带宽。

52 2.5设置Socket选项 10、设定连接时间、延迟和带宽的相对重要性:
在Internet上,数据传输之前先被分割成包。当数据包到达目的地,接收方再以正确的次序把数据包重新装配起来。所谓延迟就是一个数据包到达目的地需要的时间;另外,当你发出对某个目标的请求,目标系统在应答时返回一些数据包,延迟这一概念也可用来描述这一过程所需要的时间。最理想的组合当然是高带宽、低延迟:数据包能够以最快的速度到达,且有充裕的带宽支持;反之,低带宽、高延迟则属于最差的组合。

53 2.5设置Socket选项 10、设定连接时间、延迟和带宽的相对重要性:
  许多通过卫星建立的连接会出现高带宽、高延迟的情形,例如,虽然带宽高达768Kbps,如果你点击网页上的链接或发送一个命令,收到应答数据可能需要一秒以上。由于延迟时间太长,对于许多联机游戏来说,基于卫星的连接几乎无法使用,因为联机游戏要求延迟时间小于半秒或更少。当然,也有的应用对延迟时间的要求不高,例如 。   网络延迟与许多因素有关,最重要的是发送方和接收方之间的路由器,它们对连接的质量有着重要的影响。卫星连接之所以比光缆连接慢,就是因为数据必须通过轨道上的卫星中转。Cable和DSL线路的延迟一般要小得多,但最终还是要由线路的具体情况决定。

54 2.6发送邮件的SMTP客户程序 STMP协议:建立在TCP/IP协议之上。 规定了把邮件从发送方传输到接收方的规则
STMP客户程序请求发送邮件,STMP服务器(默认端口25)负责把邮件传输到目的地。 在STMP客户程序和STMP服务器的一次会话过程中, STMP客户程序会发送一系列SMTP命令,STMP服务器则作出响应,返回相应的应答码,以及对应答码的描述,如下图:

55 主要的STMP命令和应答码

56

57 系统构成框图 用户代理(UA)负责报文的生成与处理,报文传送代理(MTA)负责建立与远程主机的通信与报文数据的传输,邮件数据存储系统MS负责保存邮件。邮件在发送端的MTA与接收端的MTA之间的TCP连接上,进行“端-端”的直接传输或存储转发传递。

58 单端 系统原理图

59 2.6发送邮件的SMTP客户程序 SMTP设计基于以下通信模型:针对用户的邮件请求,发送SMTP建立与接收SMTP之间建立一个双向传送通道。接收SMTP可以是最终接收者也可以是中间传送者。SMTP命令由发送SMTP发出,由接收SMTP接收,而应答则反方面传送。

60 2.6发送邮件的SMTP客户程序 一旦传送通道建立,SMTP发送者发送MAIL命令指明邮件发送者。如果SMTP接收者可以接收邮件则返回OK应答。SMTP发送者再发出RCPT命令确认邮件是否接收到。如果SMTP接收者接收,则返回OK应答;如果不能接收到,则发出拒绝接收应答(但不中止整个邮件操作),双方将如此重复多次。当接收者收到全部邮件后会接收到特别的序列,如果接收者成功处理了邮件,则返回OK应答。

61 2.6发送邮件的SMTP客户程序 SMTP协议机理 SMTP也是基于客户机/服务器工作模式来实现的。 系统SMTP实现模型

62 2.6发送邮件的SMTP客户程序 SMTP协议命令集 SMTP协议命令集

63 SMTP 使用SMTP协议在邮件服务器(Message Transfer Agents)之间传送邮件信息。 MTA MTA MTA SMTP
File System UA UA

64 SMTP 协议 SMTP发送者是客户 SMTP接收者是服务器 交互过程: 客户向服务器发送命令,服务器对命令进行响应(回复状态信息)。
命令有先后顺序! 状态信息包括ascii编码的数字和文本。

65 SMTP协议命令集 命令 命令格式 描述 HELO 用于建立SMTP连接并交换标识符 DATA DATA<CRLF> 报文数据
HELP HELP[<SP><string>]<CRLF> 请求帮助信息 MAIL MAIL<SP>FROM:<reverse-path><CRLF> 提供发送者邮件地址 NOOP NOOP<CRLF> 无操作 RCPT RCPT<SP><TO:><forward-path><CRLF> 提供接收者邮件地址 RSET RSET<CRLF> 终止当前事务 TURN TURN<CRLF> 改变发送方向 VRFY VRFY<SP><string><CRLF> 检验用户名 SEND SEND<SP>FROM:<reverse-path><CRLF> 将报文数据发送到终端或邮箱 QUIT QUIT<CRLF> 关闭STMP连接

66 2.6发送邮件的SMTP客户程序 class Message{ //表示邮件 String from; //发送者的邮件地址
String to; //接收者的邮件地址 String subject; //邮件标题 String content; //邮件正文 String data; //邮件内容,包括邮件标题和正文 public Message(String from,String to, String subject, String content){ this.from=from; this.to=to; this.subject=subject; this.content=content; data="Subject:"+subject+"\r\n"+content; }

67 2.6发送邮件的SMTP客户程序 import java.net.*; import java.io.*;
public class MailSender{ private String smtpServer="smtp.mydomain.com"; //SMTP邮件服务器的主机名 //private String smtpServer="localhost"; private int port=25; public static void main(String[] args){ Message msg=new Message( //发送者的邮件地址 //接收者的邮件地址 "hello", //邮件标题 "hi,I miss you very much."); //邮件正文 new MailSender().sendMail(msg); }

68 2.6发送邮件的SMTP客户程序 public void sendMail(Message msg){ Socket socket=null; try{ socket = new Socket(smtpServer,port); //连接到邮件服务器 BufferedReader br =getReader(socket); PrintWriter pw = getWriter(socket); String localhost= InetAddress.getLocalHost().getHostName(); //客户主机的名字 sendAndReceive(null,br,pw); //仅仅是为了接收服务器的响应数据 sendAndReceive("HELO " + localhost,br,pw); sendAndReceive("MAIL FROM: <" + msg.from+">",br,pw); sendAndReceive("RCPT TO: <" + msg.to+">",br,pw); sendAndReceive("DATA",br,pw); //接下来开始发送邮件内容 pw.println(msg.data); //发送邮件内容 System.out.println("Client>"+msg.data); sendAndReceive(".",br,pw); //邮件发送完毕 sendAndReceive("QUIT",br,pw); //结束通信 }catch (IOException e){ e.printStackTrace(); }finally{ try{if(socket!=null)socket.close(); }catch (IOException e) {e.printStackTrace();} } }

69 2.6发送邮件的SMTP客户程序 /** 发送一行字符串,并接收一行服务器的响应数据*/
private void sendAndReceive(String str,BufferedReader br,PrintWriter pw) throws IOException{ if (str != null){ System.out.println("Client>"+str); pw.println(str); //发送完str字符串后,还会发送“\r\n”。 } String response; if ((response = br.readLine()) != null) System.out.println("Server>"+response); private PrintWriter getWriter(Socket socket)throws IOException{ OutputStream socketOut = socket.getOutputStream(); return new PrintWriter(socketOut,true); private BufferedReader getReader(Socket socket)throws IOException{ InputStream socketIn = socket.getInputStream(); return new BufferedReader(new InputStreamReader(socketIn));

70 2.7Socket小结 1、Socket建立在TCP/IP协议基础上,客户端与服务器都通过Socket来收发数据,也就是说两者都需要创建Socket对象来输入和输出流。 2、构造函数 3、正确关闭Socket 4、Socket属性选项设置,用来控制建立连接、接收和发送数据,以及关闭Socket的行为。


Download ppt "网络程序设计 第二章 客户端Socket用法详解."

Similar presentations


Ads by Google