前言
计算机网络是指两台或更多的计算机组成的网络,在同一个网络中,任意两台计算机都可以直接通信,因为所有计算机都需要遵循同一种网络协议。
一、概述。
互联网是网络的网络( internet ),即把很多计算机网 络连接起来,形成一个全球统一的互联网。对某个特定的计算机网络来说,它可能使用网络协议ABC,而另一个计算机网络可能使用网络协议XYZ。如果计算机网络 各自的通讯协议不统一, 就没法把不同的网络连接起来形成互联网。因此,为了把计 算机网络接入互联网,就必须使用TCP/IP 协议。TCP/IP协议泛指互联网协议,其中最重要的两个协议是TCP协议和IP协议。只有使用TCP/IP协议的计算机才能够联入互联网,使用其他网络协议(例如NetBI0S、AppleTalk协议等)是无法联入互联网的。
网络模型
由于计算机网络从底层的传输到高层的软件设计十分复杂,要合理地设计计算机网络模型,必须采用分层模型,每一层负责处理自己的操作。网络模型是ISO组织定义的一一个计算机互联的标准模型,注意它只是一个定义,目的是为了简化网络各层的操作,提供标准接口便于实现和维护。这个模型从上到下依次是:
●应用层,提供应用程序之间的通信;
●表示层:处理数据格式,加解密等等;
●会话层:负责建立和维护会话;
●传输层:负责提供端到端的可靠传输;
●网络层:负责根据目标地址选择路由来传输数据;
●数据链路层和物理层:负责把数据进行分片并且真正通过物理网络传输,例如,无线网、光纤等。
常用协议
IP协议是一个分组交换协议,它不保证可靠传输。而TCP协议是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。TCP协议是建立在IP协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协议负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接收、确认、超时、 重传这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。
TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTP、SMTP等。
二、TCP编程
1,什么是Socket?
在开发网络应用程序的时候,会遇到Socket 这个概念。Socket 是-个抽象概念, 一个应用程序通过一个Socket 来建立一个远程连接,而Socket 内部通过TCP/IP协议把数据传输到网络。
Socket、TCP 和部分IP的功能都是由操作系统提供的,不同的编程语言 只是提供了对操作系统调用的简单的封装。例如,Java 提供的几个Socket 相关 的类就封装了操作系统提供的接、口:1 ServerSocket 类、| Socket 类。
为什么需要Socket进行网络通信?因为仅仅通过IP地址进行通信是不够 的,同一台计算机同一时间会运行多个网络应用程序,例如浏览器、QQ、 邮件客户 端等。当操作系统接收到一个数据包的时候,如果只有IP 地址,它没法判断应该发给哪个应用程序,所以,操作系统抽象出Socket接口,每个应用程序需要各自对 应到不同的Socket,数据包才能根据Socket 正确地发到对应的应用程序。
一个Socket 就是由IP地址和端口号(范围是0~ 65535)组成,可以把Socke t简单理解为IP地址加端口号。端口号总是由操作系统分配,它是一个0 ~ 655 35之间的数字,其中,小于1024 的端口属于特权端口,需要管理员权限,大于1024的端口可以由任意用户的应用程序打开。
●101.202.99.2:1201
●101.202.99.2:1304
●101.202.99.2:15000
使用Socket 进行网络编程时,本质上就是两个进程之间的网络通信。其中一个 进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。
因此,当Socket 连接成功地在服务器端和客户端之间建立后:
●对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
●对客户端来说,它的Socket是它所在计算机的IP地址和一一个由操作系统分配的随机端口号。
服务器端
要使用Socket编程,我们首先要编写服务器端程序。Java 标准库提供了ServerSocket来实现对指定IP和指定端口的监听。Serversocket的典型实现代码如下:
package Cm1;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class ChatServer {
public static void main(String[] args) {
Map<String, String> chatMap = new HashMap<String, String>() {
{
put("你好", "你好呀");
put("hi", "hi~");
put("hello", "哈喽");
put("吃了吗", "没呢,你呢");
put("孤勇者", "爱你孤身走暗巷");
put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学.....");
put("很高兴认识你", "我也是哦");
}
};
try (ServerSocket server = new ServerSocket(2333)) {
while (true) {
Socket client = server.accept();
String clientIp = client.getInetAddress().getHostAddress();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));) {
String question = reader.readLine();
if (question != null) {
System.out.println("[服务器]来自客户端" + clientIp + "的提问" + question);
String answer = chatMap.get(question);
answer = answer == null ? "我不知道你在说什么" : answer;
writer.write(answer);
writer.newLine();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
package In;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class chatClient {
public static void main(String[] args) {
Scanner input =new Scanner(System.in);
while(true) {
try(Socket client=new Socket("192.168.220.1",2333);
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(
client.getOutputStream()));
BufferedReader reader=new BufferedReader(new InputStreamReader(
client.getInputStream()));){
String question=input.nextLine();
if(question.equals("over")) {
break;
}
writer.write(question);
writer.flush();
client.shutdownOutput();
String answer=reader.readLine();
System.out.println("[客户端]来自服务器的回答"+answer);
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("Game over");
}
}
运行结果如下图:
客户端:
服务器端:
Socket流
当Socket 连接创建成功后,无论是服务器端,还是客户端,我们都使用Socke t实例进行网络通信。因为TCP是- -种基于流的协议,因此,Java标准库使用InputStream和OutputStream来封装Socket的数据流,这样我们使用Socket的流,和普通I0流类似:
// 用于读取网络数据:
InputStream in = sock.getInputStream();
// 用于写入网络数据:
OutputStream out = sock.getOutputStream();
写入网络数据时,必须要调用flush() 方法。如果不调用flush() ,我们很可能会发现,客户端和服务器都收不到数据,这并不是Java标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送到网络,而是先写入内 存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络, 就必须调用flush() 强制把缓冲区数据发送出去。
总结
● 使用Java进行TCP编程时,要使用Socket模型;
●服务器端用ServerSocket 监听指定端口;
●客户端使用Socket( InetAddress, port) 连接服务器;
●服务器端用accept() 接收连接并返回Socket 实例;
●双方通过Socket打开InputStream / outputStream 读写数据;
●服务器端通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率;flush()方法用于强制输出缓冲区到网络。