主要内容
软件架构 CS / BS
网络通信三要素
TCP 通信
Socket 套接字
ServerSocket
第一章 网络编程入门
1.1 软件结构
C/S 结构 :全称为 Client/Server 结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
B/S 结构 :全称为 Browser/Server 结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。 网络编程 ,就是在一定的协议下,实现两台计算机
的通信的程序。
1.2 网络通信协议
网络通信协议: 通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。这就
好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对数据的传输格式、传输速率、传输步骤等做了
统一规定,通信双方必须同时遵守,最终完成数据交换。
TCP/IP 协议: 传输控制协议 / 因特网互联协议 ( Transmission Control Protocol/Internet Protocol) ,是
Internet 最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它
的内部包含一系列的用于处理数据通信的协议,并采用了 4 层的分层模型,每一层都呼叫它的下一层所提供的
协议来完成自己的需求。
1.3 协议分类
通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这
些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net 包中提供了两种常见的网络协议的支持:
TCP :传输控制协议 (Transmission Control Protocol) 。 TCP 协议是 面向连接 的通信协议,即传输数据之前,
在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
三次握手: TCP 协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可
靠。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性, TCP 协议可
以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
UDP :用户数据报协议 (User Datagram Protocol) 。 UDP 协议是一个 面向无连接 的协议。传输数据时,不需
要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个
数据包的大小限制在 64k 以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应
用中 , 例如视频会议、 QQ 聊天等。
每次发送的数据最大为 64kb
1.4 网络编程三要素
协议
协议: 计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。
IP 地址
IP 地址:指互联网协议地址( Internet Protocol Address ) ,俗称 IP 。 IP 地址用来给一个网络中的计算机设
备做唯一的编号。假如我们把 “ 个人电脑 ” 比作 “ 一台电话 ” 的话,那么 “IP 地址 ” 就相当于 “ 电话号码 ” 。
IP 地址分类
IPv4 :是一个 32 位的二进制数,通常被分为 4 个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。其
中 a 、 b 、 c 、 d 都是 0~255 之间的十进制整数,那么最多可以表示 42 亿个。
IPv6 :由于互联网的蓬勃发展, IP 地址的需求量愈来愈大,但是网络地址资源有限,使得 IP 的分配越发紧张。
有资料显示,全球 IPv4 地址在 2011 年 2 月分配完毕。
为了扩大地址空间,拟通过 IPv6 重新定义地址空间,采用 128 位地址长度,每 16 个字节一组,分成 8 组十六进
制数,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网
址,这样就解决了网络地址资源数量不够的问题。
常用命令
查看本机 IP 地址,在控制台输入:
ipconfig
检查网络是否连通,在控制台输入:
ping 空格 IP 地址
ping www . baidu . com
特殊的 IP 地址
本机 IP 地址: 127.0.0.1 、 localhost 。
端口号
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分
这些进程呢?
如果说 IP 地址 可以唯一标识网络中的设备,那么 端口号 就可以唯一标识设备中的进程(应用程序)了。
端口号:用两个字节表示的整数,它的取值范围是 0~65535 。其中, 0~1023 之间的端口号用于一些知名的网
络服务和应用,普通的应用程序需要使用 1024 以上的端口号。如果端口号被另外一个服务或应用所占用,会
导致当前程序启动失败。
利用 协议 + IP 地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其
它进程进行交互。
第二章 TCP 通信程序
2.1 概述
TCP 通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端( Client )与服务端( Server )。
两端通信时步骤:
1. 服务端程序,需要事先启动,等待客户端的连接。
2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
在 Java 中,提供了两个类用于实现 TCP 通信程序:
1. 客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建
立连接开始通信。
2. 服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端
的连接。
2.2 Socket 类
Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
构造方法
public Socket(String host, int port) : 创建套接字对象并将其连接到指定主机上的指定端口号。如果指
定的 host 是 null ,则相当于指定地址为回送地址。
回送地址 (127.x.x.x) 是本机回送地址( Loopback Address ),主要用于网络软件测试以及本
地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。
构造举例,代码如下:
Socket client = new Socket ( "127.0.0.1" , 6666 );
成员方法
public InputStream getInputStream() : 返回此套接字的输入流。
如果此 Scoket 具有相关联的通道,则生成的 InputStream 的所有操作也关联该通道。
关闭生成的 InputStream 也将关闭相关的 Socket 。
public OutputStream getOutputStream() : 返回此套接字的输出流。
如果此 Scoket 具有相关联的通道,则生成的 OutputStream 的所有操作也关联该通道。
关闭生成的 OutputStream 也将关闭相关的 Socket 。
public void close() :关闭此套接字。
一旦一个 socket 被关闭,它不可再使用。
关闭此 socket 也将关闭相关的 InputStream 和 OutputStream 。
public void shutdownOutput() : 禁用此套接字的输出流。
任何先前写出的数据将被发送,随后终止输出流。
2.3 ServerSocket 类
ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。
构造方法
public ServerSocket(int port) :使用该构造方法在创建 ServerSocket 对象时,就可以将其绑定到一个指
定的端口号上,参数 port 就是端口号。
构造举例,代码如下:
ServerSocket server = new ServerSocket ( 6666 );
成员方法
public Socket accept() :侦听并接受连接,返回一个新的 Socket 对象,用于和客户端实现通信。该方法
会一直阻塞直到建立连接。
2.4 简单的 TCP 网络程序
TCP 通信分析图解
1. 【服务端】启动 , 创建 ServerSocket 对象,等待连接。
2. 【客户端】启动 , 创建 Socket 对象,请求连接。
3. 【服务端】接收连接 , 调用 accept 方法,并返回一个 Socket 对象。
4. 【客户端】 Socket 对象,获取 OutputStream ,向服务端写出数据。
5. 【服务端】 Scoket 对象,获取 InputStream ,读取客户端发送的数据。
到此,客户端向服务端发送数据成功。
自此,服务端向客户端回写数据。
6. 【服务端】 Socket 对象,获取 OutputStream ,向客户端回写数据。
7. 【客户端】 Scoket 对象,获取 InputStream ,解析回写数据。
8. 【客户端】释放资源,断开连接。
客户端向服务器发送数据
服务端实现:
public class ServerTCP {
public static void main (
String
[]
args
)
throws
IOException
{
System . out .
println
(
"
服务端启动
,
等待连接
.... "
);
// 1. 创建 ServerSocket 对象,绑定端口,开始等待连接
ServerSocket ss = new ServerSocket
(
6666
);
// 2. 接收连接 accept 方法
,
返回
socket
对象
.
Socket server = ss .
accept
();
// 3. 通过 socket 获取输入流
InputStream is = server .
getInputStream
();
// 4. 一次性读取数据
// 4.1 创建字节数组
byte [] b =
new
byte
[
1024
];
// 4.2 据读取到字节数组中 .
int len = is
.
read
(
b
)
;
// 4.3 解析数组 , 打印字符串信息
String msg = new
String
(
b
,
0
,
len
);
System . out .
println
(
msg
);
//5. 关闭资源 .
is . close ();
server . close ();
}
}
客户端实现:
public class ClientTCP {
public static void main (
String
[]
args
)
throws
Exception
{
System . out .
println
(
"
客户端 发送数据
"
);
// 1. 创建 Socket ( ip , port ) , 确定连接到哪里
.
Socket client = new Socket
(
"localhost"
,
6666
);
// 2. 获取流对象 . 输出流
OutputStream os = client .
getOutputStream
();
// 3. 写出数据 .
os . write (
"
你好么
? tcp ,
我来了
"
.
getBytes
());
// 4. 关闭资源 .
os . close ();
client . close ();
}
}
服务器向客户端回写数据
服务端实现:
public class ServerTCP {
public static void main (
String
[]
args
)
throws
IOException
{
System . out .
println
(
"
服务端启动
,
等待连接
.... "
);
// 1. 创建 ServerSocket 对象,绑定端口,开始等待连接
ServerSocket ss = new ServerSocket
(
6666
);
// 2. 接收连接 accept 方法
,
返回
socket
对象
.
Socket server = ss .
accept
();
// 3. 通过 socket 获取输入流
InputStream is = server .
getInputStream
();
// 4. 一次性读取数据
// 4.1 创建字节数组
byte [] b =
new
byte
[
1024
];
// 4.2 据读取到字节数组中 .
int len = is
.
read
(
b
)
;
// 4.3 解析数组 , 打印字符串信息
String msg = new
String
(
b
,
0
,
len
);
System . out .
println
(
msg
);
// ================= 回写数据 =======================
// 5. 通过 socket 获取输出流
OutputStream out = server .
getOutputStream
();
// 6. 回写数据
out . write (
"
我很好
,
谢谢你
"
.
getBytes
());
// 7. 关闭资源 .
out . close ();
is . close ();
server . close ();
}
}
客户端实现:
public class ClientTCP {
public static void main (
String
[]
args
)
throws
Exception
{
System . out .
println
(
"
客户端 发送数据
"
);
// 1. 创建 Socket ( ip , port ) , 确定连接到哪里
.
Socket client = new Socket
(
"localhost"
,
6666
);
// 2. 通过 Scoket, 获取输出流对象
OutputStream os = client .
getOutputStream
();
// 3. 写出数据 .
os . write (
"
你好么
? tcp ,
我来了
"
.
getBytes
());
// ============== 解析回写 =========================
// 4. 通过 Scoket, 获取 输入流对象
InputStream in = client .
getInputStream
();
// 5. 读取数据数据
byte [] b =
new
byte
[
100
];
int len = in
.
read
(
b
);
System . out .
println
(
new
String
(
b
,
0
,
len
));
// 6. 关闭资源 .
in . close ();
os . close ();
client . close ();
}
}