(一)Udp通信
网络通讯协议:
udp协议
tcp协议
在java中不管是用哪种协议通信,计算机与计算机之间的通信都统称为Socket(插座)通信,通信的两端 计算机都必须要安装上Socket。
在不同的协议下应该用不同的Socket.
udp协议的特点
1.将数据及其源和目的封装为数据包,不需要建立连接。
2.每个数据包大小限制在64k中,基于数据包进行传输。
3.因为无连接,所以不可靠(会出现数据丢失)。
4.因为不需要建立连接,所有速度快。
5.udp协议不分客户端与服务器,只分发送端与接收端。
玩游戏是udp,卡机其实是数据包丢失。
比如:对讲机、飞Q通信、视频会议
udp通信:
DatagramSocket(udp协议的服务类)
DatagramPacket(数据包类)
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
buf:当前数据的字节数组的表现形式。
length:字节数组的长度。
address:发送的ip地址。
port:端口号。
//udp的发送端
public class demo1 {
public static void main(String[] args) throws IOException {
//第一步:建立udp的服务
DatagramSocket socket=new DatagramSocket();
//第二步:准备数据,把数据封装到数据包中
String data="这个是我的第一个udp的例子!";
byte[] buf=data.getBytes();
DatagramPacket packet=new DatagramPacket(buf, buf.length,InetAddress.getLocalHost(),9090);//InetAddress.getByName(ip地址)指定接收端ip地址
//第三步:调用udp的服务,发送数据
socket.send(packet);
//第四步:关闭资源(释放端口号)(io中关闭资源是释放资源文件)
socket.close();
}
}
//先启动接收端,再启动发送端
//udp服务的接收端
public class demo2_receiver {
public static void main(String[] args) throws IOException {
//第一步:建立udp的服务,并且压迫监听一个端口
DatagramSocket socket=new DatagramSocket(9090);
//第二步:准备一个空的数据包
byte[] buf=new byte[1024];
DatagramPacket packet=new DatagramPacket(buf,buf.length);
//第三步:调用udp的服务接收数据包,数据其实是存入了字节数组中,数据包依赖于字节数组存储数据。
socket.receive(packet);//receive()方法是一个阻塞型方法,如果没有接收到数据,会一直等待下去
//Scanner的next方法是阻塞型方法,只有接收到数据才会继续进行,否则会一致等待
System.out.println(packet.getAddress().getHostAddress()+"接收端接收到的数据:"+new String(buf,0,packet.getLength()));//getLength是获取本次接收到的字节个数);
//packet.getAddress().getHostAddress()获取到发送方的ip地址 getAddress是获取对方的IP地址对象 getHostAddress()获取ip地址
//第四步:关闭资源
socket.close();
}
}
udp协议可能导致数据丢失的情况:
1.带宽不足。
2.cpu处理能力不足。
(二)Tcp通信
tcp协议的特点:
1.面向连接,在传输数据之前一定要建立连接,tcp的客户端一旦建立,马上要与服务端建立连接。
2.在连接中可以传输大量数据,基于IO流进行传输。
3.通过三次握手机制连接,是可靠协议(保证数据传输的完整性)。
4.因为tcp是面向连接的,所有效率稍低。
5.tcp协议分客户端与服务端。
tcp协议下的Socket:
Socket(客户端类)
ServerSocket(服务端类)
比如:qq文件传输、打电话
//客户端
public class demo1 {
public static void main(String[] args) throws UnknownHostException, IOException {
//第一步:建立tcp的客户端服务
Socket socket=new Socket(InetAddress.getLocalHost(),9090);
//第二步:准备数据(基于io流传输数据),获取对应的流 通道
String data="这是第一个tcp例子";
//数据相对当前客户端来说是流出的,用输出流
OutputStream outputStream=socket.getOutputStream();
//第三步:把数据写出
outputStream.write(data.getBytes());
//客户端要接收服务端回送的数据
//获取socket的输入流
InputStream input=socket.getInputStream();
byte[] buf=new byte[1024];
int length=input.read(buf);
System.out.println("客户端接收到的内容:"+new String(buf,0,length));
//第四步:关闭资源
//outputStream.close();//io中关闭输出流是因为占用了文件资源,这里不需要关,从socket中获得,直接关socket即可
socket.close();
}
}
//先启动服务端,再启动接收端
//服务端 ServerSocket
public class demo2_server {
public static void main(String[] args) throws IOException {
//第一步:建立tcp的服务端
ServerSocket serverSocket=new ServerSocket(9090);
//第二步:接受客户端的连接
Socket socket=serverSocket.accept();//accept是阻塞型方法,没有客户端与其连接时,一直等待下去。
//第三步:获取对应的输入流通道
InputStream inputStream=socket.getInputStream();
//第四步:通过输入流通道读取数据
byte[] buf=new byte[1024];
int length=inputStream.read(buf);
System.out.println("服务端接收到的数据:"+new String(buf,0,length));
//服务端给客户端回送数据
//获取socket的输出流
OutputStream output=socket.getOutputStream();
output.write("客户端辛苦啦。。".getBytes());
//第五步:关闭资源
serverSocket.close();
}
}
练习:
/*
一个服务端可以与多个客户端连接。
需求:编写一个服务端可以与多个客户端进行连接,客户端一旦连接成功,马上输送一张图片数据给客户端。
再统计接收到的客户端数,同一个客户端只算一次。
服务端需要多线程
*/
//多线程服务端 提供图片
public class demo3 extends Thread{
Socket socket;
static HashSet ips=new HashSet();
public demo3(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
try {
//第一步:建立tcp服务端的服务
//ServerSocket serverSocket=new ServerSocket(9090);
//第二步:接收用户的请求连接
//Socket socket=serverSocket.accept();
//第三步:获取Socket输出字节流(服务端输出图片)
OutputStream socketOut=socket.getOutputStream();
//第四步:获取文件的输入流,读物文件的数据,把文件数据写出给客户端
FileInputStream fileInputStream=new FileInputStream("E:\\1.jpg");
byte[] buf=new byte[1024];
int length=0;
while((length=fileInputStream.read(buf))!=-1) {
socketOut.write(buf,0,length);
}
String ip=socket.getInetAddress().getHostAddress();//获取到对方(接收方)的ip地址
if(ips.add(ip)) {//如果可以存储到集合中,意味着这是一个新的ip地址
System.out.println("恭喜"+ip+"下载成功、、");
System.out.println("当前下载的人数:"+ips.size());
}
//第五步:关闭资源
fileInputStream.close();
socket.close();//每个socket都会占用端口,用完需关闭
}catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//第一步:建立tcp服务端的服务,只需要一个服务端对象
ServerSocket serverSocket=new ServerSocket(9090);
while(true) {
Socket socket=serverSocket.accept();//不断地接收用户的连接请求
new demo3(socket).start();//如果产生了一个Socket就意味着有一个客户与服务端连接,马上开启一个线程为其服务
}
}
}
//客户端接收图片
public class demo4 {
public static void main(String[] args) throws IOException {
//建立tcp的客户端服务
Socket socket=new Socket(InetAddress.getLocalHost(),9090);//客户端写的是本机
//获取socket的输入流对象
InputStream inputStream=socket.getInputStream();
//建立一个文件的输出字节流对象
FileOutputStream fileOutputStream=new FileOutputStream("E:\\小猫.jpg");
//边读边写
byte[] buf=new byte[1024];
int length=0;
while((length=inputStream.read(buf))!=-1) {
fileOutputStream.write(buf,0,length);
}
//关闭资源
fileOutputStream.close();
socket.close();
}
}