Java网络编程-基础篇
网络编程
概述:
网络通讯三要素:IP地址,端口号,传输协议。
IP地址:InetAddress
1, 网络中设备的标识
2, 不易于记忆,可用主机名
3, 本地会还地址:127.0.0.1 主机名Localhost
端口号:
1, 用于标识进程的逻辑地址,不同的进程的标识
2, 常用端口号:
1, web服务器端口号:80(位于0~1024之间)
2, Tomcat服务器端口:8080
3, MySQL数据库端口:3306
3,一个网络应用程序可以对应一个或多个端口号
4,有效端口:0~65535,忠诚0~1024系统使用或保留端口
传输协议
1, 通讯规则
2, 常见协议:TCP,UDP
网络模型:
因为网络传输需要不同的组件协同工作,而这些组件运行时间和方式不同,为了让这些组件什么时候该做什么工作,就有了按照层次的形式进行了划分,就有了网络传输模型。
网络模型:OSI参考模型,TCP/IP参考模型。
如图:
通常用户操作是在应用层(应用层一般协议:FTP和HTTP协议等)
编程人员操作是传输层和网际层(传输层:UDP和TCP等;网际层:IP)
用户子啊应用层操作数据,然后经由每个层级逐个封包,最后到物理层发生到相对应的模型中,再一次逐层解包,下面为图例:
IP地址:
查询API,在java.net包InetAddres类,标示互联网协议(IP)地址
通过该类中的方法可以获取IP地址,主机名等。
示例:
import java.net.*;
class IPDemo{
public static void main(String[] args) throws UnknownHostException{
//获取本类对象
InetAddress ia = InetAddress.getLocalHost();
//获取本地IP地址
String ip = ia.getHostAddress();
//获取IP地址主机名
String name = ia.getHostName();
System.out.println("ip:"+ip+" name:"+name);
//通过指定的IP地址获取信息
InetAddress i = InetAddress.getByName("192.168.0.2");
String ii = i.getHostAddress();
String nn = i.getHostName();
//如果IP地址和对应的主机名,这种映射关系没有在网络上,
//就不会解析成功,返回的还是指定的IP。
System.out.println("ii:"+ii+" nn:"+nn);
//在给定主机名的情况下,
//根据系统上配置的名称服务返回其 IP 地址所组成的数组
InetAddress[] baidus = InetAddress.getAllByName("www.baidu.com");
for(InetAddress baidu : baidus){
String pp = baidu.getHostAddress();
String mm = baidu.getHostName();
System.out.println("pp:"+pp+" mm:"+mm);
}
}
}打印结果:
传输协议:TCP和UDP
UDP
1, 将数据及源和目的封装成数据包,不需要建立连接,面向无连接。
2, 数据会被封包,每个数据报的大小在限制在64k内。
3, 因为无连接,是不可靠协议
4, 没有建立连接,速度快。
如:聊天,视频会议,桌面共享等都可以通过UDP传输。
TCP
1, 建立连接,形成传输数据的通道。
2, 在连接中进行大数据量传输
3, 通过三次握手完成连接,是可靠协议(三次握手:第一次本方发送请求,第二次对方确认连接,第三次本方再次确认连接成功。)
4, 必须建立连接,效率会比较低。
如:应用下载程序等可以通过TCP传输。
Socket(套接字)
概述:所谓的网络编程就是指对Socket的编程
特点:
1,Socket就是为网络服务提供的一种机制。
2,通信的两端都有Socket.
3,网络通信其实就Socket之间的通信。
4,数据在两个Socket间通过IO传输。
总得来说,Socket就是用来做数据的连接点,相当于硬件上用网卡来做网线中数据的连接点。
UDP
概述:
查询API,在java.net包中有DatagramSocket类,此类标示用来发送和接收数据报包的套接字
在java.net包中有DatagramPacket类,此类标示数据报包,用来实现无连接包的投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器
常用方法:
1, 创建服务对象,构造方法:
DatagramSocket();//构造数据报套接字并将其绑定到本地主机上任何可用的端口
DatagramSocket(int port);//创建数据报套接字并将其绑定到本地主机上的指定端口
2, 发送数据报
void send(DatagramPacket p);// 从此套接字发送数据报包
3, 接收数据报
Void receive(DatagramPacket p);// 从此套接字接收数据报包
创建UDP发送端:
示例:
/*
需求:通过UDP传输方式,将一段文字数据发送出去。
思路:
1,建立UDPSocket服务,在此无需指定端口,也可以将端口加入。
如果不指定的话,系统会随机分配一个端口。
如第一次运行时端口为1093,那么第二次就会顺延为1094
再运行会一直顺延,因为之前的端口还没有得到释放,
所以会顺延端口号值
2,提供数据,并将数据封装到数据包中
3,通过socket服务的发送功能,将数据包发送出去
4,关闭资源
5,还要对其异常处理,或抛出异常
*/
import java.net.*;
class UdpSendDemo{
public static void main(String[] args) throws Exception{
//创建DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//将数据分组成数据包:
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
byte[] buy = "UDP示例1234567".getBytes();
DatagramPacket dp =
new DatagramPacket(buy,buy.length,InetAddress.getByName("192.168.0.2"),10000);
//将数据包发送出去
ds.send(dp);
//关闭资源
ds.close();
}
}
创建UDP-接收端
示例:
/*
需求:定义一个应用程序,用于接受udp协议传输过来的数据并将其处理。
思路:
1,定义udpsocket服务,通常会监听一个端口。其实就是给这个网络应用程序定义数字标识
方便明确哪些数据过来哪些应用程序可以处理
2,定义一个数据包,因为要存储接收到的字节数据
因为数据包对象中有更多功能可以提取字节数据中的不同数据信息
3,通过socket服务的receive方法将收到的数据存入已定好的数据包包中
4,通过数据包对象的特有功能,将这些不同数据取出,并打印控制台上。
5,关闭资源。
*/
import java.net.*;
class UdpReceiveDemo{
public static void main(String[] args) throws Exception{
//创建udp socket服务,建立端点
DatagramSocket ds = new DatagramSocket(10000);
while(true){
//定义数据包用于存储数据,
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b,b.length);
//通过服务的receive方法将收到的数据存储到数据包中
ds.receive(dp);//阻塞式方法
//通过数据包的方法获取其中数据
String ip = dp.getAddress().getHostName();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println("IP:"+ip+",数据:"+data+",端口:"+port);
//关闭资源
//ds.close();
}
}
}
/*
*/需求:通过键盘录入的方式录入数据。
发送端
import java.net.*;
import java.io.*;
class Send{
public static void main(String[] args) throws Exception{
//创建udp socket服务
DatagramSocket ds = new DatagramSocket();
//获取键盘录入
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
//对获取的数据进行封装成数据包
DatagramPacket dp = null;
String line = null;
while((line = br.readLine())!=null){
if("over".equals(line))
break;
byte[] buy = line.getBytes();
dp = new DatagramPacket(buy,buy.length,InetAddress.getByName("192.168.1.116"),10001);
ds.send(dp);
}
ds.close();
}
}
接收端
import java.net.*;
import java.io.*;
class Reveive{
public static void main(String[] args) throws Exception{
//创建udp socket服务,建立端点
DatagramSocket ds = new DatagramSocket(10001);
while(true){
//定义数据包用于存储数据,
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b,b.length);
//通过服务的receive方法将收到的数据存储到数据包中
ds.receive(dp);//阻塞式方法
//通过数据包的方法获取其中数据
String ip = dp.getAddress().getHostName();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println("IP:"+ip+",数据:"+data+",端口:"+port);
//关闭资源
//ds.close();
}
}
}打印结果如图:
练习二:
/*
需求:
编写一个聊天程序。
有收数据的部分,和发数据的部分。
这两部分需要同时执行。
那就需要用到多线程技术。
一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法。
而且这个两个方法要封装到不同的类中。
*/
import java.net.*;
import java.io.*;
class UdpSend implements Runnable{
DatagramSocket ds;
UdpSend(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
//从键盘获取数据并将该数据封装成数据包
DatagramPacket dp = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine())!=null){
if("over".equals(line))
break;
byte[] buy = line.getBytes();
dp = new DatagramPacket(buy,buy.length,InetAddress.getByName("192.168.116"),10002);
//将数据包发送出去
ds.send(dp);
}
ds.close();
}
catch (Exception e){
throw new RuntimeException("发送失败");
}
}
}
class UdpReceive implements Runnable{
DatagramSocket ds;
UdpReceive(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
while (true){
//创建一个数据包用于存储接收进来的数据
byte[]b =new byte[1024];
DatagramPacket dp = new DatagramPacket(b,b.length);
//通过socket服务接收数据
ds.receive(dp);
//获取发送端的ip地址
String ip = dp.getAddress().getHostAddress();
//获取数据
String data = new String(dp.getData(),0,dp.getLength());
//获取端口
int port = dp.getPort();
System.out.println("IP:"+ip+",数据:"+data+",端口:"+port);
}
}
catch (Exception e){
throw new RuntimeException("接收失败");
}
}
}
class UdpChartDemo {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket();
DatagramSocket dp = new DatagramSocket(10002);
new Thread(new UdpSend(ds)).start();
new Thread(new UdpReceive(dp)).start();
}
}
TCP传输
概述:
Tcp分客户端和服务端
客户端对应的对象是Socket。
服务端对应的对象是ServerSocket
建立连接后,通过Socket中的IO流进行数据的传输
关闭socket
同样,客户端和服务端是两个独立的应用程序。
客户端与服务端:
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。
因为tcp是面向连接的,所以在建立socket服务时,就要有服务端的存在,并连接成功。
在形成通路后,在该通道进行数据的传输。
示例:
/*
需求:给服务端发送一个文本数据,服务端收到后,给客户端反馈信息
*/
import java.net.*;
import java.io.*;
class TcpClient{
public static void main(String[] args) throws Exception{
//创建客户端的Socket服务,指定目的主机和端口
Socket s = new Socket("192.168.0.2",10003);
//为了发送数据,还要获取socket流中的输出流
OutputStream os = s.getOutputStream();
//将数据写入输出流
os.write("我来了!!!我是客户端".getBytes());
//读取服务端返回的信息,需要使用读取流对象并打印
InputStream is = s.getInputStream();
byte[] buy = new byte[1024];
int len = is.read(buy);
System.out.println(new String(buy,0,len));
//关闭资源
s.close();
}
}
/*
需求:定义端点接收数据并打印在控制台上
服务端:
1,创建服务端socket服务,ServerSocket
并监听一个端口
2,获取连接过来的客户端,通过ServerSocket的accept方法。
没有连接就会等,所以这个方法是阻塞的
3,客户端如果发过来数据,那么服务端要使用对应的客户端对象,
并获取该客户端的对象的读取流来读取发过来的数据并打印控制台上
4,关闭服务器(可选)
*/
class TcpServer{
public static void main(String [] args)throws Exception{
//建立服务端socket服务并监听一个端口
ServerSocket ss = new ServerSocket(10003);
//通过accept方法获取链接过来的客户端对象
Socket s = ss.accept();
//获取客户端发送过来的数据,使用客户端对象读取流来读取数据
InputStream in = s.getInputStream();
//定义缓冲区将流里的数据存储到缓冲区
byte[] buy = new byte[1024];
int len = in.read(buy);
//获取客户端ip地址
String ip = s.getInetAddress().getHostAddress();
System.out.println("ip地址是:"+ip);
String data = new String(buy,0,len);
System.out.println("数据信息:"+data);
//给客户端发送数据,使用客户端写入流对象
OutputStream os = s.getOutputStream();
os.write("哥们,我收到了,我是服务端".getBytes());
ss.close();
}
}打印结果如图:
练习一:
/*
练习:
需求:建立一个文本转换服务器。
客户端给服务器发送文本服务端将文本转成大写返回给客户端。
而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束
分析步骤:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律操作
源:键盘录入
目的:网络设备,网络输出流
操作的是文本数据,可以选择字符输出流
步骤:
1,建立服务
2,获取键盘录入
3,将数据发给服务端
4,然后服务端返回大写数据
5,结束关闭资源
*/
import java.io.*;
import java.net.*;
class ClientDemo{
public static void main(String[] args) throws Exception{
//创建客户端Socket服务,指定主机及端口
Socket s = new Socket("192.168.0.2",10004);
//获取键盘录入并包装缓冲
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入socket中的输出流
//BufferedWriter bwOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
//定义socket读取流,读取服务端返回的大写信息
BufferedReader brin =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while ((line = br.readLine())!=null){
if("over".equals(line))//判断什么时候退出
break;
//bwOut.write(line);//将键盘数据写入流对象
//bwOut.newLine();//换行
//bwOut.flush();//刷新
pw.println(line);
String Uper = brin.readLine();//读取返回信息并打印控制台
System.out.println("服务端:"+Uper);
}
//关闭流对象
br.close();
s.close();
}
}
/*
服务端
1,创建服务端并指定接收端口
2,获取Socket对象,并将客户端发送过来的数据封装进缓冲区
3,定义输出流,将读取后转换成大写的数据返回给客户端
*/
class ServerDemo{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
//获取客户端发送过来的数据
BufferedReader br =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的Socket输出流,将大写数据写入到输出流
//BufferedWriter bwout =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = br.readLine())!=null){
//bwout.write(line.toUpperCase());
//bwout.newLine();//换行
//bwout.flush();//刷新
System.out.println(line);
pw.println(line.toUpperCase());//将数据写入流中
}
s.close();
ss.close();
}
}
练习二:
/*
练习:TCP复制文件
需求:客户端向服务端上传文件,服务端将文件复制到另一设备中,
并向客户端返回上传成功信息。
分析:
客户端:
源:硬盘设备文件;目的:网络输出设备
若操作的是文本数据,选择字符流;若操作的是图片音频文件,选择字节流。
服务端:
源:网络设备Socket读取流;目的:网络设备Socket输出流
问题:
当文件已经上传成功,但是服务端没有返回信息。
原因:
在上一示例中有"over"结束标记,可以上程序停止,
但是该练习中客户端将数据发送完毕后没有定义结束标记
因为阻塞,导致服务端一直等待读取数据,没法将信息返回。
解决:
1,定义结束标记,服务端识别到后将信息返回,
但是可能标记与数据中有重复,导致提前结束程序,返回信息。
2,定义时间戳,因为时间是唯一的,我们可以在发送数据之前获取时间。
在发送完后将之前的时间戳写在结尾处,然后在服务器收到数据之前
接收时间戳,然后判断后面时间戳标记。
3,通过Socket方法中的shutdownOutput(),关闭输入流资源,从而结束传输流,
以给定结束标记。通常用这个方法。
*/
import java.io.*;
import java.net.*;
class FileClient{
public static void main(String[] args)throws Exception {
Socket s = new Socket("192.168.0.2",10004);
//关联要上传的文件
BufferedReader br =
new BufferedReader(new FileReader("TcpClient.java"));
//目的:网络输出流
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = br.readLine())!=null){
pw.println(line);
}
s.shutdownOutput();//定义结束标记,关闭传输流
//接收服务端返回的上传信息
BufferedReader bufr =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = bufr.readLine();
System.out.println("上传信息:"+str);
br.close();
s.close();
}
}
class FileServer{
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
//获取客户端上传的信息 使用socket服务的读取流
BufferedReader br =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//将数据写入到指定目的
PrintWriter pw = new PrintWriter(new FileWriter("复制1.txt"),true);
String line = null;
while ((line = br.readLine())!=null){
//读一行写一行
pw.println(line);
}
//返回客户端上传信息
PrintWriter pwout = new PrintWriter(s.getOutputStream(),true);
pwout.println("上传成功");
s.close();
ss.close();
}
}
----------------------
ASP.Net+Android+IOS开发、
.Net培训、期待与您交流! ----------------------详细请查看:
http://edu.csdn.net