黑马程序员----java网络编程

本文详细介绍了网络通信的基础模型,包括OSI七层参考模型和TCP/IP模型,重点阐述了UDP和TCP两种协议的区别及其实现方式。通过示例代码展示了如何使用Java实现UDP和TCP的网络通信,包括Socket的创建、数据的发送与接收,以及如何利用BufferedReader和OutputStream进行数据传输。同时,文章还讨论了图片上传和浏览器作为客户端的案例,深入浅出地讲解了不同场景下网络通信的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.网络模型

OSI七层参考模型

物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。

TCP/IP模型

网络接口层,网络层,传输层,应用层

数据通信主要用到ip地址和端口号组成的套接字,网络通信的实质是Socket之间的通信。

2.UDPSocket通信建立

Socket标示具有唯一性,网络通信根据ip地址找到特定的主机,再根据端口号找到相关程序的进程或线程。

DatagramSocket的几个常用构造方法:

DatagramSocket()    构造数据报套接字并将其绑定到本地主机上任何可用的端口。该端口号是随机绑定的。

DatagramSocket(int port)    创建数据报套接字并将其绑定到本地主机上的指定端口。

DatagramSocket(int port,InetAddress laddr)  创建数据报套接字,将其绑定到指定的本地地址。

DatagramSocket(SocketAddress bindaddr)  创建数据报套接字,将其绑定到指定的本地套接字地址。通过SocketAddress的子类InetSocketAddress(InetAddress addr, int port) 创建实例参数。

DatagramSocket()的send和receive方法是阻塞方法,如果监听端口没有数据,则receive处于等待状态,如果接收到数据,则唤醒。

DatagramPacket的构造方法

分为接收数据的数据报和发送数据的数据报两类构造方法。

DatagramPacket(byte[] buf, int length)  构造 DatagramPacket,用来接收长度为length 的数据包。

DatagramPacket(byte[] buf, int length, InetAddress address, int port)  构造数据报包,用来将长度为length 的包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int offset, int length) 构造 DatagramPacket,用来接收长度为length 的包,在缓冲区中指定了偏移量。

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造数据报包,用来将长度为length 偏移量为offset 的包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造数据报包,用来将长度为length 偏移量为offset 的包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为length 的包发送到指定主机上的指定端口号。

发送数据的数据报必须包含套接字(ip+端口号)相关信息。

3.利用两个线程模拟UDP通信时,创建Rend和Receive类,需要通过构造方法传递DatagramSocket参数,在run方法中实现需要的业务。.BufferedReader类:从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。通过控制台输入(System.in)将数据放入BufferedReader缓冲区中,然后通过行读取方法,将数据封装到数据包中,然后通过Socket的Send方法将数据发送到指定主机的对应的端口号。

例如:

class UdpSend {

 public static void main(String[] args) throws IOException {
  //创建UDP服务,通过DatagramSocket对象
  DatagramSocket ds=new DatagramSocket(new InetSocketAddress(InetAddress.getByName("192.168.1.100"), 10003));
  //创建数据包,通过DatagramPacket对象
  //byte[] data ="hello world".getBytes();
  BufferedReader bf =new BufferedReader(new InputStreamReader(System.in));
  String line=null;
  while((line=bf.readLine())!=null){
   if("886".equals(line))
    break;
   byte[] buf=line.getBytes();
   DatagramPacket dp=new DatagramPacket(buf, 0,buf.length,InetAddress.getByName("192.168.1.100"),10000 );
   //通过Socket方法发送
   ds.send(dp);  
  } 
  //释放资源
  ds.close(); 
 }
}
/*
 * 需求:创建一个应用程序,用于接收发送端传输的数据
 *步骤:
 *1.定义udpSocket服务
 *2.创建DatagramPacket包,用UDPSocket的recevie方法将数据封装到DatagramPacket包中
 *3.利用封装对象的特有方法,取出相关的数据。并打印在控制台上。
 *4.关闭资源
 */
class UDPRecive{
 public static void main(String[] args) throws IOException{
  //创建UDPSocket服务
  DatagramSocket ds=new DatagramSocket(10000);
  while(true){ 
  //定义接收的数据包
  byte[] buf=new byte[1024];
  DatagramPacket dp= new DatagramPacket(buf,0, buf.length);
  //通过服务的Receive方法存放到数据包中
  ds.receive(dp);
  String ip=dp.getAddress().getHostAddress();
  byte[] data=dp.getData();
  String dataString =new String(data,0,dp.getLength());
  int port = dp.getPort();
  System.out.println(ip+":"+dataString+"--"+port);
  }
 }
 
}

 

4.TCP传输

客户端(Socket)和服务端(ServerSocket),建立连接后通过Socket的IO流进行数据传输,关闭Socket。客户端和服务端是两个独立的应用程序。

5.Socket类

Socket通路一建立,流就已经存在了,这是Socket内部封装好的。

Socket类的构造方法

