Java学习日记(十一)网络编程

本文深入探讨了网络编程的基础概念,包括OSI和TCP/IP参考模型、网络通讯要素(IP地址、端口号、传输协议)、以及TCP和UDP的区别。详细介绍了Socket的使用方法,包括UDP和TCP的发送与接收实现,以及如何通过键盘输入进行数据交换。此外,还通过实例展示了如何实现简单的文本转换、上传文本和图片,以及多用户并发上传图片的功能。

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

1.网络编程

网络模型:

       OSI参考模型

       TCP/IP参考模型

网络通讯要素

       IP地址

       端口号

       传输协议

 

2.网络参考模型

 

3.网络通讯要素:

IP地址:InetAddress

       网络中设备的标识

       不易记忆,可用主机名

       本地回环地址:127.0.0.1    主机名:localhost

示例:

classIPDemo {
       public  static void main(String[] args) throwsException {
              // 建立对象i返回本机地址
              InetAddress  i = InetAddress.getLocalHost();
              // 打印本机地址信息
              System.out.println(i.toString());
              // 打印本机名和本机Ip
              System.out.println(i.getHostName()+ ":" + i.getHostAddress());
              // 建立对象,根据主机名获取对象信息再传递给对象
              InetAddress  ia = InetAddress.getByName("PC-Administrator");
              // 获取主机地址
              System.out.println("address:"+ ia.getHostAddress());
              // 获取主机名
              System.out.println("name:"+ ia.getHostName());
       }
}

运行结果:

PC-Administrator/192.168.1.101

PC-Administrator:192.168.1.101

address:192.168.1.101

name:PC-Administrator

端口号:

       用于标识进程的逻辑地址,不同进程的标识

       有效端口:0-65535.其中0-1024系统使用或保留端口

传输协议:

       通讯的规则:

       常见协议:TCP、UDP

 

4.TCP和UDP

TCP:

       将数据及源和目的封装在数据包中,不需要建立连接

       每个数据报的大小限制在64k内

       因无连接,是不可靠协议

       不需要建立连接,速度快

UDP:

       建立连接,形成传输数据的通道

       在连接中进行大数据量传输

       通过三次握手完成连接,是可靠协议

       必须建立连接,效率会稍低

 

5.Socket

Socket就是为网络服务提供的一种机制

通信的两端都有Socket

网络通信其实就是Socket间的通信。

数据在两个Socket间通过IO传输

 

6.UDP传输

DatagramSocket与DataprogramPacket

·建立发送端、接收端

·建立数据包

·调用Socket的发送接受方法

·关闭Socket

·发送端与接收端是两个独立的运行程序

示例一:(通过UDP传输方式,将一段文字数据发送出去)

思路:(发送端)

       1.建立updsocket服务;

       2.提供数据,并将数据封装到数据包中;

       3.通过Socket服务的发送功能,将数据包发出去;

       4.关闭资源;

class UdpSend {
       public static void main(String[] args) throwsException {
              //创建udp服务。通过DatagramSocket对象。
              DatagramSocket ds = new DatagramSocket(8888);
              //确定数据,并封装成数据包。
              byte[] by = "hello world javatest.".getBytes();
              DatagramPacket dp = new  DatagramPacket(by, by.length,InetAddress.getByName("192.168.1.101"), 10000);
              //通过socket服务,将已有的数据包发送出去。通过send方法。
              ds.send(dp);
              //关闭资源
              ds.close();
       }
}

思路:(接收端)

1.      定义一个udpsocket服务,通常会监听一个端口,其实就是给这个接收网络应用程序定义数字标识,方便于明确哪些数据过来本程序可以处理;

2.      定义一个数据包,因为要存储接收到的字节数组(数据包对象中有更多功能可以提取字节数据中的不同数据信息);

3.      通过socket服务的receive方法将收到的数据存入到已经定义好的数据包中;

4.      通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。

5.      关闭资源

class UdpRece {
       public static void main(String[] args) throwsException {
              //创建udp socket,建立端点。
              DatagramSocket ds = new DatagramSocket(10000);
              while(true) {
                     //定义数据包。用于存储数据
                     byte[] by = new byte[1024];
                     DatagramPacketdp = new DatagramPacket(by, by.length);
                     //通过服务的receive方法将收到数据存入数据包中。
                     ds.receive(dp);
                     //通过数据包的方法获取其中的数据。
                     String ip = dp.getAddress().getHostAddress();
                     String data = new String(dp.getData(), 0,dp.getLength());
                     int  port = dp.getPort();
                     System.out.println(ip +"-" + port + ":" + data);
              }
       }
}

