网络编程套接字Socket

简介: 网络编程套接字Socket

一.什么是网络编程

网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。

二.为什么要实现网络编程

我们通过网络编程可以在网络中获取资源,实质是通过网络,获取到我们所需要的资源。

三.如何进行网络编程

针对网络编程,操作系统提供了一套专门实现网络编程的API,称为Socket套接字

我们的程序在应用层,操作系统工作在传输层,socket套接字就是传输层提供给应用层的API,传输层中最知名的协议就是TCP和UDP。

四.Socket关键字

Socket关键字针对传输协议分为三类:

①TCP传输协议②UDP传输协议③原始套接字

Tcp传输协议和UDP传输协议的区别:

df611ab7c6dab481348c4bdbbf71dbe2.png

有连接:相当于打电话,得先接通,才能交互数据

无连接:相当于发微信,不需要接通,直接就能发数据

可靠传输:传输过程中,发送方道知接收方有没有收到数据

不可靠传输:传输过程中,发送方不知道接收方有没有收到数据

面向字节流:以字节为单位进行传输

面向数据报:以数据报为单位进行传输(一个数据报都会明确大小)一次发送/接收必须是一个完整的数据报

全双工:一条链路,双向通信

半双工:一条链路,单向通信

大小限制:打电话可长可短,但是发短信有字数限制。

4.1UDP中的API

主要涉及两个类DatagramSocket(针对服务器上启动的网络服务,对应到服务器上用来接收用户请求的进程)和DatagramPacket(针对通信报文)

在网络编程中收发数据的过程主要是通过网卡来实现的,接收数据是对网卡的读取,发送数据时对网卡的写入。

构造方法:

f0d1bd735a58649086fa8490f9b25b67.png

20ad5d3ac6d87736ce942b7ec8f613e8.png

收发和关闭方法:

f4d2d677c0c141fb5128b822eae59bf0.png

f4d2d677c0c141fb5128b822eae59bf0.png

03dd49fd6555f160ab2b14836c9e2e96.png

notes:receive()方法会在没有客服端访问时进入阻塞状态,send()方法则是直接发送

测试要求:

根据请求返回响应:响应数据与请求数据一致

服务端代码:

import java.io.IOException;

import java.net.BindException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.SocketException;

import java.nio.charset.StandardCharsets;

 

/**

* @author tongchen

* @create 2023-02-17 23:30

*/

public class UDPEchoServer {

   //创建socket变量

   private DatagramSocket socket;

   //通过构造方法指定端口号

 

   public UDPEchoServer(int port) throws SocketException {

       //检查端口号的合法性

       if(port<0||port>65535){

           throw new BindException("端口建议在合法范围内");

       }

       //创建socket

       this.socket=new DatagramSocket(port);

   }

   //启动服务

   public void start() throws IOException {

       //标识运行

       System.out.println("服务端开始启用.......");

       while(true){

           //使用datagrampacket进行数据的接收

           DatagramPacket datagramPacket = new DatagramPacket(new byte[1024], 1024);

           //使用socket接收数据

           socket.receive(datagramPacket);

           //解析接收到的数据

           String request=new String(datagramPacket.getData(), 0, datagramPacket.getData().length, "utf-8");

           //根据数据获取响应数据

           String response=pocess(request);

           //对响应数据进行封装

           DatagramPacket responsePacket=new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),

                   response.getBytes().length,datagramPacket.getSocketAddress());

           //发送响应数据

           socket.send(responsePacket);

