——- android培训、java培训、期待与您交流! ———-
网络模型
OSI参考模型
TCP/IP参考模型
网络通讯要素
IP地址
端口号
传输协议
网络传输步骤
1.找到对方ip
2.数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用了数字进行标识。为了方便称呼这些数字,叫做端口(逻辑端口)
3.定义通信规则,这个通讯规则称为协议。国际组织定义了通用协议TCP/IP.
ip地址
网络总设备的标识
不易记忆,可用于主机名
本地回环地址:127.0.0.1 主机名:localhost
获取ip地址
package net;
import java.io.IOException;
import java.net.InetAddress;
public class IpDemo {
public static void main(String[] args) throws IOException {
//获取本地ip地址
InetAddress ad=InetAddress.getLocalHost();
String sad=ad.getHostAddress(); //变成字符串
System.out.println(sad);
System.out.println(ad.toString());
//获取任意一台主机地址
InetAddress ia=InetAddress.getByName("VY7MP0RXWHHFEPU");
System.out.println(ia);
//获取新浪ip
System.out.println(InetAddress.getByName("www.sina.com"));
}
}
端口号
用于标识进程的逻辑地址,不同进程的标识
有效端口: 0~65535, 其中0~1024系统使用或保留端口。
传输协议
通讯的规则
常见协议:TCP,UDP
UDP:将数据及源目的封装成数据包中,不需要建立连接
每个数据包的大小限制在64k内
因为无连接是不可靠协议,不需要建立接连所以速度快
TCP:建立连接,形成传输数据的通道。在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。
Socket
Socket就是为网络服务提供的一种机制,通信两端都有socket,网络通信其实就是Socket间的通信,数据在两个socket间通过io传输。
UDP的Socket传输
DatagramSocket与DatagramPacket
建立发送端,接收端。
建立数据包
调用socket的发送接收方法
关闭socket
发送端与接收端是两个独立的运行程序
UDP发送与接收示例:
发送端:
package net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 需求:通过udp传输方式,将一段文字数据发送出去
* 思路:
* 建立udpSocket服务
* 提供数据,并将数据封装到数据包中
* 通过socket服务的发送功能,将数据包发送出去
* 关闭资源
*/
public class UdpSend {
public static void main(String[] args) throws Exception {
//通过DatagramSocket建立Socket服务
DatagramSocket ds=new DatagramSocket();
//创建数据
byte[] b="hello ,i am udp ".getBytes();
//将数据封装到包中
DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.118"),10000);
//通过send方法发送
ds.send(dp);
//关闭资源
ds.close();
}
}
接收端:
package net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 需求:
* 定义一个应用程序,用于接收udp协议传输的数据
*
* 思路:
* 1,定义udpSocket服务,通常会监听一个端口,其实就是给这个接收网络应用程序定义数据标识。
* 2,定义一个数据包,因为要存储接收到的字节数据
* 因为数据包对象中有更多动能可以提取到字节书中的不同数据信息。
* 3.通过socket服务的receive方法将接收到的数据存入已定义好的数据包中。
* 4.通过数据包对象的特有动能,将这些不同数据取出打印在控制台上,
* 5.关闭资源
*
*/
public class Udprecever {
public static void main(String[] args) throws Exception {
//建立udpsocket服务
DatagramSocket ds=new DatagramSocket(10000);
//定义一个数据包,用于接收数据
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: "+data+port);
ds.close();
}
}
接收端接收的数据结果:
通过键盘录入,发送数据以及接收数据
键盘录入发送端
package net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpSendByKey {
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)
{
if("886".equals(line))
break;
byte[] b=line.getBytes();
DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.118"),10001);
ds.send(dp);
}
ds.close();
}
}
接收端:
package net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpSendByKey {
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)
{
if("886".equals(line))
break;
byte[] b=line.getBytes();
DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.118"),10001);
ds.send(dp);
}
ds.close();
}
}
发送的数据及接收结果
练习:模拟聊天
/*
* 编写一个聊天程序,有收数据的部分,和发数据的部分,这两部分需要同时执行,那就需要用到多线程技术,
* 一个线程接收,一个线程控制发,因为收和发动作是不一致的,所以要定义两个run方法;
* 而且这两个方法要封装到不同的类中。
*/
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class Send implements Runnable
{
private DatagramSocket ds;
Send(DatagramSocket ds)
{
this.ds=ds;
}
public void run() {
try
{
BufferedReader bur =new BufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=bur.readLine())!=null)
{
if("886".equals(line))
break;
byte[] b=line.getBytes();
DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.118"),10003);
ds.send(dp);
}
} catch (Exception e)
{
throw new RuntimeException("发送端失败");
}
}
}
class Rece implements Runnable
{
private DatagramSocket ds;
Rece( DatagramSocket ds)
{
this.ds=ds;
}
public void run() {
try {
while(true)
{
byte[] b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
ds.receive(dp);
String data=new String(dp.getData(),0,dp.getLength());
String ip=dp.getAddress().getHostAddress();
System.out.println(data+"...."+ip);
}
} catch (IOException e) {
throw new RuntimeException("接收端失败");
}
}
}
public class Copyqq {
public static void main(String[] args) throws Exception {
new Thread(new Send(new DatagramSocket())).start();
new Thread(new Rece(new DatagramSocket(10003))).start();//接收端要指定接收端口,发送端可有可无。
}
}
TCP传输
Socket和ServerSocket
建立客户端和服务器端
建立连接后,通过Socket中io流进行数据的传输
关闭Socket
同样,客户端与服务器端是两个独立的应用程序。
tcp分客户端和服务端,客户端对应的Socket,服务端对应的是ServerSocket
客户端:
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机因为tcp是面向连接的,所以在建立socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据的传输。
步骤:
1.创建socket服务,并指定要连接的主机和端口
2.获取输出流,并发送
3.关闭服务
服务端:
1.建立服务端的socket服务,ServerSocket();并监听一个端口。
2.获取连接过来的客户端对象,通过ServerSocket的accept方法,没有连接就会
等,所以这个方法时阻塞式的。
3.客户端如果发送过来数据,那么服务端要使用对应的客户端对象,并获取该客户端对象的
读取流来读取发送过来的数据并打印在控制台上
示例:
客户端发送数据:
package net;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
//需求:用TCP从客户端发送一句话到服务端,服务端接收
public class TcpCust {
public static void main(String[] args) throws Exception, IOException {
//建立客户端,并指定接收端口
Socket s=new Socket("192.168.1.118",10004);
OutputStream out=s.getOutputStream();
out.write("hello,good morning".getBytes());
s.close();
}
}
服务端接收数据:
package net;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
//创建服务端接收数据
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10004);
Socket s=ss.accept();
InputStream in=s.getInputStream();
byte[] b=new byte[1024];
int num=in.read(b);
String data=new String(b,0,num);
String ip=ss.getInetAddress().getHostAddress();
System.out.println("data:"+data+".."+ip);
s.close();//关闭客户端
ss.close();//关闭服务端(可选操作)
}
}
服务端接收结果;
tcp示例2
/*
* 演示tcp传输的客户端和服务端的互访。
* 需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
*
*
* 步骤:
* 1,建立socket服务,指定要连接主机和端口
* 2.获取socket流中输出流,将数据写到该流中,通过网络发送到服务端。
* 3.获取socket流中输出流,将服务端反馈的数据获取到,并打印。
* 4.关闭客户端资源。
*
*/
package net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TcpCus2 {
public static void main(String[] args) throws Exception, IOException {
Socket s=new Socket("192.168.1.110",10000);
OutputStream out=s.getOutputStream();
out.write("服务端,你好,在么?".getBytes());
InputStream in=s.getInputStream();
byte[] b=new byte[1024];
int num=in.read(b);
String data=new String(b,0,num);
System.out.println(data);
s.close();
}
}
package com.itheima.dom4j;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer2 {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(10000);
Socket s=ss.accept();
InputStream in=s.getInputStream();
byte[] b=new byte[1024];
int num=in.read(b);
String data =new String(b,0,num);
System.out.println(data);
OutputStream out=s.getOutputStream();
out.write("你好,客户端,我在,".getBytes());
s.close();
ss.close();
}
}
tcp练习三:
/*
* 需求:建立一个文本转换服务器
* 客户端给服务端发送文本,服务端将文本转成大写再返回给客户端
* 而且客户端可以不断地进行文本装换,当客户端输入over的时候,转换结束。
*
* 分析:
* 客户端:既然是操纵设备上的数据,那么就使用io技术,并按照io的操作规律来思考
* 源:键盘录入
* 目的:网络设备,网络输出流
* 而且操作的时文本数据,可以选择字符流
*
* 步骤
* 1,建立服务
* 2.获取键盘录入
* 3.将数据发给服务端
* 4.读取服务端返回的大写数据,
* 5.结束,关资源
*
* 都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
*
*/
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TcpCusTest {
public static void main(String[] args) throws Exception, IOException {
Socket s=new Socket("192.168.1.110",10014);
//键盘录入
BufferedReader bur=new BufferedReader(new InputStreamReader(System.in));
//字符输入缓冲区
//BufferedWriter buw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
//字符读取缓冲区
BufferedReader bur_ser=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=null;
while((line=bur.readLine())!=null)
{
if("over".equals(line))
break;
pw.println(line);//自动刷新
String data=bur_ser.readLine();
System.out.println(data);
}
bur.close();
s.close();
}
}
package net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpSerTest {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(10013);
Socket s=ss.accept();
BufferedReader bur=new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
while((bur.readLine())!=null)
{
String data=bur.readLine();
pw.println(data.toUpperCase());
}
s.close();
ss.close();
}
}
上传图片
/*
* 客户端
* 1.服务端点
* 2.读取客户端已有的图片数据
* 3通过Socket 输出流将数据发给服务端
* 4.读取服务端反馈信息
* 5.关闭
*
*/
package net2;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class PicCus {
public static void main(String[] args) throws Exception, IOException {
Socket s=new Socket("192.168.1.107",10001);
//定义字节输出流,并加入字节缓冲区
FileInputStream fi=new FileInputStream("c:\\1.jpg");
//获取socket字节输出流,向客户端写数据
OutputStream out=s.getOutputStream();
int num=0;
byte[] b=new byte[1024];
while((num=fi.read(b))!=-1)
{
out.write(b,0,num);
}
//告诉服务端数据已写完
s.shutdownOutput();
//获取socket字节读取流,读取服务端的反馈信息,因为信息是字符,所以加入字符缓冲区提高效率
BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=bufr.readLine();
System.out.println(line);
s.close();
bufr.close();
}
}
package net2;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class PicServer {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(10001);
Socket s=ss.accept();
InputStream in=s.getInputStream();//获取socket中的字节读取流,读取客户端发来的数据
BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream("c:\\copy1.jpg"));
byte[] b=new byte[1024];
int num=0;
while((num=in.read(b))!=-1)
{
out.write(b, 0, num);
}
//获取socket中的输出流,向客户端发送反馈信息
OutputStream sout=s.getOutputStream();
sout.write("图片已收到".getBytes());
s.close();
ss.close();
sout.close();
}
}
这个服务端哟个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程,
这时 B客户端连接,只有等待,因为服务端还没有还没有处理完A客户端的请求,还有循环回来
执行下次accept方法,所以暂时获取不了B客户端对象。
那么为了可以让多个客户端同时并发访问服务器,服务端最好就是将每个客户端封装
到一个单独的线程中,这样就可以同时处理多个客户端请求。
如何定义线程?
主要明确了每一个客户端要在服务端执行的代码即可,将该代码存入run方法中。
客户端:
package net2;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class PicCus {
public static void main(String[] args) throws Exception, IOException {
if(args.length!=1)
{
System.out.println("请选择一个jpg格式的图片");
return ;
}
File file=new File(args[0]);
if(!(file.exists() && file.isFile()))
{
System.out.println("该文件有问题,要么不存在,要么不是文件");
}
if(!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择");
return;
}
if(file.length()>1024*1024*8)
{
System.out.println("文件过大");
return;
}
Socket s=new Socket("192.168.1.107",10001);
//定义字节输出流,并加入字节缓冲区
FileInputStream fi=new FileInputStream(file);
//获取socket字节输出流,向客户端写数据
OutputStream out=s.getOutputStream();
int num=0;
byte[] b=new byte[1024];
while((num=fi.read(b))!=-1)
{
out.write(b,0,num);
}
//告诉服务端数据已写完
s.shutdownOutput();
//获取socket字节读取流,读取服务端的反馈信息,因为信息是字符,所以加入字符缓冲区提高效率
BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=bufr.readLine();
System.out.println(line);
s.close();
bufr.close();
}
}
服务端:
package net2;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
class PicThread implements Runnable
{
private Socket s;
PicThread( Socket s)
{
this.s=s;
}
public void run() {
try
{
int count=1;
String ip=s.getInetAddress().getHostAddress();
File file=new File("c:\\"+ip+"("+(count)+")"+".jpg");
while(file.exists())
file=new File("c:\\"+ip+"("+(count++)+")"+".jpg");
InputStream in=s.getInputStream();
BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream(file));
byte[] b=new byte[1024];
int num=0;
while((num=in.read(b))!=-1)
{
out.write(b, 0, num);
}
//获取socket中的输出流,向客户端发送反馈信息
OutputStream sout=s.getOutputStream();
sout.write("上传成功".getBytes());
s.close();
out.close();
}
catch(Exception e)
{
throw new RuntimeException("上传失败");
}
}
}
package net2;
import java.net.ServerSocket;
import java.net.Socket;
public class PicServerThread {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(10005);
while(true)
{
Socket s=ss.accept();
new Thread(new PicThread(s)).start();
}
}
}
/*
* 演示客户端和服务端
*
* 1.客户端:浏览器
* 服务端:自定义
*
*/
package net2;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(10003);
Socket s=ss.accept();
System.out.println(s.getInetAddress().getHostAddress());
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
out.println("客户端你好");
s.close();
ss.close();
}
}
url演示
package net2;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
public static void main(String[] args) throws Exception {
URL url=new URL("http://192.168.1.100:8080/myweb/demo.html?name=haha&age=30");
//获取此url的协议名称
System.out.println("getProtocol() "+url.getProtocol());
//获取url的主机名
System.out.println("getHostl() "+url.getHost());
//获取此url的端口号
System.out.println("getPort() "+url.getPort());
//获取此url的路径部分
System.out.println("getPath() "+url.getPath());
//获取此url的文件名
System.out.println("getFile() "+url.getFile());
//获取此url的查询部
System.out.println("getQuery() "+url.getQuery());
}
}