示例二:(现在将上面的数据通过键盘录入形式给出)

class UdpSend {
       public static void main(String[] args) throwsException {
              //创建udp服务,通过DatagramSocket对象
              DatagramSocket ds = new  DatagramSocket(8888);
              BufferedReader in = new  BufferedReader(newInputStreamReader(System.in));
              String line = null;
              while((line = in.readLine()) != null) {
                     if("886".equals(line))
                            break;
                     //确定数据,并封装成数据包
                     byte[] by = line.getBytes();
                     DatagramPacket dp = new  DatagramPacket(by, by.length, InetAddress.getByName("192.168.1.101"),1000);
                     //通过socket服务,将已有的数据包发送出去                    
                     ds.send(dp);
              }
              //关闭资源
              ds.close();
       }
}
class UdpRece {
       public static void main(String[] args) throwsException {
              //创建udp socket,建立端点。
              DatagramSocket ds = new  DatagramSocket(10000);
              while(true){
                     //定义数据包,用于存储数据
                     byte[] by=new  byte[1024];
                     DatagramPacket dp=new  DatagramPacket(by,by.length);
                     //通过服务的receive方法将收到数据存入数据包中
                     ds.receive(dp);
                     String ip=dp.getAddress().getHostAddress();
                     //通过数据包的方法获取其中的数据
                     int  port=dp.getPort();
                     String data=newString(dp.getData(),0,dp.getLength());
                     System.out.println(ip+ ":" + port + "-" + data);
              }
       }
}

 

7.UDP聊天程序

通过键盘录入获取要发送的信息

将发送端和接收端分别装到两个线程中

//发送端
class Send implements Runnable {
       private DatagramSocket  ds;
       public Send(DatagramSocket  ds) {
              this.ds= ds;
       }
       public void run() {
              try{
                     BufferedReader in = new  BufferedReader(new  InputStreamReader(System.in));
                     String line = null;
                     while((line = in.readLine()) != null) {
                            byte[] by = line.getBytes();
                            DatagramPacket dp = new  DatagramPacket(by, by.length,InetAddress.getByName("192.168.1.255"), 10000);
                            ds.send(dp);
                            if("886".equals(line))
                                   break;
                     }
              }catch (Exception  e) {
                     System.out.println(e.toString());
              }
       }
}
//接收端
class Rece implements Runnable {
       private DatagramSocket  ds;
       public Rece(DatagramSocket  ds) {
              this.ds= ds;
       }
       public void run() {
              try{
                     while(true) {
                            byte[] by = new byte[1024];
                            DatagramPacket dp = new  DatagramPacket(by, by.length);
                            ds.receive(dp);
                            String ip = dp.getAddress().getHostAddress();
                            String data = new  String(dp.getData(), 0, dp.getLength());
                            if("886".equals(data)) {
                                   System.out.println(ip+ ":" + "...离开聊天室");
                                   break;
                            }
                            System.out.println(ip+ ":" + data);
                     }
              }catch (Exception  e) {
                     System.out.println(e.toString());
              }
       }
}
class ChatTest {
       public static void main(String[]  args) throws Exception {
              DatagramSocket sendSocket = new  DatagramSocket();
              DatagramSocket receSocket = new  DatagramSocket(1000);
              newThread(new  Send(sendSocket)).start();
              newThread(new  Rece(receSocket)).start();
       }
}

 

8.TCP传输

Socket和ServerSocket

建立客户端和服务器端

建立连接后,通过Socket中的io流进行数据的传输

关闭socket

同样,客户端与服务端是两个独立的应用程序

示例:(TCP传输)

基本思路:

客户端:

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常;

连接成功,说明客户端与服务端建立通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream与getOutputStream获取即可。

与服务端通讯结束后,关闭Socket。

//客户端
class TcpClient {
       public static void main(String[]  args) throws Exception {
              //创建客户端的socket服务。指定目的主机和端口
              Socket s = new  Socket("192.168.1.1", 6666);
              //为了发送数据,应该获取socket流中的输出流
              OutputStream out = s.getOutputStream();
              out.write("TcpHello world Java Test".getBytes());
              s.close();
       }
}

