Java网络编程之TCP/IP,源,多线程和Internet简介
1.TCP/IP的四层模型
A.主机网络层(链路层,数据链路层,网络接口层)
实际上主机网络层定义了一个特定的网络接口(如以太网卡或WIFI天线)如何通过物理连接向本地网络或世界其它地方发送IP数据报。TCP/IP参考模型没有真正描述主机网络层的实现,它只要求能够提供给其上层(网络互连层)一个访问接口以便在其上面传递IP分组。由于数据链路层未被定义,所以其具体的实现方法将随着网络类型的不同而不同。
B.网际层(网络层)
网络层协议定义了数据位和字节如何组织为更大的分组(称为包),还定义了寻址地址,不同计算机要按这个寻址地址查找对方。在IPv4和IPv6中,数据按包在网际层上传输,这些包称为数据报(datagram)。网际层是整个TCP/IP协议栈的核心。它的功能是把分组发往目标网络或者主机。同时为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。因此分组到达的顺序和发送的顺序可能不同,这就需要上层必须对分组进行排序。网际层还定义了分组格式和协议,即IP协议(Internet Protocol)。网际层除了需要完成路由的功能外,也可以完成将不同类型的网络(异构网)互连的任务。除此之外网际层还需要完成拥塞控制的功能。
C.传输层
传输层负责确保各包以发送的顺序接收,并保证没有数据丢失或破坏。在TCP/IP模型中,传输层的功能是使源端主机和目标端主机上的对等实体可以进行会话。在传输层定义了两种服务质量不同的协议。即:传输控制协议TCP(transmission control protocol)和用户数据报协议UDP(user datagram protocol)。TCP协议是一个面向连接的,可靠的协议。它将一台主机发出的字节流无差错地发往互联网上的其他主机。在发送端它负责把上层传送下来的字节流分成报文段并传递给下层。在接收端它负责把收到的报文进行重组后递交给上层。TCP协议还要处理端到端的流量控制,以避免缓慢接收的接收方没有足够的缓冲区接收发送方发送的大量数据。
UDP协议是不可靠的,无连接协议,主要适用于不需要对报文进行排序和流量控制的场合。
D.应用层
向用户传送数据的层称为应用层,应用层确定了数据传输后的操作。应用层下面的三层共同定义了数据如何从一台计算机传输到另一台计算机。应用层面向不同的网络应用引入了不同的应用层协议。其中有基于TCP协议的,如文件传输协议(File Transfer Protocol,FTP),虚拟终端协议(TELNET),超文本链接协议(Hyper Text Transfer Protocol,HTTP),也有基于UDP协议的。
2.流(Stream)的基本简介
流在发送缓冲区中的数据之前会等待更多的数据到达,在服务器响应到达之前不会向流写入更多的数据,但是由于请求还没有发送故响应永远不会到来,OutputStream的flush()方法可以强迫缓冲的流发送数据,即使缓冲区还没满。
如果不想等待所需的全部字节都立即可用,可以使用available()方法来确定不阻塞的情况下有多少字节可以读取,它会返回可以读取的最少字节数。Reader类有个ready()方法与之类似,但是InputStream的available()方法返回一个Int并指定可以无阻塞的最少读取多少字节,然而ready()方法只返回一个boolean,指示阅读器是否可以无阻塞的读取。
PrintStream是有害的,因为println()的输出是与平台有关的,而且PrintStream假定使用所在平台的默认编码方式,除此之外PrintStream吞掉了所有异常。
3.多线程的基本简介
程序生成了多少线程,系统的CPU和磁盘的速度,系统使用多少个CPU,Java虚拟机为不同线程分配时间所用的算法,这些称为静态条件(race condition)。
Future的get()方法会产生阻塞。
String参数是安全的,因为它们是不可变的(immutable)的,String对象一旦创建它就不能被任何线程修改。不可变对象永远也不会改变状态,其字段的值只能在构造函数运行时设置一次,其后就不会再改变。使用不可变的对象是同步的替代方式中的一种。
构造函数的线程安全问题:构造函数依赖于另一个线程中的另一个对象,这个对象可能在构造函数运行时改变;构造函数以某种方式把它正在创建的对象的引用传递给一个不同的线程。
4.Internet地址
IPv4或者IPv6,这里的4和6指的是Internet协议的版本,不是地址中的字节数。
5.日志处理示例
import java.net.InetAddress;
import java.util.concurrent.Callable;
public class LookupTask implements Callable<String> {
private String line;
public LookupTask(String line) {
this.line = line;
}
@Override
public String call() throws Exception {
try {
// 分解IP地址
int index = line.indexOf(' ');
String address = line.substring(0, index);
String theRest = line.substring(index);
String hostName = InetAddress.getByName(address).getHostName();
return hostName + " " + theRest;
} catch (Exception ex) {
return line;
}
}
}
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class PooledWebLog {
private final static int NUM_THREADS = 4;
public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
Queue<LogEntry> results = new LinkedList<LogEntry>();
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(args[0]), "UTF-8"));) {
for (String entry = in.readLine(); entry != null; entry = in.readLine()) {
LookupTask task = new LookupTask(entry);
Future<String> future = executor.submit(task);
LogEntry result = new LogEntry(entry, future);
results.add(result);
}
}
for (LogEntry result : results) {
try {
System.out.println(result.future.get());
} catch (InterruptedException | ExecutionException ex) {
System.out.println(result.original);
}
}
}
private static class LogEntry {
String original;
Future<String> future;
public LogEntry(String original, Future<String> future) {
this.original = original;
this.future = future;
}
}
}