文章目录
1.UDP
UDP是面向无连接的,所以发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。而且由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,但是由于UDP的面向无连接性,不能保证数据的完整性,偶尔会丢失一两个包,因此在传输重要数据时,不建议使用UDP协议。
特点:数据被限制在64kb以内,超出这个范围就不能发送了。
2.TCP
TCP是面向连接的,在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
1.客户端向服务器端发出连接请求,等待服务器确认。
2.服务器端向客户端回送一个响应,通知客户端收到了连接请求。
3.客户端再次向服务器端发送确认信息,确认连接。
其次,TCP的客户端和服务端断开连接,需要四次挥手
InetAddress的使用
InetAddress类无构造方法
-
bash byte[] getAddress()
返回此 InetAddress 对象的原始 IP 地址。 -
bash static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。 -
bash String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。 -
bash String getHostName()
获取此 IP 地址的主机名。 -
bash static InetAddress getLocalHost()
返回本地主机。(域名/IP地址)
127.0.0.1:本机地址,主要用于测试。别名:Localhost
public class test类 {
public static void main(String[] args) throws Exception {
InetAddress ip1=InetAddress.getByName("www.baidu.com"); //如果是IP地址的话就只能解析出ip地址
System.out.println(ip1.getHostName());//www.baidu.com
System.out.println(ip1.getHostAddress());//183.232.231.174
System.out.println(ip1.getAddress());//[B@1b6d3586
System.out.println(ip1.getLocalHost());//采荼薪樗/192.168.3.229
}
}
DatagramPacket类、DatagramSocket类放在java.net包中
1.DatagramSocket. 构造方法摘要
protected DatagramSocket(
)构造数据报套接字并将其绑定到本地主机上的任何可用端口。protected DatagramSocket(int port)
构造数据报套接字并将其绑定到本地主机上的指定端口。protected DatagramSocket(int port, InetAddress laddr)
创建一个数据报套接字,绑定到指定的本地地址。
2.DatagramPacket
(1)构造方法摘要
DatagramPacket(byte[] buf, int offset, int length)
构造一个 DatagramPacket用于接收指定长度的数据报包到缓冲区中。DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
构造用于发送指定长度的数据报包到指定主机的指定端口号上。
(2)常用方法摘要
byte[] getData() 返回数据报包中的数据。
InetAddress getAddress() 返回该数据报发送或接收数据报的计算机的IP地址。
int getLength() 返回要发送的数据的长度或接收到的数据的长度。
UDP网络编程
//客户发送端
public class Inet {
public static void main(String[] args) throws IOException {
//创建发送端的接口
DatagramSocket ds=new DatagramSocket();
//创建发送端的数据包
byte[]b="服务器你好呀,我来自客户端".getBytes();
DatagramPacket dp=new DatagramPacket(b,b.length,InetAddress.getByName("localhost"),1024);
//发送数据包
ds.send(dp);
ds.close();
}
}
//服务器接收端
public class Server {
public static void main(String[] args) throws Exception{
//创建接收端接口
DatagramSocket ds =new DatagramSocket(1024);
//创建一个接收包
byte[]b=new byte[1024];
DatagramPacket dp=new DatagramPacket(b,b.length);
//接受数据包
ds.receive(dp);
//获取发送端的IP地址以及数据
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData(),0,dp.getLength()); //获取包中的数据段转化成字符串
System.out.println("from"+ip+":"+data);
}
}
public class Client {
public static void main(String[] args)throws Exception {
//创建Socket对象,连接服务器
Socket socket =new Socket("localhost",9999);
System.out.println("客户端向服务器连接成功");
//2.从客户端发送数据给服务器
Scanner sc=new Scanner(System.in);
while(true)
{
OutputStream os =socket.getOutputStream();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));//字节流转化为字符流
bw.write(sc.nextLine());
bw.newLine();
bw.flush();
//客户端接收数据
InputStream in= socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(in));
String content=br.readLine();
System.out.println("从服务器发送来的数据是:"+content);
}
}
}
class Server {
public static void main(String[] args) throws Exception{
//1.创建服务器端对象
ServerSocket ss=new ServerSocket(9999);
System.out.println("服务器正在等待连接");
//2.等待客户端连接
Socket s=ss.accept(); //先跑服务器,阻塞,再跑客户端
System.out.println("服务器已与客户端连接成功");
Scanner sc=new Scanner(System.in);
while(true)
{
InputStream in=s.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(in));
String content=br.readLine();
System.out.println("客户端传过来"+content);
//服务器发送数据
OutputStream os=s.getOutputStream();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));
bw.write(sc.nextLine());
bw.newLine();
bw.flush();
}
}
}
ByteArrayOutputStream 对byte类型数据进行写入的类 相当于一个中间缓冲层,将类写入到文件等其他outputStream。它是对字节进行操作,属于内存操作流
TCP网络编程
1.Socket类
Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
(1)构造方法摘要
public Socket(String host, int port)**
创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。
//创建发送端接口
Socket s=new Socket("127.0.0.1",8888);
(2)常用方法摘要
public InputStream getInputStream() :
返回此套接字的输入流。
如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream() :
返回此套接字的输出流。
如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
关闭生成的OutputStream也将关闭相关的Socket。
-
public void shutdownOutput()
: 任何先前写出的数据将被发送,随后终止输出流。用来提示服务器,我客户端已经发送数据结束了 -
public void close() :关闭此套接字。
一旦一个socket被关闭,它不可再使用。
关闭此socket也将关闭相关的InputStream和OutputStream 。
2.ServerSocket类
ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。
(1)构造方法摘要
public ServerSocket(int port) :
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
(2)常用方法摘要
public Socket accept() :
侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。
ServerSocket recive =new ServerSocket(8888); //创建服务器接口,指明自己的端口号
Socket socket=recive.accept(); // 阻塞式监听客户端的接入
演示案例:
客户端
public class TCPsend { //TCP要保证数据被收到所以要先开启服务器
public static void main(String[] args) throws Exception {
//创建发送端接口
Socket s=new Socket("127.0.0.1",8888);
//获取输出流,写数据
OutputStream out =s.getOutputStream();
byte[]b="你好呀".getBytes();
out.write(b);
s.close(); //释放资源
}
}
服务器端:
public class TCPreceive {
public static void main(String[] args) throws Exception{
//创建服务器接口,指明自己的端口号
ServerSocket recive =new ServerSocket(8888);
Socket socket=recive.accept(); // 阻塞式监听客户端的接入
InputStream in=socket.getInputStream(); //服务器没有IO流获取客户端对象的IO流
byte []buffer=new byte[1024]; //这样在用String方式输出会截取汉字产生乱码,最好用字节缓冲输出流
int len=0;
while((len=in.read(buffer))!=-1)
{
String str=new String(buffer,0,buffer.length);
System.out.println("收到来自可客户端ip:"+socket.getInetAddress().getHostAddress()+str);
}
}
}
注意
服务器是没有IO流的,服务器可以获取到请求的客户端对象socket
使用每个客户端socket中提供的IO流和客户端进行交互
服务器使用客户端的字节输入流读取客户端发送的数据
服务器使用客户端的字节输出流给客户端回写数据
如果是TCP中先启动客户端会报错
而如果是UDP中先启动发送方不会报错,但会正常退出。
1.服务端与客户端的双向回写数据
客户端:
package TCP;
import com.sun.org.apache.bcel.internal.ExceptionConst;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPsend { //TCP要保证数据被收到所以要先开启服务器
public static void main(String[] args) throws Exception {
//创建发送端接口
Socket s=new Socket("127.0.0.1",8888);
//获取输出流,写数据
OutputStream out =s.getOutputStream();
out.write("你好呀,我是客户端".getBytes());
//用来提示服务器客户端已经发送数据结束了,不然服务器会一直while循环不知道什么时候结束,导致阻塞
s.shutdownOutput();
InputStream in=s.getInputStream();
int len;
byte[]b= new byte[1024];
while((len=in.read(b))!=-1)
{ String str=new String(b,0,b.length);
System.out.println(str);
System.out.println(s.getInetAddress().getHostAddress()+"端口号为:"+s.getPort()+str);
}
s.close(); //释放资源
}
}
服务器端:
package TCP;
import 网络.Server;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPreceive {
public static void main(String[] args) throws Exception{
//创建服务器接口,指明自己的端口号
ServerSocket recive =new ServerSocket(8888);
Socket socket=recive.accept(); // 阻塞式监听客户端的接入
InputStream in=socket.getInputStream(); //服务器没有IO流获取客户端对象的IO流
byte []buffer=new byte[1024]; //这样在用String方式输出会截取汉字产生乱码,最好用字节缓冲输出流
int len=0;
while((len=in.read(buffer))!=-1)
{
String str=new String(buffer,0,buffer.length);
System.out.println("收到来自客户端ip:"+socket.getInetAddress().getHostAddress()+"端口号为:"+socket.getPort()+str);
}
//输出流
OutputStream out=socket.getOutputStream();
out.write("服务器已收到数据".getBytes());
socket.close();
}
}
服务器端的输出结果:
服务器已收到数据
127.0.0.1端口号为:8888服务器已收到数据
客户端的输出结果::
收到来自客户端ip:127.0.0.1端口号为:54303你好呀,我是客户端
2.从键盘输入数据的包装流案例:
客户端:
package TCP.包装流;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
//创建接收端的接口,指定接收端Ip与端口
Socket s=new Socket("127.0.0.1",4319);
//将键盘输入的标准流封装成字符流BufferReader
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//获取接口的写入流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String str=null;
while((str=br.readLine())!=null) //由于是键盘录入数据,不可能得到null所以要设置一个结束标志
{ if(str.equals("886")) //设置一个读入流的结束标志
break;
bw.write(str); //将键盘输入的数据写入到服务器
bw.newLine();
bw.flush(); //一定要加上缓存,将通道的数据全部写入
}
bw.close();
br.close();//前两个可以不关闭,因为关闭接口也就关闭了读写流接口
s.close();
}
}
服务器:
package TCP.包装流;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception{
ServerSocket socket;
//设置服务器端的接口
socket = new ServerSocket(4319);
//阻塞式接口
Socket s=socket.accept();
//将读入流也封装成字符流,就可以读入一行啦
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
byte[]b=new byte[1024];
String str=null;
while((str=br.readLine())!=null) //读入一行字符流
{
System.out.println(str);
}
s.close();
}
}
//将键盘数据写入到文件中,封装一个文件写入流
/* BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//在服务器中封装一个输出到文件的流,将键盘输入的数据写入到文件中
BufferedWriter bw= new BufferedWriter(new FileWriter("a.txt"));
byte[]b=new byte[1024];
String str=null;
while((str=br.readLine())!=null) //读入一行字符流
{
bw.write(str);
bw.newLine();
bw.flush();
}
s.close();*/
3.TCP上传一个文本文件
客户端:
package TCP.上传文件;
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
//创建接口,并指定接收端的IP和端口
Socket s=new Socket("127.0.0.1",4329);
//封装一个文本文件的读入流,表示从文本文件中读取数据
BufferedReader br=new BufferedReader(new FileReader("副本文件"));
//封装一个接口输出流,用来向服务器传输数据
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//读取文本文件的数据写入到服务器中
String str=null;
while((str=br.readLine())!=null)
{
bw.write(str);
bw.newLine();
bw.flush();
}
//同样的要加上一个输出流的关闭提示,不然服务器就不知道你什么时候数据写完,就会一直阻塞
s.shutdownOutput();
br.close();//关闭文件即可,不必关闭流包装和接口,否则导致后面无法使用接口流
//这里封装一个接口的读入流,是用来读取服务器那边的反馈信息
BufferedReader br2=new BufferedReader(new InputStreamReader(s.getInputStream()));
String ret=br2.readLine();
System.out.println(ret);
}
}
服务器端:
package TCP.上传文件;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception{
//创建接收接口并指定接收端口
ServerSocket socket=new ServerSocket(4329);
//阻塞并返回发送端的接口
Socket s=socket.accept();
//封装输入流
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//封装文本写入流
BufferedWriter bw =new BufferedWriter(new FileWriter("上传的文件"));
//将读取到的数据写入文件
String str=null;
while((str=br.readLine())!=null)
{
bw.write(str);
bw.newLine();
bw.flush();
}
bw.close();
//给客户端一个反馈,封装一个写入流
BufferedWriter bw2=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bw2.write("文件已成功上传".toString());
bw2.flush();
bw2.close();
}
}