服务端:

       服务端需要明确它要处理的数据是从哪个端口进入的;

当有客户端访问时,要明确是哪个客户端,可以通过accept获取已经连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输;

当该客户端访问结束,关闭该客户端。

//服务端
class TcpServer {
       public static void main(String[] args) throws Exception {
              //建立服务端socket服务,并监听一个端口
              ServerSocket ss = new  ServerSocket(6666);
              while(true) {
                     //通过accept方法获取连接过来的客户端对象
                     Socket s = ss.accept();
                     String ip =s.getInetAddress().getHostAddress();
                     System.out.println(ip+ "...connected");
                     //获取客户端发送过来的数据
                     InputStream in = s.getInputStream();
                     byte[] by = new  byte[1024];
                     int len = in.read(by);
                     System.out.println(newString(by, 0, len));
                     //关闭客户端
                     s.close();
              }
       }
}

练习一:(建立一个文本转换服务器:客户端给服务器发送文本,服务器将文本转成大写后返回给客户端,而且服务端可以不断地进行文本转换,当客户端输入over时,转换结束)

分析:

       客户端:

              既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考,

              源:键盘

              目的:网络设备,网络输出流

              操作对象:文本数据,选择字符流

步骤:

1.      建立连接;

2.      获取键盘录入;

3.      将数据发送给服务端;

4.      显示服务端返回的大写数据;

5.      结束,关闭资源

//客户端
class TransClient {
       public static void main(String[] args) throwsException {
              Socket s = new  Socket("192.168.1.1", 6666);
              BufferedReader fromUser = new   BufferedReader(new   InputStreamReader(System.in));
              PrintWriter out = new  PrintWriter(s.getOutputStream(), true);
              BufferedReader fromServer = new  BufferedReader(newInputStreamReader(s.getInputStream()));
              String line = null;
              while((line = fromUser.readLine()) != null) {
                     if("over".equals(line))
                            break;
                     out.println(line);
                     String str = fromServer.readLine();
                     System.out.println("server:"+ str);
              }
              fromUser.close();
              s.close();
       }
}
//服务端
class TransServer {
       public static void main(String[] args) throwsException {
              ServerSocket ss = new  ServerSocket(6666);
              Socket s = ss.accept();
              String ip =s.getInetAddress().getHostAddress();
              System.out.println(ip+ "...connected");
              BufferedReader fromClient = new  BufferedReader(new  InputStreamReader(s.getInputStream()));
              PrintWriter out = new PrintWriter(s.getOutputStream(), true);
              Stringline = null;
              while((line = fromClient.readLine()) != null) {
                     System.out.println(line);
                     out.println(line.toUpperCase());
              }
              s.close();
              ss.close();
       }
}

练习二:(上传文本)

//客户端

class TextClient {
       public static void main(String[] args) throwsException {
              Socket s = new  Socket("localhost", 6666);
              BufferedReader fromText = new  BufferedReader(new  FileReader("4.txt"));
              PrintWriter out = new  PrintWriter(s.getOutputStream(), true);
              String line = null;
              while((line = fromText.readLine()) != null) {
                     out.println(line);
              }
              //关闭客户端的输出流,相当于给流中加入一个结束标记-1
              s.shutdownOutput();
              BufferedReader fromServer = new  BufferedReader(new  InputStreamReader(s.getInputStream()));
              String str = fromServer.readLine();
              System.out.println(str);
              fromServer.close();
              s.close();
       }
}
//服务端:
class TextServer {
       public static void main(String[] args) throws Exception {
              ServerSocket ss = new  ServerSocket(6666);
              Socket s = ss.accept();
              String ip =s.getInetAddress().getHostAddress();
              System.out.println(ip+ "...connected");
              BufferedReader fromClient = new  BufferedReader(new  InputStreamReader(s.getInputStream()));
              PrintWriter out = new  PrintWriter(new  FileWriter("server.txt"), true);
              String line = null;
              while((line = fromClient.readLine()) != null) {
                     out.println(line);
              }
              PrintWriter pw = new  PrintWriter(s.getOutputStream(), true);
              pw.println("上传成功");
              out.close();
              s.close();
              ss.close();
       }
}

练习三:(上传图片)

思路:

客户端通过socket输出流将数据发给服务端,服务端读取客户端发来的图片数据,再将反馈信息送至客户端即可

