Java
网络编程
计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来的,实现资源共享和数据传输的系统。网络编程就是编写程序使互联网的两个或多个设备(如计算机)之间进行数据传输。Java
语言对网络编程提供了良好的支持。通过其提供的接口我们可以很方便地进行网络编程。
IP
地址相关操作
InetAddress
是
Java
对
IP
地址的封装。其下有两个子类
Inet4Address
和
Inet6Address
。这个类的实例经常和UDP DatagramSockets
和
Socket
,
ServerSocket
类一起使用
InetAddress
没有公开的构造方法,因此你必须通过一系列静态方法中的某一个来获取它的实例。
//www.baidu.com
实际上是主机名称
//IPv4
采用的是点分十进制的记法,所以难以记忆,
//
引入
DNS
可以将一个有含义的字符串和一个
IP
地址建立对应关系,当使用有含义的字符串时可以通过
DNS
转
换为
IP
地址
//InetAddress
对象中可以封装一个
IP
地址和一个主机名
InetAddress ia
=
InetAddress
.
getByName
(
"www.baidu.com"
);
//
获取对应主机名称的
IP
地
址,会自动调用
DNS
服务
System
.
out
.
println
(
ia
);
System
.
out
.
println
();
InetAddress
[]
arr
=
InetAddress
.
getAllByName
(
"www.baidu.com"
);
//
获取对应主机
名称的所有注册
IP
地址
for
(
InetAddress tmp
:
arr
)
System
.
out
.
println
(
tmp
);
System
.
out
.
println
();
// new InetAddress()
没有提供可以直接使用的构造器,所以不能直接
new
创建对象
ia
=
InetAddress
.
getLocalHost
();
//
获取本地机的
IP
地址
System
.
out
.
println
(
ia
);
//lover/172.30.1.101
ia
=
InetAddress
.
getLoopbackAddress
();
//
获取回绕地址
System
.
out
.
println
(
ia
);
//localhost/127.0.0.1
System
.
out
.
println
(
ia
.
getClass
());
//class java.net.Inet4Address
System
.
out
.
println
();
//
通过
IP
地址查询对应信息
InetAddress ia1
=
InetAddress
.
getByName
(
"14.215.177.39"
);
System
.
out
.
println
(
ia1
);
URL
访问网上资源
URL
对象代表统一资源定位器,是指向互联网资源的指针,资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数据库或搜索引擎的查询。用协议名、主机、端口和资源组成,即满足格式:
protocol://host:port/resourceName
,例如
http://www.yan.com:80/index.php
http
协议的标准端口为
80 https=http+SSL
加密数据传输的
http
,标准端口为
443
URL
统一资源指针,可以指定一个具体的资源,例如一个
html
网页
URI
统一资源标识符,可以认为为了方便记忆,给
URL
起的别名
构建
URL
对象
//
创建一个
URL
对象,用于指代网络中的一个资源,如果网址不合法,则抛出
MalformedURLException
URL url = new
URL("
https://news.cctv.com/2022/09/12/ARTIZ8cD6tXepRSQ5cFIHyA5220912.shtml
");
最重要的方法:
url.openConnection()
:
URLConnection
可以获取输入、输出流
url.openStream()
:
InputStream
直接获取输入流
不重要的方法
:
通过
URL
对象的一些方法可以访问该
URL
对应的资源:
String getFile():
获取该
URL
的资源名
String getHost():
获取主机名
String getPath():
获取路径部分
int getPort():
获取端口号
URL
与
URLConnection
比较
URL
和
URLConnection
的区别在于前者代表一个资源的位置
,
后者代表一种连接
Java
语言提供了两种方法读取数据,一种是通过
URL
对象直接得到相关的网络信息
InputStream is =
url.openStream()
;
,另一种是先得到一个
URLConnection
实例,再得到
InputStream
或
InputStream
对象,然后读取数据前者是一种简单、直接的方法,但缺乏灵活性,并且只能读取只读信息,后者提供了更加灵活有效的方法来读取网络资源
TCP
编程
在实际应用中
TCP
网络程序提供可靠的数据通信,而
UDP
网络程序则不保证数据的可靠性,但是协议简单、传输速度快(比如用在音视频数据传输,它们不需要很高的可靠性,偶尔丢帧是可以忍受的)
TCP
是
Tranfer Control Protocol
的 简称,是一种面向连接的端对端的保证可靠传输的协议。
通过
TCP
协议传输,得到的是一个顺序的无差错的数据流。
//
用于判断是否可以连接
ia
对象对应的主机,可以理解为尝试是否能够
ping
通
System
.
out
.
println
(
ia1
.
isReachable
(
2000
));
URLConnection conn
=
url
.
openConnection
();
InputStream is1
=
conn
.
getInputStream
();
OutputStream os1
=
conn
.
getOutputStream
();
发送方和接收方的成对的两个
socket
之间必须建立连接,以便在
TCP
协议的基础上进行通信,当一个 socket(通常都是
server socket
)等待建立连接时,另一个
socket
可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。
Socket
概述
Socket
套接字,就是两台主机之间逻辑连接的端点。
TPC
协议是传输层协议,主要解决数据如何在网络中传输,而HTTP
是应用层协议,主要解决如何包装数据。
Socket
本质上就是一组接口,是对
TCP/IP
协议的封装和应用(
程序员层面上
)
Socket
编程主要涉及到客户端和服务器端两个方面,首先是在服务器端创建一个服务器套接字
ServerSocket
,并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是
0
到
65536
,但是0
到
1024
是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者
IP
地址,加上端口号,打开一个套接字。
当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。
Socket
整体流程
Socket
编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字
ServerSocket
,并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是
0
到
65536
,但是0
到
1024
是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者
IP
地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。
ServerSocket
类
Java.net
包中的
ServerSocket
类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50
。
构造方法主要有以下几种形式:
ServerSocket():
创建非绑定服务器套接字。
ServerSocket(int port)
:
创建绑定到特定端口的服务器套接字。
Port
的取值范围为
0-65535
之间,
0
表示使用任意未占用端口,建议使用的端口号大于
1024
。如果端口已经被占用则会
BindException ServerSocket(int port,int backlog):利用指定的
backlog
创建服务器套接字并将其绑定到指定的本地 端口号。
ServerSocket(int port,int backlog,InetAdress bindAddress):
使用指定的端口、监听
backlog
和要
绑定到本地
IP
地址创建服务器,适用于计算机有多个网卡、多个
IP
的情景
端口扫描
cmd
命令查看端口使用情况
netstat
-
an
LISTENING
是指开放着的,等待连接的
//
利用的是
ServerSocket
在创建时,如果端口已经被占用,则报异常
for
(
int
i
=
0
;
i
<=
65535
;
i
++
) {
//
端口号
0
表示使用自由端口,实际上是不能建立连接的
try
{
ServerSocket ss
=
new
ServerSocket
(
i
);
ss
.
close
();
//finally
}
catch
(
Exception e
) {
System
.
out
.
println
(
"
端口
"
+
i
+
"
已经被占用
"
);
}
}
ESTABLISHED
是正在连接
CLOSE_WAIT
、
TIME_WAIT
、
SYN_SENT
是三次握手四次挥手过程中的某些状态
开放端口包括三种:
0.0.0.0
:端口号
127.0.0.1
:端口号
主机
ip
:端口号
区别是
0.0.0.0
和主机
ip
后面跟的端口号是对外部网络开放的,是可以通过服务域名、
ip
可以访问的端口,而127.0.0.1
的端口则是只供本机访问的端口。
客户端
Socket
的构造器
Socket(InetAddress address, int port);
创建远程连接到指定服务器
Socket(String host, int prot);
在客户端构建
Socket
对象,如果构建成功则获取对象,否则
ConnectException
。参数
1
为链接主机的名称,也可以使用
InetAddress
表示
IP
地址;参数
2
为链接服务器的监听端口号,要求服务器已经打开的链接端口
Socket(String host, int port, InetAddress localAddr, int localPort)
Socket(InetAddressaddress, int port, InetAddress localAddr, int localPort)
简单的
Client/Server
程序
C/S
架构全称为客户端
/
服务器体系结构,它是一种网络体系结构,其中客户端是用户运行应用程序的
PC端或者工作站,客户端要依靠服务器来获取资源。C/S
架构是通过提供查询响应而不是总文件传输来减少了网络流量。它允许多用户通过GUI
前端更新到共享数据库,在客户端和服务器之间通信一般采用远程
调用
RPC
或标准查询语言
SQL
语句。
C/S
架构的基本特征:
客户端进程包含特定于解决方案的逻辑,并提供用户与应用程序系统其余部分之间的接口。服务器
进程充当管理共享资源(如数据库,打印机,调制解调器或高性能处理器)的软件引擎
前端任务和后端任务对计算资源有着根本不同的要求,例如处理器速度,内存,磁盘速度和容量以
及输入
/
输出设备 客户端和服务器的硬件平台和操作系统通常不相同。客户端和服务器进程通过一组明确定义的标准
应用程序接口
API
和
RPC
进行通信 C/S架构的一个重要特征是可扩展性,它们可以水平或垂直缩放。水平扩展意味着添加或删除客户
端,工作站只会对性能产生轻微影响。垂直扩展意味着迁移到更大更快的服务器计算机或多服务器
中。
服务器端套路
创建
ServerSocket
对象,绑定监听端口。
通过
accept
方法监听客户端请求。
连接建立后,通过输入流读取客户端发送的请求信息。
通过输出流向客户端发送响应信息。
//java.net.ConnectException: Connection refused: connect
Socket socket
=
new
Socket
(
"127.0.0.1"
,
9999
);
//cmd
命令
netstat -an | findstr
"9999"
System
.
out
.
println
(
socket
);
关闭响应的资源。
客户端套路
创建
Socket
对象,指明需要连接的服务器的地址和端口号。
连接建立后,通过输出流向服务器发送请求信息。
通过输入流获取服务器响应的信息。
关闭相应资源。
ServerSocket ss
=
new
ServerSocket
(
8000
);
//
阻塞当前线程的执行,并等待客户端的链接请求,如果链接成功则返回一个
Socket
对象
Socket socket
=
ss
.
accept
();
//
通过链接对象
Socket
可以获取一个输入流和一个输出流
InputStream is
=
socket
.
getInputStream
();
OutputStream os
=
socket
.
getOutputStream
();
//
具体的数据接收处理逻辑
BufferedReader br
=
new
BufferedReader
(
new
InputStreamReader
(
is
));
//
这里使用的是
BIO
,所以当客户端没有发送数据时,会阻塞等待
String
str
=
br
.
readLine
();
System
.
out
.
println
(
"Server:"
+
str
);
//
服务器回传系统当前时间
PrintStream ps
=
new
PrintStream
(
os
);
Date now
=
new
Date
();
DateFormat df
=
new
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
);
String
sdate
=
df
.
format
(
now
);
ps
.
println
(
sdate
);
ps
.
flush
();
//
发送完毕,关闭流和套接字,完善的写法为
try-finally
结构
ps
.
close
();
br
.
close
();
socket
.
close
();
//
也可以使用回绕地址
127.0.0.1
。注意这里的端口号必须和服务器的端口号一致,否则不能链接
//
如果链接成功则返回
Socket
对象
Socket socket
=
new
Socket
(
"localhost"
,
8000
);
//
通过链接对象
Socket
可以获取一个输入流和一个输出流
InputStream is
=
socket
.
getInputStream
();
OutputStream os
=
socket
.
getOutputStream
();
//
具体的请求逻辑
PrintStream ps
=
new
PrintStream
(
os
);
ps
.
println
(
"Hello Server!"
);
//
向服务器发送一个内容为
Hello Server!
的字符串
//
客户端接收服务器传回的系统当前时
BufferedReader br
=
new
BufferedReader
(
new
InputStreamReader
(
is
));
String
sdate
=
br
.
readLine
();
//
阻塞等待
System
.
out
.
println
(
"Client:"
+
sdate
);
//
关闭
br
.
close
();
ps
.
close
();
socket
.
close
();
一般的编程实现方式
主线程一直处于阻塞等待状态,如果一旦链接建立成功则启动一个新线程对外提供服务,而主线程继续
等待连接请求
依据:监听端口在连接创建期间被占用,连接一旦创建则继续处于空闲状态
public class
MyServer
{
public static
void
main
(
String
[]
args
)
throws
Exception
{
ServerSocket server
=
new
ServerSocket
(
9999
);
while
(
true
) {
Socket socket
=
server
.
accept
();
new
MyWorker
(
socket
).
start
();
}
}
}
class
MyWorker
extends
Thread
{
private
Socket socket
;
public
MyWorker
(
Socket socket
) {
this
.
socket
=
socket
;
}
@Override
public
void
run
() {
BufferedReader br
=
null
;
PrintStream ps
=
null
;
try
{
InputStream is
=
socket
.
getInputStream
();
OutputStream os
=
socket
.
getOutputStream
();
br
=
new
BufferedReader
(
new
InputStreamReader
(
is
));
ps
=
new
PrintStream
(
os
);
String
ss
=
br
.
readLine
();
System
.
out
.
println
(
"Client:"
+
ss
);
if
(
"hello"
.
equals
(
ss
)) {
Date now
=
new
Date
();
DateFormat df
=
new
SimpleDateFormat
(
"yyyy-MM-ddE hh:mm:ss"
);
ss
=
df
.
format
(
now
);
ps
.
println
(
ss
);
}
}
catch
(
Exception e
) {
// e.printStackTrace();
throw new
RuntimeException
(
e
);
}
finally
{
if
(
br
!=
null
)
try
{
br
.
close
();
}
catch
(
IOException e
) {
e
.
printStackTrace
();
}
if
(
ps
!=
null
)
ps
.
close
();
if
(
socket
!=
null
)
try
{
socket
.
close
();
}
catch
(
IOException e
) {
c/s
和
b/s
区别
C/S
(
Client/Server
,客户机
/
服务器)结构,胖客户端应用,是软件系统体系结构的一种。
C/S
模
式简单地讲就是基于企业内部网络的应用系统,大部分的应用逻辑都集中在客户端中,而一般服务
器端只提供数据的存储。与
B/S
(
Browser/Server
,浏览器
/
服务器)模式相比,
C/S
模式的应用系
统最大的好处是不依赖企业外网环境,即无论企业是否能够上网,都不影响应用。
B/S
结构(
Browser/Server
结构)结构即浏览器和服务器结构,瘦客户端应用,主要逻辑集中在服
务器端,客户端一般只包含的简单的显示逻辑。它是随着
Internet
技术的兴起,对
C/S
结构的一种变
化或者改进的结构。在这种结构下,用户工作界面是通过
WWW
浏览器来实现,极少部分事务逻辑
在前端
Browser
实现,但是主要事务逻辑在服务器端
Server
实现,形成所谓三层
3-tier
结构。
C/S
聊天室的实现
服务器端应用包含多个线程,每个
Socket
对应一个线程,该线程负责读取
Socket
对应输入流的数据,并将读到的数据向每个Socket
发送一次
每个客户端应该包含两个线程,一个负责读取用户键盘输入并将用户输入数据写入
Socket
对应的流中,一个负责读取Socket
对应输入流的数据并打印显示
模拟粘包效果
e
.
printStackTrace
();
}
}
}
}