           System.out.printf("[%s:%d] request = %s, response = %s\n", datagramPacket.getAddress().toString(),

                   datagramPacket.getPort(), request, response);

       }

 

   }

 

   private String pocess(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.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetSocketAddress;

import java.net.SocketException;

import java.nio.charset.StandardCharsets;

import java.util.Scanner;

 

/**

* @author tongchen

* @create 2023-02-18 11:00

*/

public class UDPchoClient {

   //定义服务器的地址和端口号

   private String address;

   private int port;

   private DatagramSocket socket;

   //初始化端口号和目标地址

   public UDPchoClient(String address, int port) throws SocketException {

       this.address = address;

       this.port = port;

       this.socket=new DatagramSocket();

   }

   //启动程序

   public void start() throws IOException {

       System.out.println("客户端启动......");

       //输入请求数据

       while(true){

           System.out.println("请输入请求数据");

           Scanner scanner=new Scanner(System.in);

           String request=scanner.nextLine();

           //与服务端建立连接

           InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);

           //进行数据的封装

           DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);

           //发送数据

           socket.send(requestPacket);

           //创建接收packet

           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 {

       //创建客户端

       UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9090);

       //启动客户端

       udPchoClient.start();

测试结果分析

48b7d91f767b8b10f2c5bbb598ffc3f2.png

测试要求2:请求英文单词,返回其中文翻译

服务端

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.SocketException;

import java.nio.charset.StandardCharsets;

import java.util.HashMap;

 

/**

* @author tongchen

* @create 2023-02-18 11:36

*/

public class UDPdicServer {

   //创建字典

   private HashMapdic;

   //创建socket

  private DatagramSocket socket;

   //通过构造方法进行对所需内容的初始化

 

   public UDPdicServer(int port) throws SocketException {

       //创建新的hashmap

       this.dic=new HashMap<>();

       dic.put("tiger", "老虎");

       dic.put("cat", "小猫");

       dic.put("dog", "小狗");

       dic.put("pig", "小猪");

       //创建新的socket

       socket=new DatagramSocket(port);

   }

 

   //创建启动方法

   public void start() throws IOException {

       System.out.println("服务端启动.......");

       //创建循环

       while(true){

           //创建接收请求的packet

           DatagramPacket requestPacket=new DatagramPacket(new byte[1024],1024);

           //接收数据

           socket.receive(requestPacket);

           //对数据进行解析

           String request=new String(requestPacket.getData(), 0, requestPacket.getLength(), "utf-8");

           //根据请求获取响应

           String response=pocess(request);

           //封装数据

           DatagramPacket responsePacket = new DatagramPacket(response.getBytes(StandardCharsets.UTF_8), response.getBytes().length, requestPacket.getSocketAddress());

           //发送数据

           socket.send(responsePacket);

           //打印结果

           System.out.printf("[%s:%d] request = %s, response = %s\n", requestPacket.getAddress().toString(),

                   requestPacket.getPort(), request, response);

       }

 

   }

 

   private String pocess(String request) {

       return dic.getOrDefault(request, "查无此词");

   }

 

   public static void main(String[] args) throws IOException {

       //创建服务端

       UDPdicServer udPdicServer = new UDPdicServer(9091);

       udPdicServer.start();

   }

}

用户端:

import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetSocketAddress;

import java.net.SocketException;

import java.nio.charset.StandardCharsets;

import java.util.Scanner;

 

/**

* @author tongchen

* @create 2023-02-18 11:00

*/

public class UDPchoClient {

   //定义服务器的地址和端口号

   private String address;

   private int port;

   private DatagramSocket socket;

   //初始化端口号和目标地址

   public UDPchoClient(String address, int port) throws SocketException {

       this.address = address;

       this.port = port;

       this.socket=new DatagramSocket();

   }

   //启动程序

   public void start() throws IOException {

       System.out.println("客户端启动......");

       //输入请求数据

       while(true){

           System.out.println("请输入请求数据");

           Scanner scanner=new Scanner(System.in);

           String request=scanner.nextLine();

           //与服务端建立连接

           InetSocketAddress inetSocketAddress = new InetSocketAddress(address, port);

           //进行数据的封装

           DatagramPacket requestPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8), request.getBytes().length, inetSocketAddress);

           //发送数据

           socket.send(requestPacket);

           //创建接收packet

           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 {

       //创建客户端

       UDPchoClient udPchoClient = new UDPchoClient("127.0.0.1", 9091);

       //启动客户端

       udPchoClient.start();

   }

}

结果分析:

image.png


4.2实现TCP协议的类

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法:

a08642452fb3fba0a9da3890ab46b69d.png

Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端

Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket 构造方法:

1680a24ebfdfebb8620286952c962cf8.png

c2e35f0528909f4c8f8aebb858c66514.png

我们通过实例对TCP协议进行讲解:客户端向服务端发送数据,服务端返回同样的数据

服务端

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.BindException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Scanner;

 

/**

* @author tongchen

* @create 2023-02-20 10:50

*/

public class TcpEchoService {

   //通过创建构造方法指定端口号获取socket连接

   private ServerSocket socket;

 

   public TcpEchoService(int port) throws IOException {

       //判断端口的有效性

       if(port<1025||port>65535){

           throw new BindException("端口建议在1025和65535之间.......");

       }

       //通过指定端口号创建socket服务

       this.socket=new ServerSocket(port);

   }

 

   //获取客户端连接

   public void start() throws IOException {

       System.out.println("服务端端获取连接成功......");

       //不断循环接收用户的请求

       while(true){

           //获取与客户端的连接

           Socket clientSocket = socket.accept();

          //处理用户请求

           pocessRequset(clientSocket);

 

       }

 

   }

 

   private void pocessRequset(Socket clientSocket) throws IOException {

       //打印客户端信息

       System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());

       //通过clientSocket中获取输入和输出数据

       try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){

           //循环从输入中获取数据

           while (true){

               Scanner scan=new Scanner(inputStream);

               //判断是否存在

               if(!scan.hasNextLine()){

                   System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());

                   break;

               }

               //获取数据并获得返回数据

               String request=scan.nextLine();

               String response=pocess(request);

               //发送数据

               PrintWriter printWriter = new PrintWriter(outputStream);

               //注意输入输出格式的一致性(用户和客户端)

               printWriter.println(response);

               //强制刷新缓存区

               printWriter.flush();

               //打印信息

               System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),

                       clientSocket.getPort(), request, response);

 

           }

 

       }catch (Exception e){

           e.printStackTrace();

       }finally {

           //关闭资源

           clientSocket.close();

 

       }

   }

 

   private String pocess(String request) {

       return request;

   }

   //初始化服务器

   public static void main(String[] args) throws IOException {

       TcpEchoService tcpEchoService = new TcpEchoService(9090);

       tcpEchoService.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;

 

/**

* @author tongchen

* @create 2023-02-20 11:17

*/

public class TcpEchoClient {

   //创建socket

   private Socket socket;

   //构造方法创建socket

 

   public TcpEchoClient(String address,int port) throws IOException {

       this.socket=new Socket(address, port);

   }

   //连接

   public void start(){

       System.out.printf("客户端启动成功....");

       try(InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream()){

           //循环输入

           while(true){

               System.out.println("->");

               Scanner scan=new Scanner (System.in);

               String request=scan.nextLine();

               //验证request的有效性

               if(request==null||request.equals("")){

                   System.out.println("输入的请求不能为空");

                   continue;

               }

               //将request输入到socket

               PrintWriter printWriter = new PrintWriter(outputStream);

               printWriter.println(request);

               //强制刷新

               printWriter.flush();

               //在socket中获取请求

               Scanner scanner = new Scanner(inputStream);

               String response = scanner.nextLine();

               System.out.printf("request = %s, response = %s\n", request, response);

           }

       }catch (Exception e){

           e.printStackTrace();

       }finally {

 

       }

       //循环输入

 

   }

   //验证‘=

   public static void main(String[] args) throws IOException {

       TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);

       tcpEchoClient.start();

   }

}

