irpas技术客

Java网络编程基础_耀

未知 5497

目录

一、Socket套接字

1,概念

2,分类

二、Java数据报套接字通信模型

三、Java流套接字通信模型

四、UDP数据报套接字编程

1,DatagramSocket API

2,DatagramPacket API

3,示例代码

五、TCP流套接字编程

1,ServerSocket API

2,Socket API

3,示例代码


一、Socket套接字 1,概念

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基 于Socket套接字的网络程序开发就是网络编程。

2,分类

流套接字:使用传输层TCP协议

TCP,即Transmission Control Protocol(传输控制协议),传输层协议。

以下为TCP的特点

????????有连接

????????可靠传输

????????面向字节流

????????有接收缓冲区,也有发送缓冲区

????????大小不限

数据报套接字:使用传输层UDP协议

UDP,即User Datagram Protocol(用户数据报协议),传输层协议。

以下为UDP的特点

????????无连接

????????不可靠传输

????????面向数据报

????????有接收缓冲区,无发送缓冲区

????????大小受限:一次最多传输64k

二、Java数据报套接字通信模型

三、Java流套接字通信模型

四、UDP数据报套接字编程 1,DatagramSocket API

DatagramSocket是UDP Socket,用于发送和接收UDP数据报。

DatagramSocket方法

方法签名方法说明void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻 塞等待)void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)void close()关闭此数据报套接字

DatagramSocket构造方法

方法签名方法说明DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口 (一般用于客户端)DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用 于服务端)
2,DatagramPacket API

DatagramPacket是UDP Socket发送和接收的数据

DatagramPacket方法

方法签名方法说明InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取 接收端主机IP地址int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获 取接收端主机端口号byte[] getData()获取数据报中的数据

DatagramPacket构造方法

