java 网络编程
两台主机之间是怎么联系的呢,为什么我用QQ给你发信息你在另个地方能收到呢?我们知道每台主机都有
它自己的ip地址,例如我和你在用QQ交流,那我是不是得先找你的IP地址,然后再找你电脑上的QQ端口,
不然的话有可能就会出现我用QQ发送消息,而你用msn收到的情况。
网络通讯三要素:
1.IP地址
2.端口号 主机上这么多应用程序,数据怎么发送到指定的应用程序上的呢?所以为了标识这些应用层序
给这些应用程序都用数字进行了标识,就是端口(逻辑端口)。
3.传输协议 你玩网游的为什么能和外国的同志一起玩啊,什么韩服台服,那就是因为你们通讯的数据都
遵循了同一个规则,这个通讯规则称为协议。国际组织定义通用协议为TCP/IP。
*******************************************************************************************************
* 本地回环地址:127.0.0.1此ip地址是本地回环地址,子你的电脑没有配置任何ip地址的情况下,默认的就
* 是它。装上网卡就有,所以可以用它来测试网卡,即 ping 127.0.0.1。
*
* 保留地址:一般不用于公网上而用局域网内部,如10.10段与192.168段,192.168段的是比较常用的保留地址段、
*
* 常用的端口有:web服务端口为80,tomcat服务器默认是80.80,mysql默认端口3306。
*******************************************************************************************************
网络模型:
OSI参考模型:OSI(Open System Interconnect)开放式系统互联。 一般都叫OSI参考模型,是ISO(国际
标准化组织)组织在1985年研究的网络互联模型。
TCP/IP参考模型:简化成了四层,应用层表示层会话层为化为应用层,传输层仍为传输层,网络测光为网
际层,数据链路层与物理层合为主机至网络层。
IP地址协议在网际层上,TCP、UDP协议在传输层上,我们搞软件开发,就在网络层与传输层上混,所以要
重点了解下IP协议与TCP、UDP协议。
IP地址:InetAddress
IP地址是网络中设备的标识,我们知道IP地址有很多段,也有很多数字,很不容易记,所以一般都给
它起个名字,对于本地回环地址127.0.0.1,它的主机名为:localhost。其实我们上网的时候输入的
网址像www.baidu.com什么的,其实就是百度的主机名,是不是比让你记它的ip地址好记啊,www什么
的只不过是起名字的太多,让它们起名字时遵循的规则。
通过查看API发现InetAddress类中没有构造函数,所以能肯定它里面有一个静态的方法,返回的是本
类对象,就像单例设计模式,再一看还真有一个getLocalHost()方法返回的是本类对象,然后根据它
里面的方法获取IP地址和主机名。
****************************************************************************************************
我们能拿到自己主机的Ip地址,那能不能获取任意一台主机呢?
我们想拿到任意一台主机的ip地址,首先要知道它的返回值类型是应该是 InetAddress对象,然
后想要知道哪一台主机的ip地址,是不是得把哪台的主机名传进来,通过查找API找到了这样一个
方法时getByName();
部分代码:
InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");
for(int x=0;x<arr.length;x++)
{
System.out.println("IPaddress:::"+arr[x].getHostAddress());
System.out.println("name:::"+arr[x].getHostName());
}
TCP与UDP
UDP面向无连接,就像是去邮局邮东西,要先把你有的东西封装成包,然后把要发到哪写到包裹上,
不管包裹上写的地址对不对就都发出去了,地址存在的话就收到了,地址要不存在的话包就没用
了就丢掉。就是我给你发数据你在不在我都发,你在的话就能收到,不在的话包里数据就没用了。
**********************************************************************************************
* UDP
* •将数据及源和目的封装成数据包中,不需要建立连接
* •每个数据报的大小在限制在64k内
* •因无连接,是不可靠协议
* •不需要建立连接,速度快
* 如聊天,飞秋,视频会议,凌波用的都是UDP因为它速度快。
*
* TCP
* •建立连接,形成传输数据的通道。
* •在连接中进行大数据量传输
* •通过三次握手完成连接,是可靠协议
* •必须建立连接,效率会稍低
* 下载是 TCP 不能丢失数据
************************************************************************************************
Socket
Socket插座的意思通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。一台主机
肯定运行了不止一个应用程序,那么通讯的时候怎么找到相应的程序呢,就是我的QQ怎么找到你的
QQ呢,就是通过Socket。因为每个程序都有一个Socket并且绑定在了端口上,所以找到了Socket就
找到了需要的那个程序。
一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供
110伏交流电,有的则提供有线电视节目。 客户软件将插头找到不同编号的插座,就可以得到不同
的服务。
Socket 特点:
•Socket就是为网络服务提供的一种机制。
•通信的两端都有Socket。
•网络通信其实就是Socket间的通信。
•数据在两个Socket间通过IO传输。
******************************************************************************************************
UDP数据传输
从上面知道,两个应用程序之间要传送数据,是不是要先有Socket?UDP是通过DatagramSocket类建立端
点,DatagramSocket 类表示用来发送和接收数据报包的套接字。就是它既能发送又能接收数据,既然此
类能发送和接收数据,那么这个封装的类里面应该就会提供这样的方法,通过查看API发现有这样的方法,
就是receive(DatagramPacket p)和send(DatagramPacket p)方法。
我们知道UDP的特点是面向无连接,要把数据封包并且每个数据包大小不超过64k,那既然把数据封包了,
传送和接收数据的时候是不是都要操作这个数据包啊?意思就是我发送数据包,我接收数据包,操作的
都是这个数据包吧?所以 DatagramSocket 类中的receive和send方法都要接收一个数据包参数。我们说
过UDP就像寄包裹,那包裹上是不是要有收件人的地址和端口和发件人的地址和端口啊?这样是不是很复
杂?于是java把那个数据包也封装成了对象了,就是DatagramPacket类。
DatagramSocket 类
此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台
机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同
的顺序到达。不对包投递做出保证
。
DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
****************************************************************************************************
怎么使用UDP传输方式来发送数据呢?
1.肯定是先创建udpSocket服务,就是端点。 就像寄东西是不是得先找邮局
2.把要发送的数据封装起来 是不是要把你寄的东西给封装起来贴上地址什么的
3.通过Socket的方法操作数据 然后就通过邮局的方法把数据发出去或接收数据
4.这个国过程调用了底层资源所以要关闭关闭资源
****************************************************************************************************
UDP发送端
在发送端,要在数据包对象中明确目的地 IP及端口。
部分代码:(UDP发送端)
class UdpSend
{
public static void main(String[] args) throws Exception
{
//1,创建udp服务。通过DatagramSocket对象。
DatagramSocket ds = new DatagramSocket();
//2,确定数据,并封装成数据包。
byte[] data = "UDP方式传送数据 ".getBytes();//将传送的数据转换为字节数组
DatagramPacket dp =
new DatagramPacket(data,data.length,InetAddress.getByName("192.168.126.1"),10000);
//3,通过socket服务,将已有的数据包发送出去。通过send方法。
ds.send(dp);
//4,关闭资源。
ds.close();
}
}
UDP接收端
在接收端,要指定监听的端口。
/*
需求:定义一个应用程序,用于接收udp协议传输的数据并处理的。
定义udp的接收端。
思路:
1,定义udpsocket服务。通常会监听一个端口。你看发送端是往你这台主机上标示为10000端口的那
个应用程序上发的数据,而你要接收此数据的话,你是不是得证明你是10000端口啊?
2,定义一个数据包,来存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
3,通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
4,通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。
5,关闭资源。
*/
部分代码:(UDP接收端)
class UdpRece
{
public static void main(String[] args) throws Exception
{
//1,创建udp socket,建立端点。并标明要监听的端口
DatagramSocket ds = new DatagramSocket(10000);
while(true)//因为receive是个阻塞式方法所以不会死循环
{
//2,定义数据包。用于存储接收过来的数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3,通过服务的receive方法将收到数据存入数据包中。
ds.receive(dp);//阻塞式方法。
//4,通过数据包的方法获取其中的数据或其他信息。就像你接收到包裹之后,不光能
//获得包裹里的数据,还能从包裹上获得发件人的地址啊端口啊什么的
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+"::"+data+"::"+port);
}
//5,关闭资源
//ds.close();
}
}
****************************************************************************************************
UDP键盘录入方式数据:
就是把数据存放到数据包里的时候,数据的源变了,不再是内存上的数据而是键盘录入的了,这时候就
想到了io流时总结的规律了,也就是那几个明确,明确源和目的;明确是否为纯文本;明确设备;
部分代码:
DatagramSocket ds =new DatagramSocket();
//2.把数据封装成包
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null)
{
if("886".equals(line))
break;
byte[] data = line.getBytes();
DatagramPacket dp =
new DatagramPacket(data,data.length,InetAddress.getByName("192.168.126.1"),10000);
ds.send(dp);
}
TCP传输:
1,tcp分客户端和服务端。
2,客户端对应的对象是Socket。
服务端对应的对象是ServerSocket。
***************************************************************************************************
* TCP传输因为是面向对象的,所以客户端一开始就应该连上服务端,连接成功之后怎么处理数据呢?
* 我们直到UDP中的 DatagramSocket即可以发送也可以接收数据,就是把数据封装之后,用DatagramSockt
** 类里的方法接收或者发送就行了。而TCP不是,它是客户端与服务端连接形成通道之后,用Socket里的输
* 入输出流来操作数据的。
* 所以用TCP时,连接成功之后,要用Socket里的方法获取到流之后再操作数据,而这时服务端要和客
* 户端交互的话怎么来操作数据呢?服务端是这样的谁连接到它,它就获得谁的对象,然后再用谁里面的流
* 来操作数据。比如说张三连接到了服务端,那这时服务端就获得了张三的对象,然后再用张三对象里的方
* 法来获取流进而来操作数据。
***************************************************************************************************
客户端
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的。所以
在建立socket服务时就要有服务端存在,并连接成功。形成通路后,在该通道进行数据的传输。
基本思路(客户端)
•客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
•连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象
已经提供了输入流和输出流对象,通过 getInputStream(),getOutputStream()获取即可。
•与服务端通讯结束后,关闭Socket。
Socket构造函数:Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
需求:给服务端发送给一个文本数据。
思路:
1.创建Socket服务,并指定要连接的主机和端口。
2.通过Socket中的方法来获得IO流进行数据的传输
3.关闭socket
部分代码:
class TcpClient
{
public static void main(String[] args) throws Exception
{
//创建客户端的socket服务。指定目的主机ip和端口
Socket s = new Socket("192.168.126.1",10003);
//为了发送数据,应该获取socket流中的输出流。
OutputStream out = s.getOutputStream();
//用io流的方法操作数据
out.write("TCP方式传输数据".getBytes());
//关闭Socket
s.close();
}
}
********************************************************************************************************
服务端:
服务端要先创建 ServerSocket并指定要监听的端口,然后获取客户端的对象,然后再用客户端对象的方
法来获取io流操作数据。
基本思路(服务端)
•服务端需要明确它要处理的数据是从哪个端口进入的,就是要监听哪个端口
•当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该
对象与客户端通过IO流进行数据传输。
•当该客户端访问结束,关闭该客户端。
部分代码:
class TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服务端socket服务。并监听一个端口。
ServerSocket ss = new ServerSocket(10086);
//通过accept方法获取连接过来的客户端对象。
while(true)
{
Socket s = ss.accept();//获取客户端对象
//先获取IP地址
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connected");
//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据。
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();//关闭客户端.
}
}
}
************************************************************************************
TCP传输练习
需求:建立一个文本转换服务器。
客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。
而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。
分析:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。
而且操作的是文本数据。可以选择字符流。
都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
思路:
1,建立服务。
2,获取键盘录入。
3,将数据发给服务端。
4,获取服务端返回的大写数据。
5,结束,关资源。
具体代码及注意事项参看博客---TCP练习之文本转换服务器
/*
需求:上传图片。
客户端。
1,服务端点。
2,读取客户端已有的图片数据。
3,通过socket 输出流将数据发给服务端。
4,读取服务端反馈信息。
5,关闭。
*/
但是按上面写的话,只能一次传一个,就是服务器一次只能接受一个客户端对象,这显然是不行的,像
百度新浪不可能是对一个人服务完之后再去服务下一个的,那怎么解决呢,这就用到了多线程,让每一
个客户端连接成功之后都去创建一个线程,然后你该干嘛干嘛,就是来一个人创建一个线程来一个人创
建一个线程,这样是不是就可以并发上传图片啦?
那具体代码怎么写呢?我们可以把每个线程都需要执行的部分放到线程的run方法中,然后让实现Runnable
接口的类的构造函数都接受一个客户端Socket对象,然后在服务端的主线程上写一个while(true)循环,让
它循环的接收Socket对象并传给线程累的构造函数。
部分代码:
ServerSocket ss = new ServerSocket(10086);
while(true)
{
Socket s = ss.accept();
PicThread pt = new PicThread(s);
Thread t = new Thread(pt);
t.start();
}
具体代码参看博客---TCP练习之并发上传图片
***************************************************************************************************
自定义浏览器:
在文本框中输入网址点击转到弹出相应网址,用到了图形化界面。
部分代码:
private void showDir()throws Exception
{
String url = tf.getText();//http://192.168.1.254:8080/myweb/demo.html
//浏览器是客户端,要想与服务前连接必然要创建Socket并把主机与端口传进来
//输入一个网址的话,要获得主机与端口,那就需要从字符串中截取了
int index1 = url.indexOf("//")+2;
int index2 = url.indexOf("/",index1);
//取"//"与第一个"/"之间表示主机ip与端口的那个子串
String str = url.substring(index1,index2);
//在“ :”出切割成一个字符数组,
String[] arr = str.split(":");
//第一个是ip第二个是端口
String host = arr[0];
int port = Integer.parseInt(arr[1]);
//然后创建Socket进行数据操作
Socket s = new Socket(host,port);
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//向服务器发送一些消息头
out.println("GET /myweb/demo.html HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-cn");
out.println("Host: 192.168.1.254:11000");
out.println("Connection: closed");
//消息头与请求体之间一定要用空格隔开
out.println();
out.println();
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null)
{
//将从服务器读取的数据添加到文本区域中
ta.append(line+"\r\n");
}
s.close();
}
************************************************************************************************
URL类--URLConnection
从上面知道我们输一个url路径,然后想那个服务器发送请求,要先把那个url路径中的主机和端口获取
到然后再创建Socket服务才能与服务器连接,这期间又是获取子串又是切割冒号,是不是很麻烦啊?在
java中万物皆对象,这么麻烦在java中他肯定会封装一个对象来便于我们这样的操作,查看API发现URL
类正是这样的,它把这一系列麻烦的操作都封装起来了,我们只要拿过来调用就行了。
其中的方法有:
String getFile(): 获取此 URL 的文件名。
String getHost(): 获取此 URL 的主机名(如果适用)。
String getPath(): 获取此 URL 的路径部分。
int getPort(): 获取此 URL 的端口号。
String getProtocol(): 获取此 URL 的协议名称。
String getQuery(): 获取此 URL 的查询部
这样是不是就方便了许多啊?这还不算URL类中还有一个openConnection()方法,它可以直接给你连接好
服务器然后给你返回来个连接对象URLConnection。然后你可以用URLConnection的方法获取Socket流然
后直接操作数据。更爽的是它还有一个openStream方法可以直接返回一个用于从该连接读入的InputStream
输入流,是不是很爽,你都不用再创建Socket然后再用Socket对象获取流了。
部分代码:
URL url = new URL("http://192.168.1.254:8080/myweb/demo.html");
//用URL中的openConnection()方法直接创建连接
URLConnection conn = url.openConnection();
//URLConnection是一个抽象类,它有两个子类
System.out.println(conn);
//openStream方法其实就是这样:openConnection().getInputStream()
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
************************************************************************************************
DNS域名解析
浏览器中输入一个网址访问某台主机的时候,它到底做了什么事情?****
例如:http://192.168.126.1:8080/myweb/demo.html 首先浏览器在解析完这句话以后他是不是要先看
协议(如http://)啊,看完之后会启动相应的协议并解析后面的主机和端口,并把它们封装成Socket。
但是我们在输入的时候往往不写主机地址,而是写主机名称如"www.baidu.com",因为这样好记。当我们
写主机名的时候是不是要把它翻译成ip地址才能去访问这台主机?到哪去翻译IP地址呢?
** 想要将主机翻译成ip地址需要域名解析,就需要一个DNS域名解析服务器,当有一个网址的时候,浏览器
** 就要先去公网上找一台域名解析服务器,这台服务器上记载的都是一些知名网站的主机名与ip地址的映射
** 关系表。DNS把IP地址返给客户机之后,客户端就再向网站那台主机发请求,而且是发送到主机的8080端口。
**
** 如果你用的网是电信的,你没有自己配置的话,那它默认的是走电信的DNS服务器,当然也可以自己配置
** 联通的移动的啊什么的,但是为什么要指定电信联通什么的啊?就是谁离我近我走谁,人家美国也有个DNS
** 你走美国的也没问题,但是麻烦在哪呢?比如说你就想访问下百度,结果你还得先去美国的公网上那个DNS
** 服务器上查一下你上面有没有记载百度的IP地址啊,它有,就给你了,然后你再去访问百度。没有的话或
** 者时间长的话就解析失败,就显示该页无法显示。
**
** 但是http://127.0.0.1:8080 怎么和http://localhost:8080怎么是相对应的呢?也没连过公网啊,应为
** 他两个的映射关系在本机上的C:\Windows\System32\drivers\etc下的hosts文件里,还可以自己更改。所
** 以访问网址的时候浏览器其实是现在本机找有没有与那个网址对应的IP,没有的话再去公网找。所以你可
** 以把百度的ip地址与主机名的映射关系写在hosts里在访问百度的话速度会稍微快一点。所以在hostsi设
** 置的话可以屏蔽一些网站。
网络架构
C/S:Client/Server
客户端,服务端。
特点:
1,需要在客户端和服务端都需要编写软件。
2,维护较麻烦。
好处:可以减轻服务端的压力,如网络游戏。
B/S:Browser/Server
浏览器 ,服务端。
1,客户端不用单独编写软件。
因为客户端用的就是浏览器。
2,对于软件升级,只要考虑服务端即可。
弊端:所有的程序都运行在服务端,客户端的浏览器毕竟解析能力较弱。对游戏等。
************************************************************************************************
本章需要注意的知识点:
1.网络模型包含哪两种?(OSI与TCP/IP)
2.OSI模型分为几层?每层的功能是什么?(七层)
3.TCP/IP模型对OSI参考模型的改进有哪些?(四层)
4.怎么获取一个任意一台主机的IP地址?( InetAddress.getByName())
5.有哪几类特殊的IP地址段?(本地回环地址,保留地址段,广播)
6.TCP与UDP的区别?(面向连接与不连接,UDP数据封包等)
7.Socket套接字特点?(插座例子,两端都有Socket等)
8.怎么使用UDP传输数据?( DatagramSocket,DatagramPacket先创建服务在封装数据等 )
9.怎么使用UDP传输方式实现聊天功能?(键盘录入,一个线程发一个线程接收)
10.TCP的客户端与服务端的是怎么实现的?
11.TCP传输容易出现的问题?(阻塞时方法容易两端都在等待)
12.两端都在等待的问题是怎么解决的?(自定义结束标记或shutdownInput等方法)
13.TCP传输中怎么解决多个客户端同时访问服务端的问题?(多线程)
14.如何使用Socket向服务端上传图片?
15.如何实现聊天室的客户端与服务器端?
16.如何自定义一个浏览器与tomcat服务器交互?(从url路径中获得主机,端口再创建Socket)
17.URL类中什么方法可以解决上个问题中的效率问题?(openConnection()方法创建连接返回URLConnection对象)
18.掌握URLConnection类中的方法?(如获取url路径中ip端口等方法,和直接获得流的方法)
19.在浏览器中输入一个网址访问某台主机的时候它做了什么事情?(先本地再公网DNS返回ip后在访问主机)