Socket()通过系统默认类型的SocketImpl 创建未连接套接字。通过connect(SocketAddress endpoint, int timeout) 方法将此套接字连接到具有指定超时值的服务器上,并通过实现SocketAddress的子类InetSocketAddress方法,建立Socket封装。

Socket(String host, int port)  创建一个流套接字并将其连接到指定主机上的指定端口号。

ServerSocket(int port) 创建绑定到特定端口的服务器套接字。

ServerSocket通过accept方法得到客户端的Socket对象,获得他的流对象,然后再和客户端进行通信。服务器端只有开通监听端口号,客户端才可以成功发送,否则客户端发送会报ConnectException。当客户端通过InputStream的read方法读取服务端的输出流时,因为read方法是阻塞式方法,没有读到数据,会一直等待。所以程序会一直等待有数据读入完成后,才会执行后面的语句。

当客户端和服务端出现莫名的等待是,需要查看阻塞式方法,这些方法有没有读到结束标记。带有缓冲区的流对象,需要通过刷新,才能将数据装入到流中。

例如:

class TCPClient{
public static void main(String[] args) throws Exception{
  Socket sk =new Socket();
  //通过connect方法连接
  sk.connect(new InetSocketAddress(InetAddress.getByName("192.168.1.100"),10009));
  //为了发送数据需要获得Socket流的输出流
  OutputStream out =sk.getOutputStream();
  //数据会写入到输出流中去,并随着网络发送到服务端
  out.write("tcp come in".getBytes());
  sk.close();
 }
}
/*
需求:定义端点接受数据并打印在控制台上
1.通过ServerSocket建立服务端Socket
2.获得客户端的对象。通过accept方法实现,这个方法是阻塞式的,没有连接就会等。
3,客户端如果发送过来数据,服务端需要用对于的客户端对象,用客户端对象会的输入流对象,并打印到控制台
4.关闭服务(可选操作)
*/
class TCPServe{
public static void main(String[] args) throws Exception{
  ServerSocket ss =new ServerSocket(10009);
  while(true){
  Socket sk =ss.accept();
  String ip =sk.getInetAddress().getHostAddress();
  System.out.println(ip+"connect");
  InputStream ips =sk.getInputStream();
  byte[] buf=new byte[1024];
  int len =ips.read(buf);
  System.out.println(new String(buf,0,len));
  sk.close();
  
 }
}

6.TCP上传图片

shutdownOutput()  通过此方法告诉套接字的输出流已经使用完毕,加上使用标记。处理流时如果没有加上结束标记,会一直循环,不会结束。多用户上传图片时,需要将客户端封装到线程当中,线程会随机的获取cpu的执行权限,从而实现多用户上传。

class TCPClient{
public static void main(String[] args) throws Exception{
  Socket sk =new Socket();
  //通过connect方法连接
  sk.connect(new InetSocketAddress(InetAddress.getByName("192.168.1.100"),10009));
  //为了发送数据需要获得Socket流的输出流
  FileInputStream fis=new FileInputStream("1.gif");
  OutputStream ops=sk.getOutputStream();
  byte[] buf=new byte[1024];
  int len;
  while((len=fis.read(buf))!=-1){
   ops.write(buf, 0, len);
  }
  sk.shutdownOutput();
  InputStream ips=sk.getInputStream();
  byte[] bufr =new byte[1024];
  ips.read(bufr);
  String ref=new String(bufr,0,bufr.length);
  System.out.println(ref);
  fis.close();
  sk.close();
 }
}
/*
需求:定义端点接受数据并打印在控制台上
1.通过ServerSocket建立服务端Socket
2.获得客户端的对象。通过accept方法实现,这个方法是阻塞式的,没有连接就会等。
3,客户端如果发送过来数据,服务端需要用对于的客户端对象,用客户端对象会的输入流对象,并打印到控制台
4.关闭服务(可选操作)
*/
class TCPServe{
public static void main(String[] args) throws Exception{
  ServerSocket ss =new ServerSocket(10009);
  while(true){
  Socket sk =ss.accept();
  String ip =sk.getInetAddress().getHostAddress();
  FileOutputStream fos=new FileOutputStream("2.bmp");
  InputStream ips =sk.getInputStream();
  byte[] buf=new byte[1024];
  int len;
  while((len=ips.read(buf))!=-1){
   fos.write(buf, 0, len);
  }
  OutputStream out=sk.getOutputStream();
  out.write("上传成功".getBytes());
  sk.close();
  }
 }
}

7.浏览器作为客户端

class  ServerDemo
{
        public static void main(String[] args) throws Exception
        {
                ServerSocket ss = new ServerSocket(11000);
    while(true){
                Socket s =ss.accept();
                String ip = s.getInetAddress().getHostAddress();
                System.out.println(ip+">>>已连接");
                PrintWriter out = new PrintWriter(s.getOutputStream(),true);
                out.println("<html><body>自定义浏览器</body></html>");
    s.close();
    }
          //ss.close();
        }
}

在浏览器地址栏输入:http://localhost:11000/

 

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值