赞
踩
网络编程套接字,是操作系统给应用程序提供的一组 API (叫做 socket API)。
这里的 socket 可以认为是应用层 和 传输层之间的通信桥梁。
传输层的核心协议有两种,一种是 TCP, 另一种是 UDP,socket API也对应有两组。由于UDP 和 TCP 协议差异很大,所以这两组 API 的差异也大。
两种套接字的直接区别:
TCP —— 有链接,可靠传输,面向字节流,全双工。
UDP —— 无连接,不可靠传输,面向字节报,全双工。
有链接 和 无连接
有链接 —— 就像打电话,我们需要先接通,然后,我们才能进行通话。
无连接 —— 就像我们微信发消息,不需要先接通,可以直接发送过去。
可靠传输 和 不可靠传输
可靠传输 —— 知道自己发的信息,对方有没有收到,就像打电话,我们知道对方有没有收到信息。
不可靠传输 —— 不知道自己发的信息,对方有没有接收到,就像是发消息,虽然我们的消息发出去了,但是我们并不知道对方有没有接收到
面向字节流 和 面向字节报
面向字节流 —— 一个字节一个字节的传输数据。
面向数据报 —— 以数据报为单位传输(其中的数据报长度由我们自己定义)。
全双工 和 半双工
全双工 —— 一条链路双向通信。
半双工 —— 一条链路单向通信。
下面我们主要介绍 UDP 和 TCP 这两个协议。
UDP 有两个核心的类,一个是 DatagramSocket ,另一个是 DatagramPacket 。
DatagramSocket
这是 UDP 版本的 Socket 对象,代表着操作系统中的一个 socket 文件,是网卡硬件设备抽象体现。
其中有几个方法:receive()接收数据,send()发送数据,close()释放资源。
DatagramPacket
表示一个 UDP 数据报(用来封装数据)。
每次发送/接收数据,都是在传输一个 DatagramPacket 对象。
举例:实现一个最简单的客户端服务器程序,回显服务(请求是什么,响应就是什么)。
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; //源 IP:本机 IP //源端口:服务器绑定的端口 //目的 IP:客户端的 IP //目的端口:客户端的端口号 //协议类型:UDP public class UdpEchoServer { //网络编程的基础要有一个socket对象。 private DatagramSocket socket = null; //在初始化的时候,要给服务器定义一个端口号,我们只有知道了这个端口号,才能从客户端发来请求。 public UdpEchoServer(int port) throws SocketException { //创建一个带有端口号的socket对象。 socket = new DatagramSocket(port); } public void start() throws IOException { System.out.println("启动服务器!!!"); while(true){ //因为UDP接收的是一个数据报,所以我们要先定义一个空的数据报,来接收数据。 DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024); //其中的requestPacket是一个输出型参数,这个参数可以带出来数据,这个数据就是客户端传来的数据 socket.receive(requestPacket); //先把数据报转换成字符串,为了方便处理 String request = new String(requestPacket.getData(), 0,requestPacket.getLength(), "UTF-8"); //使用process函数来实现服务器根据请求,来生成响应。 String response = process(request); //因为UDP传输的是一个数据报,所以要生成一个要传输出去的数据报,其中包含了响应的IP和地端口号。 DatagramPacket responsePacket = new DatagramPacket(request.getBytes(), request.getBytes().length, requestPacket.getSocketAddress()); //响应客户端的请求。 socket.send(responsePacket); System.out.printf("[%s,%d], request:%s, response:%s\n", requestPacket.getAddress(), requestPacket.getPort(),request,response); } } //这里我们实现的是一个回显效果,就是接收什么,响应什么。 private String process(String request) { return request; } public static void main(String[] args) throws IOException { UdpEchoServer server = new UdpEchoServer(9090); server.start(); } }
import java.io.IOException; import java.net.*; import java.util.Scanner; //源 IP:本机 IP //源端口:系统分配的端口 //目的 IP:服务器的 IP //目的端口:服务器端口号 //协议类型:UDP public class UdpEchoClient { //网络编程的基础要有一个socket对象。 private DatagramSocket socket = null; private String serverIP; private int serverPort; //使用构造函数来初始化socket对象,服务器的IP和端口号 public UdpEchoClient() throws SocketException { socket = new DatagramSocket(); serverIP = "127.0.0.1"; serverPort = 9090; } public void start() throws IOException { Scanner sc = new Scanner(System.in); while(true){ System.out.print("-> "); //输入要传给服务器的内容。 String request = sc.nextLine(); //要传给服务器的内容要封装成一个数据报(DatagramPacket),数据报中要有目标地址的IP和端口号 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIP), serverPort); //传送数据报 socket.send(requestPacket); //接收要用数据报来接收,先构造出数据报 DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024); //接收服务器传来的数据报 socket.receive(responsePacket); //把数据报中的内容变成字符串 String response = new String(responsePacket.getData(), 0, responsePacket.getLength(), "UTF-8"); System.out.printf("request: %s, response: %s\n", request, response); } } public static void main(String[] args) throws IOException { UdpEchoClient client = new UdpEchoClient(); client.start(); } }
TCP API 中,也是涉及到两个核心的类 ServerSocket 类和 Socket 类
1) ServerSocket(专门给TCP服务器用的)使用 accept 方法和客户端建立连接。
2)Socket 既需要给服务器用,也需要给客户端用,TCP协议是面向字节流的,所以不需要有一个数据报类,这里直接用 Socket 中自带的文件来处理。
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TcpEchoServer { //创建ServerSocket对象,是为了建立连接和接收客户端所传的信息 private ServerSocket serverSocket = null; //服务器要指定端口号,方便客户端找到服务器 public TcpEchoServer(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("程序启动!!"); while(true) { //serverSocket 使用 accept 方法建立连接,这个方法会返回客户端的请求信息。 //用 Socket 这个类来接收客户端的信息 Socket socket = serverSocket.accept(); //因为要建立连接,processConnection 方法要执行完,也就是客户端断开链接,才能执行下一个客户端请求。 //这样就不能同时处理多个客户端的请求了,为了解决这个问题,使用了多线程,每一个线程负责一个客户端。 Thread thread = new Thread(()->{ processConnection(socket); }); thread.start(); } } private void processConnection(Socket socket) { System.out.printf("[%s, %d] 建立建立连接!!", socket.getInetAddress().toString(), socket.getPort()); //Socket 对象要使用文件来进行操作,每一个对象中都自带有一个文件。 try(InputStream inputStream = socket.getInputStream()) { try(OutputStream outputStream = socket.getOutputStream()){ //使用Scanner类,为了简化代码,写起来更加的方便 Scanner scanner = new Scanner(inputStream); while(true){ //如果客户端的请求接收完成,就断开链接,退出循环 if(!scanner.hasNext()){ System.out.printf("[%s, %d] 断开链接!!!", socket.getInetAddress().toString(),socket.getPort()); break; } //接收客户端的请求 String request = scanner.next(); //服务器对客户端的请求,做出响应 String response = process(request); //把客服端的响应写入文件中,让客户端接收 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(response); //其中的 flush 是刷新缓冲区,为了让客户端及时接收到数据 printWriter.flush(); System.out.printf("[%s, %d], request:%s, response:%s\n", socket.getInetAddress().toString(), socket.getPort(), request, response); } } } catch (IOException e) { e.printStackTrace(); }finally { try { //关闭文件 socket.close(); } catch (IOException e) { e.printStackTrace(); } } } //服务器根据请求做出响应 private String process(String request) { return request; } //启动服务器 public static void main(String[] args) throws IOException { TcpEchoServer server = new TcpEchoServer(9090); server.start(); } }
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class TcpEchoClient { //创建 Socket 对象 private Socket socket = null; //这里传入的是服务器的地址,和端口号,为了能找到服务器 public TcpEchoClient(String serverIP, int serverPort) throws IOException { socket = new Socket(serverIP, serverPort); } public void start(){ System.out.println("链接建立成功"); Scanner scanner = new Scanner(System.in); try(InputStream inputStream = socket.getInputStream()) { try(OutputStream outputStream = socket.getOutputStream()){ while(true){ //客户端发出请求 String request = scanner.next(); //向文件中写入请求 PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(request); printWriter.flush(); //接收文件中返回来的响应 Scanner scanner1 = new Scanner(inputStream); String response = scanner1.next(); System.out.printf("request: %s, response: %s\n", request, response); } } } catch (IOException e) { e.printStackTrace(); } } //启动客户端 public static void main(String[] args) throws IOException { TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090); tcpEchoClient.start(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。