一个客户端时可以正常运行,建立两个客户端时却出现了下面的情况:

95883fa5d962b117b0d0718f862fd271.png

48169eb86dc46d97c5d4548b49758480.png

我们去分析情况,是因为只有一个线程,客户端没有退出,服务端卡在第二个while循环中出不来


6866b5cf9b3b5f5246f77e237f3202fd.png

24f095ae2fdc97380990e5f7bf8965ef.png

解决措施也很简单,开启多线程即可,为了线程频繁的开启和销毁所消耗的系统资源,我们使用线程池进行优化:

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.BindException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Scanner;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

/**

* @author tongchen

* @create 2023-02-20 12:18

*/

public class ThreadPoolTest {

   //通过创建构造方法指定端口号获取socket连接

   private ServerSocket socket;

 

   public ThreadPoolTest(int port) throws IOException {

       //判断端口的有效性

       if(port<1025||port>65535){

           throw new BindException("端口建议在1025和65535之间.......");

       }

       //通过指定端口号创建socket服务

       this.socket=new ServerSocket(port);

   }

 

   //获取客户端连接

   public void start() throws IOException {

       System.out.println("服务端端获取连接成功......");

       //不断循环接收用户的请求

       //创建线程池

       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,

               new LinkedBlockingQueue<>(10));

       while(true){

           //获取与客户端的连接

           Socket clientSocket = socket.accept();

           threadPoolExecutor.submit(()->{

               try {

                   //处理用户请求

                   pocessRequset(clientSocket);

               } catch (IOException e) {

                   throw new RuntimeException(e);

               }

 

           });

 

       }

 

   }

 

   private void pocessRequset(Socket clientSocket) throws IOException {

       //打印客户端信息

       System.out.printf("%s|%d,客服端连接成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());

       //通过clientSocket中获取输入和输出数据

       try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){

           //循环从输入中获取数据

           while (true){

               Scanner scan=new Scanner(inputStream);

               //判断是否存在

               if(!scan.hasNextLine()){

                   System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());

                   break;

               }

               //获取数据并获得返回数据

               String request=scan.nextLine();

               String response=pocess(request);

               //发送数据

               PrintWriter printWriter = new PrintWriter(outputStream);

               //注意输入输出格式的一致性(用户和客户端)

               printWriter.println(response);

               //强制刷新缓存区

               printWriter.flush();

               //打印信息

               System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),

                       clientSocket.getPort(), request, response);

 

           }

 

       }catch (Exception e){

           e.printStackTrace();

       }finally {

           //关闭资源

           clientSocket.close();

 

       }

   }

 

   private String pocess(String request) {

       return request;

   }

   //初始化服务器

   public static void main(String[] args) throws IOException {

       ThreadPoolTest tcpEchoService = new ThreadPoolTest(9090);

       tcpEchoService.start();

   }

}

