Java基础24-网络编程TCP、UDP

    科技2026-08-09  3

    网络编程TCP、UDP

    Java网络编程一、Socket编程1、ServerSocket 类的方法2、Socket 类的方法3、InetAddress 类的方法4、基于TCP协议的Socket通信 客户端、服务端连接实例01、客户端与服务器端互发消息102、客户端与服务器端互发消息203、多用户上线,在客户端输入04、能群发的聊天服务器Tcpserver与Tcpclient 5、基于UDP协议的Socket通信 客户端、服务端连接实例6、同步、异步、阻塞、非阻塞理解

    Java网络编程

    网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。

    java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

    java.net 包中提供了两种常见的网络协议的支持: TCP:TCP是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。

    UDP:UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。

    Tcp区分服务器客户端,是可靠的 IP区分计算机 端口区分应用 ,端口最大值65535 udp不可靠,无服务器与客户端之分

    一、Socket编程

    套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。 当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

    java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

    以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:

    1、服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

    2、服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

    3、服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

    4、Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket对象能够与服务器进行通信。

    5、在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

    连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。 TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。

    1、ServerSocket 类的方法

    服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。

    ServerSocket 类有四个构造方法: 1、 public ServerSocket(int port) throws IOException 创建绑定到特定端口的服务器套接字。 2、public ServerSocket(int port, int backlog) throws IOException 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 3、public ServerSocket(int port, int backlog, InetAddress address) throws IOException 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 4、public ServerSocket() throws IOException 创建非绑定服务器套接字。

    创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。

    ServerSocket 类的常用方法: 1、public int getLocalPort() 返回此套接字在其上侦听的端口。 2、public Socket accept() throws IOException 侦听并接受到此套接字的连接。 3、public void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 4、 public void bind(SocketAddress host, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

    2、Socket 类的方法

    java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而服务器获得一个Socket 对象则通过 accept() 方法的返回值`。

    Socket 类有五个构造方法. 1、public Socket(String host, int port) throws UnknownHostException, IOException. 创建一个流套接字并将其连接到指定主机上的指定端口号。 2、public Socket(InetAddress host, int port) throws IOException 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 3、public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 4、public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 5、public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字

    当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。

    下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。

    3、InetAddress 类的方法

    这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法:

    4、基于TCP协议的Socket通信 客户端、服务端连接实例

    基于TCP协议的Socket通信

    服务端要做的事有这些: Step 1:创建ServerSocket对象,绑定监听的端口 Step 2:调用accept()方法监听客户端的请求 Step 3:连接建立后,通过输入流读取客户端发送的请求信息 Step 4:通过输出流向客户端发送响应信息 Step 5:关闭相关资源 服务端主要连接代码: ServerSocket server=new ServerSocket(8899); //实例化一个 ServerSocket 对象,表示通过服务器上的端口通信 Socket client=server.accept(); //调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口

    客户端要做的事有这些: Step 1:创建Socket对象,指明需要链接的服务器的地址和端号 Step 2:链接建立后,通过输出流向服务器发送请求信息 Step 3:通过输出流获取服务器响应的信息 Step 4:关闭相关资源 客户端主要连接代码: Socket client=new Socket("127.0.0.1",8899); //服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接 System.out.println("远程主机地址:" + client.getRemoteSocketAddress()); //通过 socket 连接到服务器

    01、客户端与服务器端互发消息1

    客户端GreetingClient:

    package day12.net; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class GreetingClient { public static void main(String[] args) { try { Socket client=new Socket("127.0.0.1",8899);//服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接 System.out.println("远程主机地址:" + client.getRemoteSocketAddress());//通过 socket 连接到服务器 //写消息 发送一个请求 OutputStream out=client.getOutputStream(); Scanner scan = new Scanner(System.in); while(true) { String n=scan.next(); out.write(n.getBytes()); if("exit".equals(n)) { break; } } out.flush(); out.close(); scan.close(); //读消息 获取响应 byte buf[] = new byte[1000]; InputStream input=client.getInputStream(); while(true) { int len = input.read(buf); String info = new String(buf, 0, len); System.out.println("接收到=" + info); if("exit".equals(info)) { break; } } input.close(); client.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }

    服务端GreetingServer:

    package day12.net; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class GreetingServer { public static void main(String[] args) { // TODO Auto-generated method stub try { ServerSocket server=new ServerSocket(8899);//实例化一个 ServerSocket 对象,表示通过服务器上的端口通信 Socket client=server.accept();//调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口 //读消息 byte buf[] = new byte[1000]; InputStream input=client.getInputStream(); while(true) { int len = input.read(buf); String info = new String(buf, 0, len); System.out.println("接收到=" + info); if("exit".equals(info)) { break; } } input.close(); //写消息 发送一个请求 OutputStream out=client.getOutputStream(); Scanner scan = new Scanner(System.in); while(true) { String n=scan.next(); out.write(n.getBytes()); if("exit".equals(n)) { break; } } out.flush(); out.close(); scan.close(); client.close(); } catch (Exception e) { // TODO: handle exception } } }
    02、客户端与服务器端互发消息2

    写消息进程类:Ear

    class Ear extends Thread { Socket s; String name; public Ear(Socket s, String name) { this.s = s; this.name = name; this.start(); } @Override public void run() { try { byte buf[] = new byte[1000]; InputStream input = s.getInputStream();//写消息 while (true) { int len = input.read(buf); String info = new String(buf, 0, len); System.out.println(name + "接收到=" + info); if ("exit".equals(info)) { break; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    **客户端:TcpClient2 **

    package day11.work1; //客户端与服务器端互发消息 import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TcpClient2 { public static void main(String[] args) { // TODO Auto-generated method stub byte buf[] = new byte[1000]; // 服务器 端口有65535 try { // 连接服务器 127.0.0.1自己的IP Socket s = new Socket("127.0.0.1", 8899); OutputStream out = s.getOutputStream();// 发消息 Scanner scan = new Scanner(System.in); Ear ear = new Ear(s, "客户端");//读消息 while (true) { String ss = scan.nextLine(); out.write(ss.getBytes()); if ("exit".equals(ss)) { break; } } out.flush(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    服务端:Tcpserver2

    //客户端与服务器端互发消息 import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; //双向连接 public class Tcpserver2 { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(8899); Socket s = server.accept();// 服务器等待用户连接 System.out.println("一个用户已连接"); OutputStream out = s.getOutputStream();// 发消息 Scanner scan = new Scanner(System.in); Ear ear = new Ear(s, "服务器");//读消息 while (true) { String ss = scan.nextLine(); out.write(ss.getBytes()); if ("exit".equals(ss)) { break; } } out.flush(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
    03、多用户上线,在客户端输入

    写消息进程类:Ear

    class Ear extends Thread { Socket s; String name; public Ear(Socket s, String name) { this.s = s; this.name = name; this.start(); } @Override public void run() { try { byte buf[] = new byte[1000]; InputStream input = s.getInputStream();//写消息 while (true) { int len = input.read(buf); String info = new String(buf, 0, len); System.out.println(name + "接收到=" + info); if ("exit".equals(info)) { break; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    服务端MultiServer :

    import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class MultiServer { public static void main(String[] args) { int n=1; try { ServerSocket server = new ServerSocket(8899); System.out.println("服务器开启在8899"); while(true) { Socket s = server.accept(); System.out.println("用户"+n+"上线。。。"); Ear ear = new Ear(s,"用户"+n++);//写消息 } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    客户端:TcpClient2

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TcpClient2 { public static void main(String[] args) { // TODO Auto-generated method stub byte buf[] = new byte[1000]; // 服务器 端口有65535 try { // 连接服务器 127.0.0.1自己的IP Socket s = new Socket("127.0.0.1", 8899); OutputStream out = s.getOutputStream();// 读消息 Scanner scan = new Scanner(System.in); Ear ear = new Ear(s, "客户端");//写消息 while (true) { String ss = scan.nextLine(); out.write(ss.getBytes()); if ("exit".equals(ss)) { break; } } out.flush(); out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
    04、能群发的聊天服务器Tcpserver与Tcpclient

    User类 用来执行读写消息方法:

    class User extends Thread { Socket s; String name; public User(Socket s, String name) { this.s = s; this.name = name; this.start(); } @Override public void run() { try { byte buf[] = new byte[1000]; InputStream input = s.getInputStream();//读消息 while (true) { int len = input.read(buf); String info = new String(buf, 0, len); System.out.println(name + "接收到=" + info); //给其他人没人发一个 TcpServer.sending(info); if ("exit".equals(info)) { break; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //写消息 public void send(String info) { OutputStream out; try { out = s.getOutputStream(); out.write(info.getBytes()); out.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    服务器TcpServer :

    public class TcpServer { static List<User> users=new ArrayList<User>(); int n=1; public void open() throws IOException { ServerSocket server=new ServerSocket(8899); while(true) { Socket s=server.accept();//一个用户上线 User user=new User(s,"用户"+n); System.out.println("用户"+n+"上线"); n++; users.add(user); } } public static void sending(String info) { try { for(User user:users) { user.send(info); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { TcpServer ser=new TcpServer(); try { ser.open(); } catch (Exception e) { e.printStackTrace(); } } }

    客户端Tcpclient:

    public class TcpClient { public static void main(String[] args) { byte buf[] = new byte[1000]; // 连接服务器127.0.0.1代表自己的IP try { Socket s = new Socket("127.0.0.1", 8899); User user=new User(s,"客户端");//读写方法 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

    5、基于UDP协议的Socket通信 客户端、服务端连接实例

    UDP以数据报作为数据的传输载体,在进行传输时 首先要把传输的数据定义成数据报(Datagram),在数据报中指明数据要到达的Socket(主机地址 和端口号),然后再将数据以数据报的形式发送出去

    基于UDP协议的Socket通信

    服务端实现步骤: Step 1:创建DatagramSocket,指定端口号 Step 2:创建DatagramPacket Step 3:接收客户端发送的数据信息 Step 4:读取数据 客户端实现步骤: Step 1:定义发送信息 Step 2:创建DatagramPacket,包含将要发送的信息 Step 3:创建DatagramSocket Step 4:发送数据

    客户端 UdpA :

    package day12.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UdpA { public static void main(String[] args) throws Exception { /* * 向服务器端发送数据 */ // 1.定义服务器的地址、端口号、数据 InetAddress ip = InetAddress.getLocalHost(); // InetAddress ip = InetAddress.getByName("192.16.12.20"); String info = "你好中国"; // int len = info.length();//只看字符串的长度上面shi4 byte s[] = info.getBytes(); int len = s.length; // 2.创建数据报,包含发送的数据信息 // byte[],长度,IP,地址(端口) DatagramPacket p = new DatagramPacket(info.getBytes(), len, ip, 9090); // 3.创建DatagramSocket对象 DatagramSocket socket = new DatagramSocket(); // 4.向服务器端发送数据报 socket.send(p); /* * 接收服务器端响应的数据 */ // 1.创建数据报,用于接收服务器端响应的数据 byte[] data2 = new byte[1024]; DatagramPacket packet2 = new DatagramPacket(data2, data2.length); // 2.接收服务器响应的数据 socket.receive(packet2); // 3.读取数据 String reply = new String(data2, 0, packet2.getLength()); System.out.println("我是客户端,服务器说:" + reply); // 4.关闭资源 socket.close(); } }

    服务端 UdpB :

    package day12.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; public class UdpB {//UDP收先开,TCP服务器先开 /* * 接收客户端发送的数据 */ public static void main(String[] args) throws Exception { //用户B 1.创建服务器端DatagramSocket,指定端口 DatagramSocket socket = new DatagramSocket(9090); // 空包裹 2.创建数据报,用于接收客户端发送的数据 byte buf[]=new byte[1000]; DatagramPacket bao=new DatagramPacket(buf, buf.length); //收包裹 3.接收客户端发送的数据 socket.receive(bao);//收消息卡程序,不管UDP还是TCP //拆包 4.读取数据 String info=new String(buf,0,bao.getLength()); System.out.println("收到---》"+info); /* * 向客户端响应数据 */ // 1.定义客户端的地址、端口号、数据 InetAddress address = bao.getAddress(); int port = bao.getPort(); byte[] data2 = "欢迎您!".getBytes(); // 2.创建数据报,包含响应的数据信息 DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port); // 3.响应客户端 socket.send(packet2); // 4.关闭资源 socket.close(); } }

    6、同步、异步、阻塞、非阻塞理解

    01、同步和异步:

    同步和异步是针对应用程序和内核的交互而言的, 同步指的是用户进程触发IO 操作并等待或者轮询的去查看IO操作是否就绪, 而异步是指用户进程触发IO 操作以后便开始做自己的事情,而当IO 操作已经完成的时候会得到IO 完成的通知。

    以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步 IO 时,Java 自己处理IO 读写); 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO 时,Java 将 IO 读写委托给OS 处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS 需要支持异步IO操作API);

    02、阻塞和非阻塞:

    阻塞和非阻塞:阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待, 而非阻塞方式下,读取或者写入方法会立即返回一个状态值。

    以银行取款为例: 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回); 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器通知可读写时再继续进行读写,不断循环直到读写完成)

    Processed: 0.009, SQL: 9