//客户端
class PicClient {
       public static void main(String[] args) throwsException {
              Socket s = new  Socket("localhost", 6666);
              FileInputStream fis = new  FileInputStream("D:/Chrysanthemum.jpg");
              OutputStream out = s.getOutputStream();
              byte[] by = new  byte[1024];
              int len = 0;
              while((len = fis.read(by)) != -1) {
                     out.write(by,0, len);
              }
              //告诉服务端数据已写完
              s.shutdownOutput();
              InputStreamin = s.getInputStream();
              byte[] byin = new byte[1024];
              int num = in.read(byin);
              System.out.println(newString(byin, 0, num));
              fis.close();
              s.close();
       }
}
//服务端
class PicServer {
       public static void main(String[] args) throwsException {
              ServerSocket ss = new  ServerSocket(6666);
              Socket s = ss.accept();
              InputStream fromClient = s.getInputStream();
              FileOutputStream fos = new  FileOutputStream("server.bmp");
              byte[] by = new  byte[1024];
              int len = 0;
              while((len = fromClient.read(by)) != -1) {
                     fos.write(by,0, len);
              }
              OutputStream out = s.getOutputStream();
              fos.write("上传成功".getBytes());
              fos.close();
              s.close();
              ss.close();
       }
}

练习四:(多用户上传图片)

这个服务端有局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程,

这时B客户端连接,只有等待。因为服务端还没有处理完A客户端的请求,还要循环回来执行下次accept方法,所以暂时获取不到B客户端对象。

为了可以让多个客户端同时并发访问服务端,那么服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。

//客户端
class PicClient {
       public static void main(String[] args) throwsException {
              if(args.length != -1) {
                     System.out.println("请选择一个jpg格式的图片");
                     return;
              }
              File file = new  File(args[0]);
              if(file.exists() && file.isFile()) {
                     System.out.println("该文件有问题,要么不存在,要么不是文件");
                     return;
              }
              if(!file.getName().endsWith(".jpg")) {
                     System.out.println("图片格式错误,请重新选择");
                     return;
              }
              if(file.length() > 1024 * 1024 * 5) {
                     System.out.println("文件过大,没安好心");
                     return;
              }
              Socket s = new  Socket("localhost", 6666);
              FileInputStream fis = new  FileInputStream(file);
              OutputStream out = s.getOutputStream();
              byte[]  by = new  byte[1024];
              int len = 0;
              while((len = fis.read(by)) != -1) {
                     out.write(by,0, len);
              }
              //告诉服务端数据已写完
              s.shutdownOutput();
              InputStream in = s.getInputStream();
              byte[] byin = new  byte[1024];
              int num = in.read(byin);
              System.out.println(newString(byin, 0, num));
              fis.close();
              s.close();
       }
}
//图片处理线程
class PicThread implements Runnable {
       private Socket  s;
       PicThread(Socket s) {
              this.s= s;
       }
       public void run() {
              int count = 1;
              String ip =s.getInetAddress().getHostAddress();
              try{
                     System.out.println(ip+ "...connected");
                     InputStream in = s.getInputStream();
                     File dir = new  File("D:/Chrysanthemum.jpg");
                     File file = new  File(dir, ip + "(" + (count++) +")" + ".jpg");
                     while(file.exists())
                     file= new  File(dir, ip + "(" +(count++) + ")" + ".jpg");
                     FileOutputStream fos = new FileOutputStream(file);
                     byte[] by = new byte[1024];
                     int len = 0;
                     while((len = in.read(by)) != -1) {
                            fos.write(by,0, len);
                     }
                     OutputStream out = s.getOutputStream();
                     out.write("上传成功".getBytes());
                     fos.close();
                     s.close();
              }catch (Exception  e) {
                     throw new RuntimeException(ip + "上传失败");
              }
       }
}
//服务端
class PicServer {
       public static void mian(String[] args) throwsException {
              ServerSocket ss = new ServerSocket(6666);
              while(true) {
                     Socket s = ss.accept();
                     new Thread(new  PicThread(s)).start();
              }
       }
}

 

9.TCP传输最容易出现的问题

客户端连接上服务器,两端都在等待,没有任何数据传输

通过例子分析:

       因为read或readLine方法是阻塞式。这些方法没有读到结束标记,那么就一直等

从而导致两端都在等待。

解决办法:

       自定义结束标记

       使用shutdownInput,shutdowmOutput方法(上面的练习中已经使用到了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值