方法签名方法说明DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节 数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号
3,示例代码 import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UdpEchoServer { //对于一个服务器来说,核心分成两部 //进入初始化操作(实例化Socket对象) //进入主循环,接受请求 //读取响应 //根据请求计算响应 //把响应写回客户端 private DatagramSocket socket = null; public UdpEchoServer(int port) throws SocketException { socket = new DatagramSocket(port); } public void start() throws IOException { System.out.println("服务器启动"); while (true){ //读取解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); String request = new String(requestPacket.getData(),0, requestPacket.getLength()).trim(); //根据请求计算响应 String response = process(request); //把响应写回客户端,响应数据就是response,需要包装成一个对象 DatagramPacket datagramPacket = new DatagramPacket(response.getBytes(),response.getBytes().length ,requestPacket.getSocketAddress()); socket.send(requestPacket); System.out.printf("[%s:%d] req: %s; resp: %s\n", requestPacket.getAddress().toString(), requestPacket.getPort(), request, response); } } private String process(String request) { return request; } public static void main(String[] args) throws IOException { UdpEchoServer udpEchoServer = new UdpEchoServer(9090); udpEchoServer.start(); } } import java.io.IOException; import java.net.*; import java.util.Scanner; public class UdpEchoClient { //1,读客户端的数据 //2,构造请求发送给客户端 //3,从服务器读取响应 //4,把响应写回客户端 private DatagramSocket socket = null; private String serverIp; private int serverPort; //需要在启动客户端的时候来指定那个服务器 public UdpEchoClient(String serverIp,int serverPort) throws SocketException { this.serverIp = serverIp; this.serverPort = serverPort; socket = new DatagramSocket(); } public void start() throws IOException { Scanner scanner = new Scanner(System.in); while (true){ //读取用户数据 System.out.println("->"); String request = scanner.nextLine(); if (request.equals("exit")){ break; } //构造请求发送给服务器 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp),serverPort); socket.send(requestPacket); //从服务器读取响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096); socket.receive(responsePacket); String response = new String(responsePacket.getData(),0,responsePacket.getLength()).trim(); //显示数据 System.out.println(response); } } public static void main(String[] args) throws IOException { UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090); udpEchoClient.start(); } }

五、TCP流套接字编程 1,ServerSocket API

ServerSocket是创建TCP服务端Socket的API

ServerSocket构造方法

方法签名方法说明ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket方法

方法签名方法说明Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待void close()关闭此套接字
2,Socket API

Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket构造方法

方法签名方法说明Socket(String host, int port)Socket(String host, int port)

Socket方法

方法签名方法说明InetAddress getInetAddress()返回套接字所连接的地址InputStream getInputStream()返回此套接字的输入流OutputStream getOutputStream()返回此套接字的输出流
3,示例代码 import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class TcpEchoServer { //1,初始化服务器 //2,进入主循环 //先从内核中获取到一个TCP连接 //处理这个TCP连接 //读取请求并解析 //根据请求计算响应 //把响应写回客户端 private ServerSocket serverSocket = null; public TcpEchoServer(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("服务器启动"); while (true){ //先从内核中获取一个TCP连接 Socket clientSocket = serverSocket.accept(); //处理这个连接 processConnection(clientSocket); } } private void processConnection(Socket clientSocket) { System.out.printf("[%s:%d]客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort()); //通过clientSocket来与客户端交互 try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) { //此处建立一个连接过程,处理多个请求和响应 while (true){ //读取请求响应 String request = bufferedReader.readLine(); //根据请求计算响应 String response = process(request); //把响应写回客户端 bufferedWriter.write(response + "\n"); bufferedWriter.flush(); System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(), clientSocket.getPort(),request,response); } } catch (IOException e) { e.printStackTrace(); System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress().toString(), clientSocket.getPort()); } } private String process(String request) { return request; } public static void main(String[] args) throws IOException { TcpEchoServer tcpEchoServer = new TcpEchoServer(9090); tcpEchoServer.start(); } } import java.io.*; import java.net.Socket; import java.util.Scanner; public class TcpClientServer { //启动客户端 //进入主循环 //读取用户内容 //构造一个请求发送给服务器 //读取服务器的响应的数据 //把响应写到界面 private Socket socket = null; public TcpClientServer(String serverIp,int serverPort) throws IOException { //实例化Socket,建立TCP连接 socket = new Socket(serverIp,serverPort); } public void start(){ System.out.println("客户端启动"); Scanner scanner = new Scanner(System.in); try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))){ while (true){ //读取用户输入内容 System.out.println("->"); String request = scanner.nextLine(); if (request.equals("exit")){ break; } //构造请求并发送 bufferedWriter.write(request + "\n"); bufferedWriter.flush(); //读取响应数据 String response = bufferedReader.readLine(); //把响应写到界面 System.out.println(response); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { TcpClientServer tcpClientServer = new TcpClientServer("127.0.0.1",9090); tcpClientServer.start(); } }

?getOutputStream得到一个流对象,进一步封装成一个BufferedWriter

代码调用BufferedWriter.write方法的时候,先把数据放在缓冲区,此时wrtite操作并没有往内核中写socket文件中的数据。

调用flush方法,把内存缓冲区中的内容写入Socket文件中

import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class TcpThreadEchoServer { private ServerSocket serverSocket = null; public TcpThreadEchoServer(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("服务器启动"); while (true){ Socket clientSocket = serverSocket.accept(); Thread t = new Thread(){ @Override public void run() { processConnection(clientSocket); } }; t.start(); } } public void processConnection(Socket clientSocket) { System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort()); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()))) { while (true) { // 1. 读取请求并解析 String request = bufferedReader.readLine(); // 2. 根据请求计算响应 String response = process(request); // 3. 把响应写回到客户端 bufferedWriter.write(response + "\n"); bufferedWriter.flush(); System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response); } } catch (IOException e) { // e.printStackTrace(); System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort()); } } public String process(String request) { return request; } public static void main(String[] args) throws IOException { TcpThreadEchoServer server = new TcpThreadEchoServer(9090); server.start(); } }

?主线程专门负责accept,其他线程负责和客户端沟通

public void start() throws IOException { System.out.println("服务器启动"); //先创建一个线程池实例 ExecutorService executorService = Executors.newCachedThreadPool(); while (true){ //针对这个连接,单独创建一个线程池来负责 Socket clientSocket = serverSocket.accept(); executorService.execute(new Runnable() { @Override public void run() { processConnection(clientSocket); } }); } }

虽然使用多线程解决了BUG,但还有很多问题,每次来一个客户端,都要分配一个线程对于一个服务器来说,随时可能会来大量的客户端,随时也会有大量的客户端断开连接,服务器需要频繁的创建和销毁线程,所以可以用线程池


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Java网络编程基础 # #control