第二个任务:实现简单对话:

import javax.sound.sampled.Port;

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;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

/**

* @author tongchen

* @create 2023-02-20 15:01

*/

public class TcpMsgServer {

   //创建socket

private ServerSocket socket;

   public TcpMsgServer(int port) throws IOException {

       this.socket=new ServerSocket(port);

   }

   //开启工作

   public void start() throws IOException {

       System.out.println("用户端开启成功......");

       //循环接收用户请求

       //创建线程池

       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS,

               new LinkedBlockingQueue<>(10));

       while(true){

           //接收用户请求

           Socket clientSocket = socket.accept();

           //处理用户请求(使用多线程)

           threadPoolExecutor.submit(()->{

               try {

                   pocessRequset(clientSocket) ;

               } catch (IOException e) {

                   throw new RuntimeException(e);

               }

           });

       }

   }

 

   private void pocessRequset(Socket clientSocket) throws IOException {

       System.out.printf("%s|%d,客服端连接成功......\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());

       //获取两个流

       try(InputStream inputStream=clientSocket.getInputStream(); OutputStream outputStream=clientSocket.getOutputStream()){

           //多次获取用户请求

           while(true){

               //获取用户输入

               Scanner scan=new Scanner(inputStream);

               if(!scan.hasNext()){

                   System.out.printf("%s|%d,客服端退出成功......",clientSocket.getInetAddress().toString(),clientSocket.getPort());

                   break;

               }

               String request= scan.nextLine();

               //根据请求获取响应

               String response=pocess(request);

               //通过print发回

               PrintWriter printWriter = new PrintWriter(outputStream);

               printWriter.println(response);

               //打印效果

               printWriter.flush();

               System.out.printf("[%s:%d] request = %s, response = %s\n", clientSocket.getInetAddress().toString(),

                       clientSocket.getPort(), request, response);

 

           }

 

       }catch (Exception e){

           e.printStackTrace();

       }finally {

           clientSocket.close();

       }

   }

 

   private String pocess(String request) {

       System.out.println("请求为"+request);

       System.out.println("请输入你的响应->");

       Scanner scanner=new Scanner(System.in);

       String response=scanner.nextLine();

       return response;

   }

   //开启

   public static void main(String[] args) throws IOException {

       TcpMsgServer msgServer = new TcpMsgServer(9090);

       msgServer.start();

   }

}


相关文章
|
6天前
|
网络协议 算法 Linux
【嵌入式软件工程师面经】Linux网络编程Socket
【嵌入式软件工程师面经】Linux网络编程Socket
23 1
|
9天前
|
网络协议 Unix API
24.Python 网络编程:socket编程
24.Python 网络编程:socket编程
14 2
|
9天前
|
网络协议 Java API
网络编程套接字(4)——Java套接字(TCP协议)
网络编程套接字(4)——Java套接字(TCP协议)
13 0
|
9天前
|
Java 程序员 Linux
网络编程套接字(3)——Java数据报套接字(UDP协议)
网络编程套接字(3)——Java数据报套接字(UDP协议)
11 0
|
9天前
|
网络协议 API
网络编程套接字(2)——Socket套接字
网络编程套接字(2)——Socket套接字
8 0
|
13天前
|
网络协议 Java
Java的Socket编程:TCP/IP与UDP深入探索
Java的Socket编程:TCP/IP与UDP深入探索
17 0
|
30天前
Socket编程(头脑清晰,防止过载)
Socket编程(头脑清晰,防止过载)
|
7天前
|
移动开发 Java
Java Socket编程 - 基于Socket实现HTTP下载客户端
Java Socket编程 - 基于Socket实现HTTP下载客户端
13 1
|
7天前
|
监控 网络协议 Java
Java Socket编程 - 基于TCP方式的二进制文件传输
Java Socket编程 - 基于TCP方式的二进制文件传输
11 0
|
7天前
|
网络协议 Java
Java Socket编程 - 基于TCP方式的客户服务器聊天程序
Java Socket编程 - 基于TCP方式的客户服务器聊天程序
12 0