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

Slides:



Advertisements
Similar presentations
主要内容 Java 的常用包 Java 的常用包 “ == ” 和 “ equals ” 的用法 “ == ” 和 “ equals ” 的用法 基本数据类型与引用类型 基本数据类型与引用类型 String 和 StringBuffer String 和 StringBuffer 对象的克隆( clone.
Advertisements

第四章 类、对象和接口.
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
第13章:电子邮件系统 TCP/IP互联网上的电子邮件传输过程; 电子邮件的地址表示; 电子邮件传输协议SMTP和POP3;
檔案處理.
Java的程式架構與基本觀念 Java語言的歷史 Java程式的開發環境 Java程式的架構 輸出與輸入物件之使用 工具使用方法介紹
四資二甲 第三週作業 物件導向程式設計.
网络程序设计 第二章 客户端Socket用法详解.
MVC Servlet与MVC设计模式.
第五章 字符串.
南京理工大学 第2章 Java基本语法 本章我们将学习Java编程语言的基本语法,包括变量、操作符、表达式、语句、字符串、数组、控制流以及如何使用帮助文档。 使用下面的编程框架: public class Test{ public static void main(String []args){ //以下添加测试代码.
第十五章 网络编程.
第二章 JAVA语言基础.
基于WinSock的邮件系统 1 设计目标: 1.1 SMTP发送邮件服务器(c语言) 1.2 POP3接受邮件服务器(c语言)
创意源自激情,技术成就梦想 畅翼创新俱乐部 2012年春俱乐部第三次技术培训 赵程.
第三章 控制结构.
程式設計實作.
第5章 异常处理 王德俊 上海交通大学继续教育学院.
第3章 分布式文件系统HDFS (PPT版本号:2017年2月版本)
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
Java语言程序设计 清华大学出版社 第9章 网络通信.
例外處理(Exception Handling)
Java 第28讲:建立删除文件 主讲教师:李焱 讲师.
Java基础 JavaSE异常.
第3章 語法入門 第一個Java程式 文字模式下與程式互動 資料、運算 流程控制.
崑山科技大學資訊管理系 伺服網頁程式設計 系統開發細部流程 教師:游峰碩.
电子邮件系统基本协议介绍 北京春笛信息技术有限公司.
JAVA 编 程 技 术 主编 贾振华 2010年1月.
C#程序设计 c# programming 网络编程 C#程序设计课程组.
西南科技大学网络教育系列课程 高级语程序设计(Java) 第五章 继承、接口与范型.
程式設計實作.
第四章 基本輸出入 Java應用程式的輸出入介面有三種,分別是命令提示字元視窗、AWT元件、及Swing元件。本單元先介紹命令提示字元視窗,AWT請看第16、17章,Swing請看第20章。 輸入 輸出.
抽象类 File类 String类 StringBuffer类
第2章回顾 标识符:不用记,动手 关键字:if, else, switch, for, while, do, break, continue, void, …… 局部变量和成员变量 ①变量作用域 ②内存布局 基本数据类型 ①4类8种 ②互相转换 流程控制语句 ①分支 if……else, switch.
西南科技大学网络教育系列课程 高级语程序设计(Java) 第十一章 Java 中的网络编程.
第六讲 JSP中的文件操作(2) 教学目的 本讲继续讲述JSP中使用Java输入、输出流实现文件的读写操作 。 1 文件上传 2 文件下载
2018/12/3 面向对象与多线程综合实验-网络编程 教师:段鹏飞.
Java语言程序设计 第五部分 Java异常处理.
王豐緒 銘傳大學資訊工程學系 問題:JAVA 物件檔輸出入.
第一次课后作业 1. C/C++/Java 哪些值不是头等程序对象 2. C/C++/Java 哪些机制采用的是动态束定
例外處理與 物件序列化(Exception Handling and Serialization of Objects)
3.1 数据类型 3.2 标识符与关键字 3.3 常量 3.4 变量 3.5 运算符与表达式 3.6 一个编程实例
2019/1/17 Java语言程序设计-程序流程 教师:段鹏飞.
第 十二 章 C# 中的文件处理.
Chapter 11:Java网络编程 授课教师:赵小敏 浙江工业大学 软件学院
异常及处理.
Java程序设计 第2章 基本数据类型及操作.
4、按钮事件.
Ch02-基礎語法.
第五讲 JSP中的文件操作(1) 教学目的 本讲主要讲述JSP中使用Java输入、输出流实现文件的读写 操作 。 知识要点
4.2通讯服务模块线程之间传递信息 信息工程系 向模军 Tel: QQ:
Web Server 王宏瑾.
第4章 TCP/IP应用层常用协议 4.1 Telnet 协 议 4.2 文件传输协议FTP 4.3 电子邮件的工作原理及其协议
《JAVA程序设计》 语音答疑 辅导老师:高旻.
第二章Java基本程序设计.
第二章 Java基本语法 讲师:复凡.
Java程式初體驗大綱 大綱 在學程式之前及本書常用名詞解釋 Hello Java!程式 在Dos下編譯、執行程式
2019/5/3 JAVA Socket(UDP).
第二章 Java语法基础.
第二章 Java基本语法 讲师:复凡.
第二章 Java基本语法 讲师:复凡.
Java程序设计 第17章 异常和断言.
助教:廖啟盛 JAVA Socket(UDP) 助教:廖啟盛
PPT注意事项: 当前PPT课件文件必须和提供的源代码文件夹“代码”在同一目录中即不要移动文件夹“代码”的默认位置。
第2章 Java语言基础.
判斷(選擇性敘述) if if else else if 條件運算子.
第十二章 Java网络编程 1.URL编程 2.Socket网络编程 3.Datagram网络编程.
第二章 Java基础语法 北京传智播客教育
第二章 Java基本语法 讲师:复凡.
Summary
Presentation transcript:

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

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

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

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

问题

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)

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

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

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

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

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"; //计算连接所花的时间 }

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); }

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

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

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

2.2HTTPClient展示输入输出流 public void communicate()throws Exception{ StringBuffer sb=new StringBuffer("GET "+"/crm/About.jsp"+" HTTP/1.1\r\n"); sb.append("Host: www.zh-hr.com\r\n"); 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(); //关闭输出流

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(); }

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

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

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();

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

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

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); }

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

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

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

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; //关闭输出流,再结束程序

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);

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(); } }

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

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

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

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

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

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

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

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

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())); //把字节数组转换为字符串 }}

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(); }

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秒,才关闭和丢弃剩余数据。

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"); }}

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())); //把字节数组转换为字符串 }}

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

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

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

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

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

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

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)

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

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

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

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

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

主要的STMP命令和应答码

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

单端E-mail系统原理图

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

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

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

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

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

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

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连接

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; }

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( "tom@abc.com", //发送者的邮件地址 "linda@def.com", //接收者的邮件地址 "hello", //邮件标题 "hi,I miss you very much."); //邮件正文 new MailSender().sendMail(msg); }

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();} } }

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));

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