------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
网络编程
概述
网络模型 OSI参考模型,TCP/IP参考模型
网络通讯要素 IP地址,端口号,传输协议
两台主机之间通讯:
1,找到对方IP
2,数据要发送到对方指定的应用程序上,为了标示这些应用程序,给这些网络应用程序用数字进行标识,为了方便,称呼这个数字为端口
3,定义通信规则,这个通信规则称为协议,国际组织定义了通用协议TCP/IP
OSI参考模型物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
TCP/IP参考模型主机至网络层,网际层,传输层,应用层
IP地址
网络中设备的标识
不易记忆,可用主机名
本地回环地址:127.0.0.1主机名:localhost
端口号
用于标识进程的逻辑地址,不同进程的标识
有效端口:0~65535,其中0~1024为系统使用或保留端口
传输协议
常见协议:TCP,UDP
net包中,用于描述IP的对象是InetAddress
InetAddress演示如下
import java.net.*;
import java.io.*;
class IPDemo
{
public static void main(String[] args) throws Exception
{
//返回本地主机
InetAddress i = InetAddress.getLocalHost();
System.out.println(i.toString());
//返回ip地址字符串
System.out.println(i.getHostAddress());
//返回此ip地址的主机名
System.out.println(i.getHostName());
//返回任意一台主机的IP地址对象
InetAddress ia1 = InetAddress.getByName("192.168.242.1");
InetAddress ia2 = InetAddress.getByName("www.baidu.com");
}
}
TCP和UDP
UDP
将数据及源和目的封装到数据包中,不需要建立连接
每个数据报的大小限制在64k内
无连接,是不可靠协议
不需要建立连接,速度快
TCP
建立连接,形成传输数据的通道
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会较低
Socket
Socket就是为网络服务提供的一种机制,通信的两端都有Socket,网络之间的通信其实就是Socket之间的通信
UDP传输
DatagramSocket:此类用来表示传送和接收数据报包的套接字
DatagramPacket:数据报包用来实现无连接包投递服务
import java.net.*;
/*
需求:通过udp的传输方式,将一段文字数据发送出去
思路:
1,建立udp Socket服务
2,提供数据,并将数据封装到数据包中
3,通过socket服务的发送功能,将数据报发送出去
4,关闭资源
*/
class UdpSend
{
public static void main(String[] args)
{
//创建udp服务,通过DatagramSocket
DatagramSocket ds = new DatagramSocket();
//确定数据,并封装成数据包
byte[] buf = "UDP".getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.242.1"),10000);
//通过socket服务,将已有的数据包发送出去。通过send方法
ds.send(dp);
//关闭资源
ds.close();
}
}
/*
需求:
定义一个应用程序,用于接收udp协议传输的数据并处理
思路:
1,定义udp socket服务;通常会监听一个端口。
2,定义一个数据包,因为要存储接收到的字节数据
数据包对象中有更多的功能可以提取字节数据中的不同数据信息
3,通过Socket服务的receive方法将收到的数据存入已定义好的数据包中
4,通过数据包中的特有功能,将这些不同的数据取出,打印在控制台上
5,关闭资源
*/
class UdpReceive
{
public static void main(String[] args)
{
//创建udp socket,建立端点
DatagramSocket ds = new DatagramSocket(10000);
//定义数据包。用于存储数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//通过服务的receive方法将收到的数据存入到数据包中
ds.receive(dp); //阻塞式方法
//通过数据包的方法,获取其中的数据
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+" "+data+" "+port);
//关闭资源
ds.close();
}
}
UDP键盘录入方式练习
import java.net.*;
import java.io.*;
class UdpSend2
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket();
//键盘录入
BufferedReader bufr = new BufferedReader(
new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null){ //readLine() 阻塞式方法
if("bye".equals(line))
break;
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(
buf,buf.length,InetAddress.getByName("192.168.242.1"),10001);
ds.send(dp);
}
ds.close();
}
}
class UdpReceive2
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket(10001);
while(true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
// getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
// getAddress() 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.length());
}
}
}
一个UDP聊天程序
import java.io.*;
import java.net.*;
/*
编写一个聊天程序
有收数据的部分,也有发数据的部分,这两部分需要同时执行
需要用到多线程技术,一个线程控制收,一个线程控制发
因为收和发的动作是不一致的,所以要定义两个run方法
而且这两个方法要封装到不同的类中
*/
class Send implements Runnable
{
private Socket s;
public Send(Socket s){
this.s = s;
}
public void run(){
try
{
//键盘录入
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null){ //readLine() 阻塞式方法
if("bye".equals(line))
break;
byte[] buf = line.getBytes();
//构造数据包,将长度为buf.length的发送到192.168.242.1指定主机的10002端口
DatagramPacket dp = new DatagramPacket(
buf,buf.length,InetAddress.getByName("192.168.242.1"),10002);
//从ds套接字发送dp数据报包
ds.send(dp);
}
}
catch (Exception e)
{
throw new RuntimeException("发送端失败");
}
}
}
class Receive implements Runnable
{
private Socket s;
public Receive(Socket s){
this.s = s;
}
public void run(){
try
{
while(true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp); //阻塞式方法
//获得发送端的ip地址
String ip = dp.getAddress().getHostAddress();
//获得发送端的数据
String data = new String(dp.getData(),0,dp.getLength());
}
}
catch (Exception e)
{
throw new RuntimeException("接收端失败");
}
}
}
class Chat
{
public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10002);
//建立并开启发送和接收线程
new Thread(new Send(sendSocket)).start();
new Thread(new Receive(receSocket)).start();
}
}
Socket和ServerSocket
Socket:此类实现了客户端套接字
ServerSocket:此类实现服务端套接字
TCP演示
import java.io.*;
import java.net.*;
/*
Tcp分客户端和服务端
1,客户端对应的对象是Socket
2,服务端对应的对象是ServerS
*/
/*
客户端:
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机
因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功
形成通路后,在该通道进行数据的传输
需求:给服务端发送一个文本数据,接收服务端的反馈信息
步骤:
1,创建Socket服务,并指向要连接的主机和端口
2,获取socket流中的输出流,将数据写到该流中,通过网络发送给服务端
3,获取socket流中的输入流,将服务端反馈的数据获取到,并打印
4,关闭客户端资源
*/
class TcpClient
{
public static void main(String[] args) throws Exception
{
//创建Socket服务,并指向要连接的主机和端口
Socket s = new Socket("192.168.242.1",10003);
//为了发送数据,应该获取Socket流中的输出流
OutputStream out = s.getOutputStream();
out.write("TCP".getBytes());
//为了接收数据,应该获取Socket流中的输入流
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,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();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"------connect");
//获取客户端发过来的数据,使用客户端对象的读取流来操作
InputStream in = s.getInputStream();
//读数据
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
//通过客户端对象输出流给客户端反馈信息
OutputStream out = s.getOutputStream();
out.write("收到信息".getBytes());
s.close();
}
}
一个TCP练习
/*
需求:
建立一个文本转换服务器
客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端
客户端可以不断的进行文本转换。当客户端收到over时,转换结束
分析:
客户端
既然是操作设备上的数据,就可以使用IO技术,并按照IO的操作规律来思考
源:键盘录入
目的:网络设备,网络输出流
操作的是文本数据,可以选择字符流
步骤:
1,建立服务
2,获取键盘录入
3,将数据发给服务端
4,获取服务端返回的大写数据
5,结束,关资源
*/
import java.io.*;
import java.net.*;
class TransClient
{
public static void main(String[] args)
{
Socket s = new Socket("192.168.242.1",10004);
//定义读取键盘数据的流对象
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入到socket输出流,发送给服务端
BufferedWriter bufOut = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
//定义一个socket读取流,读取服务端返回的大写信息
BufferedReader bufIn = new BufferedReader(
new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals.line)
break;
bufOut.write(line);
//键盘录入需要做结束标记
bufOut.newLine();
bufOut.flush();
String str = bufIn.readLine();
System.out.println(str);
}
bufr.close();
s.close();
}
}
/*
服务端
源:socket读取流
目的:socket输出流
*/
class TransServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"------connect");
//读取socket读取流中的数据
BufferedReader bufIn = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//目的,socket输出流。将大写数据写入到socket输出流,发送给客户端
BufferedWriter bufOut = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
String line = null;
while((line=bufIn,readLine())!=null){
bufOut.write(line.toUpperCase());
bufOut.newLine();
bufOut.flush();
}
s.close();
ss.close();
}
}
多台主机上传文件示例
/*
需求:上传文件
*/
/*
1,客户端:服务端点
2,读取客户端已有的文件数据
3,通过Socket输出流将数据发给服务端
4,读取服务端反馈信息
5,关闭资源
*/
import java.io.*;
import java.net.*;
class PicClient {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//创建一个流套接字并将其连接到192.168.242.1地址的10002端口号。
Socket s = new Socket("192.168.242.1",10002);
//创建一个读取文件的流对象
FileInputStream fis = new FileInputStream("E:\\music\\喜欢音乐\\张宇 - 月亮惹的祸.ape");
BufferedInputStream bis = new BufferedInputStream(fis);
//返回此套接字的输出流
OutputStream out = s.getOutputStream();
//定义一个缓冲区,存储得到的字节数据
byte[] buf = new byte[1024];
int len = 0;
while((len=bis.read(buf))!=-1){
out.write(buf,0,len);
}
//告诉服务端,数据已写完
s.shutdownOutput();
//返回此套接字的输入流,接收服务端的反馈信息
InputStream in = s.getInputStream();
byte[] bytes = new byte[1024];
int num = in.read(bytes);
//将反馈信息打印在控制台上
System.out.println(new String(bytes,0,num));
fis.close();
s.close();
}
}
//服务端
class PicServer {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(10002);
while(true){
//拿到客户端对象
Socket s = ss.accept();
//将客户端对象封装到线程中,并开始线程
new Thread(new FileThread(s)).start();
}
}
}
//创建一个FileThread类,实现Runnable接口
class FileThread implements Runnable{
private Socket s;
FileThread(Socket s){
this.s = s;
}
public void run(){
int count = 1;
String ip = s.getInetAddress().getHostAddress();
try{
System.out.println(ip+"------connect");
InputStream in = s.getInputStream();
//创建一个文件输出流对象,将读取的数据写入到指定的文件中
File file = new File(ip+"("+(count)+")"+".ape");
//每个客户端进来都会创建一个File对象,如果重名,count++
while(file.exists())
file = new File(ip+"("+(count++)+")"+".ape");
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1){
bos.write(buf,0,len);
}
//返回s对象的输出流套接字,用来给客户端反馈信息
OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());
fos.close();
s.close();
}catch(Exception e){
throw new RuntimeException(ip+"上传失败");
}
}
}
客户端并发登陆的练习
/*
客户端通过键盘录入用户名
服务端对这个用户名进行校验
如果该用户存在,在服务端显示xxx,已登录
并在客户端显示xxx,欢迎光临
如果该用户不存在,在服务端显示xxx尝试登陆
并在客户端显示xxx用户不存在
*/
import java.net.*;
import java.io.*;
class LoginClient {
public static void main(String[] args)throws Exception {
//创建一个流套接字并将其连接到192.168.242.1主机上的10002端口。
Socket s = new Socket("192.168.242.1",10002);
//用来键盘录入用户名
BufferedReader bufr = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
BufferedReader bufIn = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//只能录入三次,使用for循环
for(int x=0; x<3; x++){
String line = bufr.readLine();
out.println(line);
if(line==null)
break;
String info = bufIn.readLine();
//如果用户已登录,返回信息,并结束循环
if(line.contains("welcom")){
System.out.println("info "+info);
break;
}
else
System.out.println("还有"+(2-x)+"次机会");
}
bufr.close();
s.close();
}
}
class LoginServer {
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10002);
while(true){
Socket s = ss.accept();
//为每一个客户端创建一个线程
new Thread(new UserThread(s)).start();
}
}
}
class UserThread implements Runnable{
private Socket s;
UserThread(Socket s){
this.s = s;
}
public void run(){
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"----------connect");
try {
//只能接受三次输入
for(int x=0; x<3; x++){
//创建缓冲区,用来存储Socket流中发来的数据
BufferedReader bufIn = new BufferedReader(
new InputStreamReader(s.getInputStream()));
String name = bufIn.readLine();
//如果读取的数据为空,跳出循环
if(name==null)
break;
BufferedReader bufr = new BufferedReader(
new FileReader("D:\\MyEclipse 8.5\\workspace\\java2014\\src\\day24\\user.txt"));
//创建一个打印流,用来给客户端反馈信息
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
//定义一个标志位,用来判断名字是否存在
boolean flag = false;
while((line=bufr.readLine())!=null){
if(line.equals(name)){
flag = true;
break;
}
}
if(flag==true){
System.out.println(name+" 已登录");
out.println(name+" welcom");
break;
}
else{
System.out.println(name+" 尝试登陆");
out.println(name+" 用户名不存在");
}
}
s.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
URL:类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用
URL的几个常用方法:
String | getFile() 获取此 URL 的文件名。 |
String | getHost() 获取此 URL 的主机名(如果适用)。 |
String | getPath() 获取此 URL 的路径部分。 |
int | getPort() 获取此 URL 的端口号。 |
String | getProtocol() 获取此 URL 的协议名称。 |
String | getQuery() 获取此 URL 的查询部分。 |