Java网络编程是基于Socket的编程,我们可以从以下5个方面学习,Java网络编程。
1、网络基础知识
2、InetAddress
3、URL
4、基于TCP的Socket编程
5、基于UDP的Socket编程
第一章 网络基础知识
1、IP地址、协议,端口的概念
IP地址现在的标准是IPv4
端口的作用就是区别同一台主机上不同应用程序的数据
范围是0-65535 其中0-1023为系统所保留的,最好别用
一些常见的端口为:
http:80 ftp :21 telnet:23
2、TCP/IP协议的模型
第二章 InetAddress
1、用于标识网络上的硬件资源,也就是互联网协议(IP)地址。
2、具体有哪些方法和方法对应的作用可查API帮助文档
第三章 URL
1、统一资源定位符,格式如下:
http: // www.imooc.com?username=tom&password=123#test
http为协议,?后面表示参数,#后面表示锚点
2、具体用法可以查API,其中几个有用的如openStream等可以记一下,会用到。
第四章 基于TCP进行Socket编程
1、所要用到的API类为:
ServerSocket、Socket
2、服务端对应的步骤为:
a、创建ServerSocket对象,并绑定相应的端口号。
b、通过ServerSocket对象的accept()方法对指定的端口进行Socket连接的监听。
c、获得指定的Socket连接对象
d、通过连接对象获得输入流,对客户端的输入进行读取。
e、通过连接对象获取输出流,将自己要发送给客户端的信息通过输出流发送给客户端
f、关闭相应的资源。
3、客户端需要做的事为:
@1、创建Socket对象对指定的主机的端口进行连接
@2、通过获取输出流,将要发送给服务器的信息写入。
@3、通过获取输入流,获得服务器的反馈信息。
@4、关闭相应的资源
代码示例如下:
案例背景:
实现一个类似与QQ的即时通讯工具,实现客户端与服务器端的即时通讯,由客户端发起对话,之后需要持续显示对方说话的内容来输入要输入的话。
在服务器端要首先读一下InputStream,看看客户端输入了什么,然后在决定输出什么,所以在循环外有一个独立的System. out.println("Client" +is.readLine());
然后在循环中再次读取。
public
class
TCPServer {
/**
*服务器端
*/
public
static
void
main(String[] args) {
try
{
ServerSocket server=
null
;
try
{
server=
new
ServerSocket(8000);
//创建serverSocket对象,对指定端口进行监听
}
catch
(Exception e){
System.
out
.println(
"Server cannot start normally!"
);
}
Socket socket=
null
;
try
{
socket=server.accept();
//通过serverSocket实例获得Socket对象,完成连接,此方法为阻塞方法
}
catch
(Exception e){
System.
out
.println(e);
}
String line;
BufferedReader is=
new
BufferedReader(
new
InputStreamReader(socket.getInputStream()));//获取输入流,读客户端发送的数据
PrintWriter os=
new
PrintWriter(socket.getOutputStream());//获取输出流,用来发送自己的数据
BufferedReader sin=
new
BufferedReader(
new
InputStreamReader(System.
in
));
System.
out
.println(
"Client"
+is.readLine());
line=sin.readLine();
while
(!line.equals(
"exit"
)){
os.println(line);
os.flush();//注意这里的顺序,因为顺序必须为读一次写一次那样,在此服务器端,先读客户的,上面的is.readLine()完了之后进行输入,然后将输入结果回馈给用户,这样用户得消息后会在输入,这样我们在下面的is.readLine()的内容就是下一次用户的输入了,一定要注意这个顺序。如果只输入一次,然后读两次,第二次就会一直读不到,程序阻塞。
System.
out
.println(
"Server:"
+line);
System.
out
.println(
"Client"
+is.readLine());
line=sin.readLine();
}
os.close();
is.close();
socket.close();
server.close();
}
catch
(Exception e){
e.printStackTrace();
}
}
}
public
class
TCPClient {
/**
*
客户端
*/
public
static
void
main(String[] args) {
try
{
Socket socket=
new
Socket(
"192.168.2.145"
,8000);
BufferedReader sin=
new
BufferedReader(
new
InputStreamReader(System.
in
));
PrintWriter os=
new
PrintWriter(socket.getOutputStream());
BufferedReader is=
new
BufferedReader(
new
InputStreamReader(socket.getInputStream()));
String readLine;
readLine=sin.readLine();
while
(!readLine.equals(
"exit"
)){
os.println(readLine);//由客户端开始,发送数据给服务器。
os.flush();
System.
out
.println(
"Client:"
+readLine);
System.
out
.println(
"Server:"
+is.readLine());
readLine=sin.readLine();
}
os.close();
is.close();
socket.close();
}
catch
(UnknownHostException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
4、
总结:在这里一定要理解输入输出流的意思,输入流和输出流的不同在于他们的作用不同,一个用来输入数据,一个用来输出数据,但是他们操作的是同一个文件,就比如说,就像在Socket中,InputStream中的数据是由OutputStream来输入的,他俩的内容是一样的,而且,输入一次才可以输出一次,比如你Writer了一次,现在可以Reader一次,如果read两次,第二次并不会执行,因为没有东西可以read,只能进行再次的write才能进行read。
那么现在我来写一个自己的QQ程序:跟上面差不多。
客户端:
public
class
TCPClient {
/*
* Socket 网络通信客户端
*/
public
static
void
main(String[] args)
throws
UnknownHostException, IOException {
Socket socket =
new
Socket(
"127.0.0.1"
,8888);
BufferedReader sysin=
new
BufferedReader(
new
InputStreamReader(System.
in
));
PrintWriter os=
new
PrintWriter(socket.getOutputStream());
BufferedReader is=
new
BufferedReader(
new
InputStreamReader(socket.getInputStream()));
String line;
while
(!(line=sysin.readLine()).equals(
"exit"
)){
os.println(line);
os.flush();
System.
out
.println(
"I am Client, what I say is:"
+line);
System.
out
.println(
"I am Client , what Server say is :"
+is.readLine());
}
os.close();
is.close();
socket.close();
}
}
服务器端:
public
class
TCPServer {
/**
* Socket网络通信服务器端
*/
public
static
void
main(String[] args) {
try
{
ServerSocket server=
null
;
try
{
server=
new
ServerSocket(8888);
}
catch
(Exception e){
System.
out
.println(
"Server cannot start normally!"
);
}
Socket socket=
null
;
try
{
System.
out
.println(
"************服务器已经启动****************"
);
socket=server.accept();
}
catch
(Exception e){
System.
out
.println(e);
}
BufferedReader sysin=
new
BufferedReader(
new
InputStreamReader(System.
in
));
BufferedReader is=
new
BufferedReader(
new
InputStreamReader(socket.getInputStream()));
PrintWriter os=
new
PrintWriter(socket.getOutputStream());
System.
out
.println(
"I am Server,Client says:"
+ is.readLine());
String line=sysin.readLine();
while
(!(line.equals(
"exit"
))){
System.
out
.println(
"I am Server,I am saying :"
+line);
os.println(line);
os.flush();//在这里就要注意顺序了。先把自己的数据发了再去获取下一次的。
System.
out
.println(
"I am Server,Client says:"
+ is.readLine());
line=sysin.readLine();
}
os.close();
is.close();
socket.close();
server.close();
}
catch
(Exception e){
e.printStackTrace();
}
}
}
5、基于多线程的Socket编程
在实际的网络通信中,通常是一个服务器要为很多台客户机提供服务,
在这种情况下就要对来自不同IP地址的连接进行分别的处理,首先想到的肯定是线程,因为线程是独立运行,在run()方法中可以对需要独立执行的代码进行执行。
此时就要建立一个Socket连接处理的线程类,每获得一个Socket连接,就要实例化一个该类。
public
class
ServerThread
extends
Thread {
// 和本线程相关的Socket
Socket
socket
=
null
;
public
ServerThread(Socket socket) {
this
.
socket
= socket;
}
//线程执行的操作,响应客户端的请求
public
void
run(){
InputStream is=
null
;
InputStreamReader isr=
null
;
BufferedReader br=
null
;
OutputStream os=
null
;
PrintWriter pw=
null
;
try
{
//获取输入流,并读取客户端信息
is =
socket
.getInputStream();
isr =
new
InputStreamReader(is);
br =
new
BufferedReader(isr);
String info=
null
;
while
((info=br.readLine())!=
null
){
//循环读取客户端的信息
System.
out
.println(
"我是服务器,客户端说:"
+info);
}
socket
.shutdownInput();
//关闭输入流
//获取输出流,响应客户端的请求
os =
socket
.getOutputStream();
pw =
new
PrintWriter(os);
pw.write(
"欢迎您!"
);
pw.flush();
//调用flush()方法将缓冲输出
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
//关闭资源
try
{
if
(pw!=
null
)
pw.close();
if
(os!=
null
)
os.close();
if
(br!=
null
)
br.close();
if
(isr!=
null
)
isr.close();
if
(is!=
null
)
is.close();
if
(
socket
!=
null
)
socket
.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
然后再监听的服务端可以这么写:
public
class
Server {
public
static
void
main(String[] args) {
try
{
//1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
ServerSocket serverSocket=
new
ServerSocket(8888);
Socket socket=
null
;
//记录客户端的数量
int
count=0;
System.out.println(
"***服务器即将启动,等待客户端的连接***"
);
//循环监听等待客户端的连接
while
(
true
){
//调用accept()方法开始监听,等待客户端的连接
socket=serverSocket.accept();
//创建一个新的线程
ServerThread serverThread=
new
ServerThread(socket);
//启动线程
serverThread.start();
count++;
//统计客户端的数量
System.out.println(
"客户端的数量:"
+count);
InetAddress address=socket.getInetAddress();
System.out.println(
"当前客户端的IP:"
+address.getHostAddress());
}
}
catch
(IOException e) {
e.printStackTrace();
}
}
客户端就不用变了。
第五章 基于UDP的编程
1、所要用的核心API有:
DatagramSocket、DatagramPacket
2、服务器端的过程如下:
@1、创建DatagramSocket,指定端口号
@2、创建DatagramPacket制定用来存放接受数据的位置
@3、接受数据
@4、读取接受的数据
3、客户端的过程如下:
@1、定义各种信息,InetAddress,port以及所要发送的数据
@2、创建DatagramPacket,用来生成发送的数据包
@3、创建DatagramSocket,用来发送数据
@4、发送数据
代码如下:
/*
* 服务器端,实现基于UDP的用户登陆
*/
public
class
UDPServer {
public
static
void
main(String[] args)
throws
IOException {
/*
* 接收客户端发送的数据
*/
//1.创建服务器端DatagramSocket,指定端口
DatagramSocket socket=
new
DatagramSocket(8800);
//2.创建数据报,用于接收客户端发送的数据
byte
[] data =
new
byte
[1024];
//创建字节数组,指定接收的数据包的大小
DatagramPacket packet=
new
DatagramPacket(data, data.
length
);
//3.接收客户端发送的数据
System.
out
.println(
"****服务器端已经启动,等待客户端发送数据"
);
socket.receive(packet);
//此方法在接收到数据报之前会一直阻塞
//4.读取数据
String info=
new
String(data, 0, packet.getLength());
System.
out
.println(
"我是服务器,客户端说:"
+info);
/*
* 向客户端响应数据
*/
//1.定义客户端的地址、端口号、数据
InetAddress address=packet.getAddress();
int
port=packet.getPort();
byte
[] data2=
"欢迎您!"
.getBytes();
//2.创建数据报,包含响应的数据信息
DatagramPacket packet2=
new
DatagramPacket(data2, data2.
length
, address, port);
//3.响应客户端
socket.send(packet2);
//4.关闭资源
socket.close();
}
}
/*
* 客户端
*/
public
class
UDPClient {
public
static
void
main(String[] args)
throws
IOException {
/*
* 向服务器端发送数据
*/
//1.定义服务器的地址、端口号、数据
InetAddress address=InetAddress.getByName(
"localhost"
);
int
port=8800;
byte
[] data=
"用户名:admin;密码:123"
.getBytes();
//2.创建数据报,包含发送的数据信息
DatagramPacket packet=
new
DatagramPacket(data, data.
length
, address, port);
//3.创建DatagramSocket对象
DatagramSocket socket=
new
DatagramSocket();
//4.向服务器端发送数据报
socket.send(packet);
/*
* 接收服务器端响应的数据
*/
//1.创建数据报,用于接收服务器端响应的数据
byte
[] data2=
new
byte
[1024];
DatagramPacket packet2=
new
DatagramPacket(data2, data2.
length
);
//2.接收服务器响应的数据
socket.receive(packet2);
//3.读取数据
String reply=
new
String(data2, 0, packet2.getLength());
System.
out
.println(
"我是客户端,服务器说:"
+reply);
//4.关闭资源
socket.close();
}
}
总结:
重点:
Socket通信原理
基于TCP的Socket编程
经验与技巧:
多线程中要设置线程的优先级,否则会很慢,因为系统的线程居多,不定什么时候运行呢
是否关闭输入输出流,有时候关闭输入输出流会导致Socket也关闭了,这不是我们想要的
我们进行传输的时候,用的都是字符串的示例,但是实际生活中,更多的是传对象或者文件。