网络编程
传输协议:UDP与TCP
UDP:将数据源和目的封装在数据包中(每个数据包的大小在64k以内),不需要建立连接,传输速度快,可以不需要对方在线,但不够安全。通常用于视频等大流量数据传输
TCP:传输效率低,对方必须在在线,否则数据会丢失,安全(因为需要三次握手:你在不?我在!哦,我知道了!)
UDP相对于TCP而言,是缺少一个可靠的丢失重发机制,因此可以立即返回,所以你觉得快
UDP属于发射后不管,但是从IP层来说,它的效率和TCP相比,几乎相同
TCP为什么慢呢?就是因为需要 发射 确认 这样一个循环过程,所以慢
现在喜欢用UDP代替TCP的原因主要是 TCP的重发机制不完美,等待时间不合理,响应经常偏慢
UDP的问题主要在于丢包,如果你的API层协议规定部分数据可以丢失,那么UDP的响应速度会是最好的选择
同样,如果规定绝对不可以丢包,那么需要你自己在API或者引擎里负责处理UDP的可靠传输
一般测试下,在相对可靠的环境里,UDP的丢包率很低,因此即使采用确认模式传输,速度也很快
但是,如果在INTERNET上,例如 电信到CNC线路,丢包率是偏高的,效率会严重降低,CPU开销剧烈增加[主要是确认机制导致的]
Socket是为网服务提供的一种机制,在通信的两端都有Socket间的通信,网络通信就是Socket间的通信,数据在两个Socket间通过IO传输来完成。
UDP传输:
1:UDP传输需要两个对象:DatagramSocket(UDPsocket服务)和DatagramPacket(数据包对象)
2:建立发送端和接收端(发送端与接收端是两个独立运行的程序,也就是需要建立两个主函数)
3:建立数据包
4:调用Socket的发送(send)和接受方法
5:关闭Socket资源
import java.net.*;
//发送端
class Send
{
public static void main(String[] args)
{
//建立Socket服务
DatagramSocket ds = new DatagramSocket();
准备数据,需要的是字节数组
byte[] buf = "UDP,哥们来了!".getBytes();
//将数据封装成数据包对象。既然用来发送,所以数据包对象必须要明确目的地址和端口
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAdress.getByname("192.168.54.2"),1232);//发送buf中的数据,且长度为该字节数组的长度,获取一个IP地址,确定一个发送端口(1232)
//使用UDP中的Socket服务的send方法将数据包发出
ds.send(dp);
//关闭资源
ds.close();
}
}
/*
接收端:
1,建立UDP的Socket服务。为了明确接收端要处理哪部分数据,所以要给接收端一个具体的数字端口,来监听一个具体的端口,只要给这个端口发送数据,接收端就会有相 应的应用程序来解析
2,准备好一个数据包用来存放接收到的字节数据,并通过数据包对象的方法对该数据包中的数据进行解析
3,使用UDP中的接受方法,将接收到的数据都储存到准备好的数据包中
4,将接收到的数据解析出来,并打印在控制台上
5,关闭资源
class Rece
{
public static void main(String[] args)
{
//创建UDP的接收端的Socket服务,确定要接受的端口
DatagramSocket ds = new DatagramSocket(1232);
//准备好一个用于存储接收到的数据的数据包
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramSocket(buf,buf.length);
//使用UDP的Socket服务的接受方法,将接收到的数据储存到数据包中
ds.receive(dp);//阻塞式方法
//通过数据包对象的方法获取数据包中的数据,同时获取包的数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"text+".."+port);
ds.close;
}
}
以上是两个线程,可以在编译前通过start再开启一个线程。最好先运行接收端,这样可以避免丢包。且一个端口一次只能接受一条信息,随着条数的增多,发送端口数会依次增多。
群聊程序
思路:该程序既可以发送数据,又可以接受数据,这样也可以同时进行,所以要用到多线程技术(一个负责发送,一个负责接收)。需要用到两个run方法进行线程的封装,并且同时封装到两个实现了Runnable的接口类中。
import java.io.*; //用于读取键盘
import java.net.*;
class Send implements Runnable
{
private DatagramSocket ds;
Send(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
try
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//键盘读取高效录入
String line = null;
while((line=bufr.readLine())!=null)
{
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAdress.getByName("192.168.54.255"),1232);//将录入的数据打包,发送地址及端口
ds.send(dp);//将打包后的数据发送出去
//如果发送的是886就结束程序
if("886".equals(line));
break;
}
}
ds.close();//关闭流资源
}
catch(Exception e)
{
throw new RuntimeException("发送失败!");
}
}
}
class Race implements Runnable
{
private DatagramSocket ds;
Race(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
try
{
while(true)//可以多次接受数据
{
byte[] buf = new byte[1024];
DatagramPacket dp new DatagramPacket(buf,buf.length);//操作数组中的数据
ds.receive(dp);//将数据打包
String ip = dp.getAddress().getHostAddress();//获取包的ip地址
String text = new String(dp.getData(),0,dp.getLength());//获取数据所在的数组,并获取该数组长度的全部数据
System.out.println(ip+":"+text);
if(text.equals("886"))
{
System.out.println(ip+"...退出聊天室");
}
}
}
catch(Exception e)
{
throw new RunntimeException("接收端失败!");
}
}
}
class ChatDemo
{
public static void main(String[] args)
{
DatagramSocket send = new DatagramSocket();
DatagramSocket race = new DatagramSocket(1232);
new Thread(new Send(send).start();
new Thread(new Send(rece).start();
}
}