网络编程
讲网络编程之前,先提一下计算机网络:
计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,
在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程就是用来 实现网络互连的不同计算机上运行的程序间可以进行数据交换。
我的理解:网络编程就是通过编程语言实现计算机间的数据通信。
要学网络编程就要先了解网络模型。
计算机网络之间以何种规则进行通信,就是网络模型研究问题。
网络模型一般是指
OSI(Open System Interconnection开放系统互连)参考模型
TCP/IP参考模型
网络参考模型图如下:
网络通信三要素:IP地址、端口号,传输协议
1)IP地址:InetAddress
网络中设备的标识,不易记忆,可用主机名
它是计算机的唯一标识
那么,我们如果获取和操作IP地址呢?
为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用。
InetAddress类的使用
它没有构造方法,想到那些问题呢?
A:成员都是静态的。
B:肯定有静态方法返回该类的对象。
要掌握的功能有:
获取本地主机:public String getHostName()
获取任意主机:public static InetAddress getByName(String host)
主机名:public String getHostName()
主机Ip地址:public String getHostAddress()
我们随便给出一个十进制的数值:
192.168.3.100
而它在网络中实际上是如下表示的:
11000000 10101000 00000011 01100100
那为什么我们不是采用下面这种方式配置ip的呢?
如果采用二进制,不方便记忆。
所以,为了方便记忆,把这种表示形式做了改进:"点分十进制"
IP地址的分类:
A类 1.0.0.1---127.255.255.254
(1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)
(2)127.X.X.X是保留地址,用做循环测试用的。
B类128.0.0.1---191.255.255.254172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C类192.0.0.1---223.255.255.254192.168.X.X是私有地址
D类224.0.0.1---239.255.255.254
E类240.0.0.1---247.255.255.254
IP地址的组成:
IP地址 = 网络号码+主机地址
A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码
256*256*256---16777216
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码
256*256---65536
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码
256
查看本机IP:ipconfig
查看网络是否有问题ping+IP地址
ping 127.0.0.1(本机回环地址)
2)端口号
用于标识进程的逻辑地址,不同进程的标识
通过360可以查看端口号
A:每个网络程序都会至少有一个逻辑端口
B:用于标识进程的逻辑地址,不同进程的标识不一样
C:有效端口:0~65535,其中0~1024系统使用或保留端口。
一般我们用的话建议用1024以上的。
3)传输协议
通讯的规则
常见协议:TCP,UDP
UDP
将数据源和目的封装成数据包中,不需要建立连接;(把数据打包,数据有限制,面向无连接)
每个数据报的大小在限制在64k;因无连接,是不可靠协议;(不可靠)
不需要建立连接,所以速度快;(速度快)
举例:QQ群聊,就是群发一些东西,不管你收到收不到
TCP
建立连接,形成传输数据的通道;(建立连接通道)
在连接中进行大数据量传输;(数据无限制)
通过三次握手完成连接,是可靠协议;(面向连接 即三次握手,可靠)
必须建立连接,效率会稍低;(速度慢)
那么平时接触到的例子有哪些呢: 蓝牙,QQ单聊,打电话等 只有和对方建立连接之后才能和对方交流。
Socket
Socket就是为网络编程提供的一种机制;
包装了有端口和IP地址;通信的两端都有Socket;
网络通信其实就是Socket间的通信;数据在两个Socket间通过IO传输。
UDP传输
DatagramSocket与DatagramPacket
传输的步骤为:
1、建立发送端,接收端。
2、建立数据包。
3、调用Socket的发送接收方法。
4、关闭Socket。
发送端与接收端是两个独立的运行程序。
通过UDP的传输步骤,下面来写一下发送端和接收端的传输步骤:
发送端思路:
1:建立udp的socket服务
2:将要发送的数据封装成数据包
3:通过udp的socket服务,将数据包发送出
4:关闭资源
接收端的传输思路:
1:建立udp的socket服务.
2:通过receive方法接收数据
3:将收到的数据存储到数据包对象中
4:通过数据包对象的功能来完成对接收到数据进行解析.
5:可以对资源进行关闭
下面通过上面步骤写一个例子。
需求:发送端向接收端发送一句话:
发送端代码:
package cn.itcast03;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
* UDP协议发送数据步骤:
* A:创建发送端Socket服务对象
* B:创建数据,并把数据打包
* C:发送数据
* D:释放资源
*/
public class SendDemo {
<span style="white-space:pre"> </span>public static void main(String[] args) throws IOException {
<span style="white-space:pre"> </span>//创建发送端Socket服务对象
<span style="white-space:pre"> </span>DatagramSocket ds=new DatagramSocket();
<span style="white-space:pre"> </span>//创建数据,并把数据打包
<span style="white-space:pre"> </span>String str="Hello ,UDP,我来啦~";
<span style="white-space:pre"> </span>// public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
<span style="white-space:pre"> </span>byte[] bys=str.getBytes();
<span style="white-space:pre"> </span>DatagramPacket dp = new DatagramPacket(bys, bys.length,
<span style="white-space:pre"> </span>InetAddress.getByName("192.168.3.100"), 12306);
<span style="white-space:pre"> </span>//发送数据
<span style="white-space:pre"> </span>// public void send(DatagramPacket p)
<span style="white-space:pre"> </span>ds.send(dp);
<span style="white-space:pre"> </span>// 释放资源<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>ds.close();
<span style="white-space:pre"> </span>}
}
package cn.itcast03;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* UDP协议接收数据步骤:
* A:创建接收端Socket服务对象
* B:创建数据包(接收容器)
* C:调用接收方法
* D:解析数据包,把数据显示在控制台
* E:释放资源
* 注意:
* <span style="white-space:pre"> </span>A:端口一定要一致。
* <span style="white-space:pre"> </span>B:Exception in thread "main" java.net.BindException:
* <span style="white-space:pre"> </span> Address already in use: Cannot bind
* 启用一次服务,只能使用一次,否则会显示该地址已被使用
*/
public class ReceiveDemo {
<span style="white-space:pre"> </span>public static void main(String[] args) throws IOException{
<span style="white-space:pre"> </span>//创建接收端Socket服务对象
<span style="white-space:pre"> </span>DatagramSocket ds=new DatagramSocket(12306);
<span style="white-space:pre"> </span>//创建数据包(接收容器)
<span style="white-space:pre"> </span>// public DatagramPacket(byte[] buf,int length)
<span style="white-space:pre"> </span>byte[] bys=new byte[1024];
<span style="white-space:pre"> </span>DatagramPacket dp=new DatagramPacket(bys, bys.length);
<span style="white-space:pre"> </span>// 调用接收方法
<span style="white-space:pre"> </span>// public void receive(DatagramPacket p)
<span style="white-space:pre"> </span>ds.receive(dp);
<span style="white-space:pre"> </span>// 解析数据包,把数据显示在控制台
<span style="white-space:pre"> </span>// public InetAddress getAddress()
<span style="white-space:pre"> </span>String ip=dp.getAddress().getHostAddress();
<span style="white-space:pre"> </span>String s=new String(dp.getData(), 0,dp.getLength());
<span style="white-space:pre"> </span>System.out.println(ip+"***"+s);
<span style="white-space:pre"> </span>//释放资源
<span style="white-space:pre"> </span>ds.close();
<span style="white-space:pre"> </span>}
}
运行后结果为:
注意:一定要先编译接收端,然后再编译发送端。(因为UDP不保证数据被收到)
TCP传输
Socket和ServerSocket
TCP传输的步骤为:
1、建立客户端和服务器端
2、建立连接后,通过Socket中的IO流进行数据的传输
3、关闭socket
同样,客户端与服务器端是两个独立的应用程序。(客户端可以有多个,而服务器只要一个)
由上面TCP传输的步骤,我们可以写出客户端和服务器端的步骤:
客户端思路:
1、建立客户端的Socket服务,并明确要连接的服务器。
2、如果连接建立成功,就表明,已经建立了数据传输的通道;
就可以在该通道通过IO进行数据的读取和写入;
该通道称为Socket流,Socket流中既有读取流,也有写入流。
3、通过Socket对象的方法,可以获取这两个流
4、通过流的对象可以对数据进行传输
5、如果传输数据完毕,关闭资源
服务器端思路:
1、建立服务器端的socket服务,需要一个端口
2、服务端没有直接流的操作,而是通过accept方法获取客户端对象,再通过获取到的客户端对象的流和客户端进行通信
3、通过客户端的获取流对象的方法,读取数据或者写入数据
4、如果服务完,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,
因为服务端是一直提供服务的
下面通过上面步骤写一个例子。
需求:客户端向服务器端发送一句话:
客户端代码:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/*
* TCP协议客户端的步骤:
* A:创建客户端的Socket对象
* B:建立连接
* C:获取输出流,写数据即可
* D:释放资源
* 注意:
* Exception in thread "main" java.net.ConnectException: Connection refused: connect
* TCP程序,必须先开服务器端。
*/
public class ClientDemo {
public static void main(String[] args) throws IOException, IOException {
//创建客户端的Socket对象
// public Socket(InetAddress address,int port)
// Socket s = new Socket(InetAddress.getByName("192.168.3.100"), 10010);
// Socket(String host, int port)
Socket s=new Socket("192.168.8.51",10010);
//获取输出流,写数据即可
// public OutputStream getOutputStream()
OutputStream os=s.getOutputStream();
os.write("hello,TCP,俺来啦~".getBytes());
//释放资源
s.close();
}
}
服务器端代码:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* TCP协议服务器端的步骤:
* A:创建服务器端Socket对象
* B:监听连接
* C:获取输入流,读取数据,并显示
* D:释放资源
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端Socket对象
// ServerSocket(int port)
ServerSocket ss=new ServerSocket(10010);
//监听连接
// public Socket accept()
Socket s=ss.accept(); //这时候是阻塞的,直到连接上客户端发来的请求后,
//获取输入流,读取数据,并显示
// public InputStream getInputStream()
InputStream is=s.getInputStream();
byte[] bys=new byte[1024];
int len=is.read(bys); //阻塞
String client=new String(bys,0,len);
System.out.println(client);
//释放资源
s.close();
}
}
运行后结果:
在这儿一定要注意:
运行的时候,先运行服务器端(接受端),再运行客户端(发送端),
因为服务器先启动,然后在那儿等着客户端的连接请求,即处于阻塞状态,
这时候启动客户端,然后在那儿找 找 找~~ 找到了对应的IP和端口号,
就向此服务器发送连接请求, 如果服务器端监听到了客户端的信息,就建立连接啦~~
否则会连接失败。(这点一直困扰我好长时间,呜呜呜呜~~~)
下面写几个例子来巩固一下所学的知识:
练习一:实现客户端发送消息,服务器端给出反馈。
客户端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args)throws IOException {
//创建客户端Socket对象
Socket s=new Socket("192.168.8.51",12345);
//获取输出流
OutputStream os=s.getOutputStream();
os.write("今天感觉有点累,不过收获的挺大的!".getBytes());
//获取输入流
InputStream is=s.getInputStream();
byte[] bys=new byte[1024];
int len=is.read(bys);//阻塞
String client=new String(bys, 0, len);
System.out.println("client:"+client);
//释放资源
s.close();
}
}
服务器端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器对象Socket对象
ServerSocket ss=new ServerSocket(12345);
//监听客户端连接
Socket s=ss.accept(); //阻塞
//获取输入流
InputStream is=s.getInputStream();
byte[] bys=new byte[1024];
int len=is.read(bys); //阻塞
String server=new String(bys, 0, len);
System.out.println("server:"+server);
//获取输出流
OutputStream os=s.getOutputStream();
os.write("数据已成功接收".getBytes());
//释放资源
s.close();
}
}
运行后结果为:
练习二:
需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。
客户端代码:
<span style="font-family: SimSun; font-size: 14px; line-height: 26px;">import java.io.BufferedReader;</span>
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/*
* 需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。
*/
public class ClientDemo {
public static void main(String[] args)throws IOException {
//创建客户端Socket对象
Socket s=new Socket("192.168.8.51",22222);
//封装键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//获取输出流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line=null;
while((line=br.readLine())!=null){
if("over".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
//获取服务器的反馈
BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
String server=brServer.readLine();
System.out.println("serer:"+server);
}
br.close();
s.close();
}
}
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;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss=new ServerSocket(22222);
//监听客户端
Socket s=ss.accept();
//获取输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line=null;
while((line=br.readLine())!=null){
bw.write(line.toUpperCase());
bw.newLine();
bw.flush();
}
s.close();
}
}
运行后结果为:
上面的练习其实还可以用PrintWriter来改进:
客户端:
/*
* 需求:数据来源于键盘录入,通过服务器转成大写,然后反馈给客户端显示。
*/
public class ClientDemo {
public static void main(String[] args)throws IOException {
//创建客户端Socket对象
Socket s=new Socket("192.168.8.51",22222);
//封装键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//获取输出流
//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null){
if("over".equals(line)){
break;
}
// bw.write(line);
// bw.newLine();
// bw.flush();
pw.println(line);
//获取服务器的反馈
BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
String server=brServer.readLine();
System.out.println("serer:"+server);
}
br.close();
s.close();
}
}
服务器端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss=new ServerSocket(22222);
//监听客户端
Socket s=ss.accept();
//获取输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null){
// bw.write(line.toUpperCase());
// bw.newLine();
// bw.flush();
pw.println(line.toUpperCase());
}
s.close();
}
}
运行后结果和上面是一样的,但是代码简化了不少。
练习三:
需求:键盘录入数据,并写入到文本文件。
写一下刚开始遇到的问题:
写数据,数据进去了,但是反馈没有成功。
原因:就是因为服务器不知道你已经写完了。
怎么解决呢?告诉服务器我写完了。
怎么告诉:
A:自定义接收标记 假如有一个数据叫“拜拜” ,就会被强制终止
B:Socket提供了一个方法,来通知我接收了。
public void shutdownOutput()
客户端代码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
/*
* 需求:数据来源于键盘录入,并写入文本文件中
*/
public class ClientDemo {
public static void main(String[] args)throws IOException {
//创建客户端Socket对象
Socket s=new Socket("192.168.8.51",22222);
//封装键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//获取输出流
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null){
if("over".equals(line)){
break;
}
pw.println(line);
}
s.shutdownOutput();
//获取服务器的反馈
BufferedReader brServer=new BufferedReader(new InputStreamReader(s.getInputStream()));
String server=brServer.readLine();
System.out.println("serer:"+server);
br.close();
s.close();
}
}
服务器端代码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss=new ServerSocket(22222);
//监听客户端
Socket s=ss.accept();
//获取输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out=new PrintWriter(new FileWriter("out.txt"),true);
String line=null;
while((line=br.readLine())!=null){
out.println(line);
}
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
pw.println("数据成功接收");
s.close();
}
}
运行后结果为:
练习四:
需求:客户端向服务端发送用户名请求登陆,服务端通过验证,返回“欢迎光临”,未通过“用户不存在”
服务器端代码:
/*
* 客户端向服务端发送用户名请求登陆,服务端通过验证,返回“欢迎光临”,未通过“用户不存在”
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器Socket对象
ServerSocket ss=new ServerSocket(11111);
//监听客户端对象
Socket s=ss.accept();
//获取输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=br.readLine();
if("admin".equals(line)){
pw.println("欢迎光临!");
}else{
pw.println("用户不存在!");
}
//释放资源
s.close();
ss.close();
}
}
客户端代码:
public class ClientDemo {
public static void main(String[] args)throws IOException {
//创建客户端Socket对象
Socket s=new Socket("192.168.8.51",11111);
//获取输出流
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
pw.println("hello");
//获取输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=br.readLine();
System.out.println("line:"+line);
//释放资源
s.close();
}
}
运行后结果为:
练习五:
需求: 客户端向服务端上传一个文本文件
服务器端代码:
/*
* 程序可以解决一部分效率问题,但是一般都是小的改进。
* 要想大的改进:硬件,网络带宽。
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务器Socket对象
ServerSocket ss = new ServerSocket(12345);
// 监听客户端对象
Socket s = ss.accept();
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// long time = System.currentTimeMillis();
// 封装输出流
// PrintWriter pw = new PrintWriter(new FileWriter(time+"_Copy.java"),
// true);
PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true);
String line = null;
while ((line = br.readLine()) != null) {
pw.println(line);
}
// 给出反馈,获取输出流
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
out.println("文件上传成功");
// 释放资源
pw.close();
s.close();
ss.close();
}
}
客户端代码:
/*
* 客户端向服务端上传一个文本文件
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端对象
Socket s = new Socket("192.168.8.51", 12345);
// 封装输入流
BufferedReader br = new BufferedReader(new FileReader("ClientDemo.java"));
// 获取输出流
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
String line = null;
while ((line = br.readLine()) != null) {
pw.println(line);
}
s.shutdownOutput();
// 获取输入流
BufferedReader brServer = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String server = brServer.readLine();
System.out.println("server反馈信息是:" + server);
//释放资源
br.close();
s.close();
}
}
运行后结果:
通过这几个练习,感觉现在能熟练的运用网络编程方面的知识,自己理解得更透了一些。
加油吧,博客马上就收尾啦~~
网络编程的知识点儿就先总结到这儿啦,有些理解的不对的地方,真诚地希望大家帮我指正出来,谢谢!!!