----------------- android培训,java培训、期待与您交流! --------------------
网络编程
1.网络编程概述
网络的模型OSI模型,TCP/IP参考模型。网络通讯要素:ip地址,端口号,传输协议。也就是说,我想和某台机器进行网络的通讯,第一,必须知道你要通讯的机器在哪(IP);第二,计算机上有多个应用程序。你想要和计算机上的哪个应用程序进行通讯,需要明确指定应用程序。(端口);第三,通过一二两个步骤,把基本的通讯通道搭建完成了。在通讯过程中,可以有多种通用通讯规则。你需要明确指定一种通讯的规则完成通讯,如果两者的协议不同不能进行通讯。(传输协议,国际通用TCP/IP)。
端口号的范围:0—65535,0-1024一般被系统的应用程序占用了。
2.网络模型
OSI参考模型:应用层,表示层,会话层,传输层,网络层,链路层,物理层。数据传递过程中经过的那么多层,相当于给数据一层一层地套上外衣(数据封包)。传到另外一台机器上的时候,从底层往上层对应地一层一层脱衣服(数据拆包),最后会到应用层。当应用层拆完数据包后,就会收到传递来的数据。
TCP/IP参考模型(对OSI参考模型进行了简化):应用层(应用层,表示层,会话层),传输层,网际层(网络层),主机至网络层(数据链路层,物理层)。
现在学的是传输层(因为做软件,通过软件来发送数据,协议是tcp/udp)。而web应用程序,属于应用层(协议是http,ftp等)。网络层的协议是:ip。
3.网络通讯要素
IP地址:网络中的设备标示。不易记忆,可用主机名。本地回还地址:127.0.0.1 主机名:localhost。在java语言中提供了方便操作的IP对象InetAddress。关于网络编程的对象java.net包中。代码示例:
//获取本地的的InetAddress包含了主机名字,和ip地址。(重点掌握)
InetAddress ia1 = InetAddress.getLocalHost();
System.out.println(ia1);
// 通过主机名字来获取InetAddress,(重点掌握)
InetAddress ia2 = InetAddress.getByName("www.baidu.com");
System.out.println(ia2.toString());
// 通过主机名字来获取所有的InetAddress
InetAddress[] ia3 = InetAddress.getAllByName("www.baidu.com");
System.out.println(ia3.length);
for(InetAddress ip :ia3){
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
}
// toString() = getHostAddress()(重点掌握)+getHostName()
端口号:用于标识进程的逻辑地址,不同进程的标识。有效端口号是:0~65535,其中0~1024系统使用或保留端口。端口没有提供对象,因为它就是一个整数。
传输协议:通讯的规则,常见的有TCP/UDP。提供了相对应的对象。
(重点)TCP和UDP的区别:
UDP:
A,将数据以及源和目的封装在数据包中,不需要建立连接。B,每个数据报的大小限制在64k内。C,因为无连接,是不可靠的协议。D,不需要建立连接,速度快。(生活中相当于到邮局寄东西,地址(IP),具体人(端口))。聊天udp,视频直播软件,qq,求速度的软件一般都是udp。
TCP:
A,建立连接,形成传输数据的通道。B,在连接中进行大数据量的传输。C,通过三次握手完成连接,是可靠的协议。D,必须建立连接,效率速度会稍低。
例:下载。
4,Socket
Socket就是为网络服务提供的一种机制。通信的两端都有Socket。网络通信其实就是Socket间的通信。数据在两个Socket间通过IO传输。
5,UDP的发送端
1. 建立udpsocket服务。(邮局)
2. 提供数据,并将数据封装到数据包中。(寄的东西,数据,地址,端口)
3. 通过socket服务的发送功能,将数据包发出去。(将已有的数据包发送出)
4. 关闭资源。
5.代码示例:
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UdpSend {
public static void main(String[] args) {
// 1.创建udpsocket服务。不指定发送端的具体端口号,让系统默认产生,也可以自定义。
DatagramSocket sendSocket = null;
try {
sendSocket = new DatagramSocket();
// 2.创建数据包,将要发送的数据和发送的地址以及端口号,写入包中。
byte[] buf = "itheima".getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress
.getByName("localhost"), 12345);
// 3.将数据包通过socket发送出去。
sendSocket.send(dp);
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 4.关闭资源
finally {
if (sendSocket != null) {
sendSocket.close();
}
}
}
}
6.UDP的发送端
1. 定义udpsocket服务。通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标示。方便于明确哪些数据过来该应用程序可以处理。
2. 定义一个数据包,因为要存储接收到的字节数据。因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
3. 通过socket服务的receive方法将要收到的数据存入已经定义好的数据包中。
4. 通过数据包对象的特有功能。将这些不同的数据取出。
5. 关闭资源。
6. 代码示例:
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpReceive {
public static void main(String[] args) {
// 1.创建接收端的udpsocket,并监听一个端口用来匹配发送端指定的端口
DatagramSocket ds = null;
try {
ds = new DatagramSocket(12345);
// 2.创建一个数据包用来存储发送端发来的数据包。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
// 3.调用DatagramSocket的recieve方法,来接收发送端发来的数据。
ds.receive(dp);
// 4.dp已经接收到发来的额数据包,通过dp获取数据包中的数据。
String hostName = dp.getAddress().getHostName();
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(hostName+"...."+ip+"...."+data+"...."+port);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 关闭资源
finally{
if(ds!=null){
ds.close();
}
}
}
}
7.UDP的键盘录入方式
用键盘录入的方式实现对讲机功能,代码如下:
发送端: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;
import java.net.SocketException;
public class UdpSend2 {
public static void main(String[] args) {
DatagramSocket ds = null;
try {
// 创建udpsend的socket服务
ds = new DatagramSocket();
// 键盘输入,并读取键盘数据
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line = br.readLine()) != null) {
// 键盘录入"886“时候,跳出循环,程序停止。并将socket关闭
if(line.equals("886"))
break;
// 将键盘录入的数据封装到了一个数据包里
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName("localhost"), 12345);
// 发送数据包
ds.send(dp);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(ds!=null)
ds.close();
}
}
}
发送端:package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.DatagramSocketImpl;
import java.net.SocketException;
public class UdpReceive2 {
public static void main(String[] args) {
// 建立udpreceive的socket接收端,并监听端口。不能关闭,因为关闭之后就无法再接收到发来的内容。
DatagramSocket ds=null;
try {
ds = new DatagramSocket(12345);
} catch (SocketException e) {
e.printStackTrace();
}
while (true) {
// 定义数据包接收数据。
byte[] buf = new byte[1024*64];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//接收数据,将接受的数据存储在dp中。
try {
ds.receive(dp);
// 从dp中获得数据,並打印。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
8.UDP聊天
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;
import java.net.SocketException;
public class UdpQQ {
public static void main(String[] args) throws Exception {
DatagramSocket sendds = new DatagramSocket();
DatagramSocket recds = new DatagramSocket(10002);
new Thread(new Send(sendds)).start();
new Thread(new Rec(recds)).start();
// new Thread(new Send(new DatagramSocket())).start();
// new Thread(new Rec(new DatagramSocket(10002))).start();
}
}
// 想要在一个进程里面既可以发数据也可以接收数据,那么就用到多线程技术。
class Send implements Runnable {
private DatagramSocket sendDs;
public Send(DatagramSocket ds) {
this.sendDs = ds;
}
@Override
public void run() {
try {
// 建立socket服务。
// 从键盘读取数据,并把数据封装到数据包中,发送给接收端。
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line = br.readLine()) != null) {
if (line.equals("886"))
break;
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName("localhost"), 10002);
sendDs.send(dp);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sendDs != null)
sendDs.close();
}
}
}
class Rec implements Runnable {
private DatagramSocket recDs;
public Rec(DatagramSocket ds) {
this.recDs = ds;
}
@Override
public void run() {
try {
// 创建一个接收端的socket服务。
while (true) {
// 定义一个数据包用来接收发来的数据。
byte[] buf = new byte[1024 * 64];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 接收数据。
recDs.receive(dp);
// 获得数据包中的数据。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip + "::" + data);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结:
如果想要把发送端和接收端不在同一个窗口中输入和输出。其实,平时的(我和你)qq聊天的原理是:1,我上面有一个DatagramSocket发送端,发送数据的时候发送了两次。一个往你上面发(数据包指向本地地址和端口号),一个往自己本地上发(数据包指向你地址和端口号)。2,我上面有一个DatagramSocket接收端,接收数据既有你的信息,又有我的信息。3,你上面和我上面得同理。4,不管你还是我,涉及到的所有端口号必须是一样的。