网络编程套接字Socket

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 网络编程套接字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();

   }

}


相关文章
|
1月前
|
Kubernetes 网络协议 Python
Python网络编程:从Socket到Web应用
在信息时代,网络编程是软件开发的重要组成部分。Python作为多用途编程语言,提供了从Socket编程到Web应用开发的强大支持。本文将从基础的Socket编程入手,逐步深入到复杂的Web应用开发,涵盖Flask、Django等框架的应用,以及异步Web编程和微服务架构。通过本文,读者将全面了解Python在网络编程领域的应用。
37 1
|
2月前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
34 1
|
2月前
|
消息中间件 监控 网络协议
Python中的Socket魔法:如何利用socket模块构建强大的网络通信
本文介绍了Python的`socket`模块,讲解了其基本概念、语法和使用方法。通过简单的TCP服务器和客户端示例,展示了如何创建、绑定、监听、接受连接及发送/接收数据。进一步探讨了多用户聊天室的实现,并介绍了非阻塞IO和多路复用技术以提高并发处理能力。最后,讨论了`socket`模块在现代网络编程中的应用及其与其他通信方式的关系。
294 3
|
2月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
2月前
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程
31 0
|
3月前
|
网络协议
关于套接字socket的网络通信。&聊天系统 聊天软件
关于套接字socket的网络通信。&聊天系统 聊天软件
|
5月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
63 4
|
5月前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
【7月更文挑战第25天】在网络应用蓬勃发展的数字时代,Python凭借其简洁的语法和强大的库支持成为开发高效应用的首选。本文通过实时聊天室案例,介绍了Python Socket编程的基础与进阶技巧,包括服务器与客户端的建立、数据交换等基础篇内容,以及使用多线程和异步IO提升性能的进阶篇。基础示例展示了服务器端监听连接请求、接收转发消息,客户端连接服务器并收发消息的过程。进阶部分讨论了如何利用Python的`threading`模块和`asyncio`库来处理多客户端连接,提高应用的并发处理能力和响应速度。掌握这些技能,能使开发者在网络编程领域更加游刃有余,构建出高性能的应用程序。
38 3
|
5月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
【7月更文挑战第26天】在网络的数字宇宙中,Python Socket编程是开启网络世界大门的钥匙。本指南将引领你从基础到实战,成为网络世界的建筑师。
67 2
|
5月前
|
网络协议 程序员 视频直播