网络编程

简介: 网络编程

Socket 套接字

1.1 概念

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

1.2 分类

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

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


特点:


• 有连接(通信双方需要刻意保留对方的信息):打电话就是有连接,要想打电话,得先有对方的号码。


• 可靠传输


• 面向字节流:以字节为传输的基本单位,读写非常灵活


• 由接收缓冲区,也有发送缓冲区


• 全双工:一条路径,双向通信。即两端可以同时收发消息。(半双工:一端发消息,另一端只能接收消息)。


• 大小不限

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

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

特点:

• 无连接(通信双方不需要刻意保留对方的消息): 发短信,发送验证码时并不需要知道对方的号码,可以直接发送消息。

• 不可靠传输

• 面向数据报:以一个 UDP 数据报为基本单位。

• 有接收缓冲区

• 全双工

• 大小有限:一次最多传输64k


二. UDP 数据报套接字编程

2.1 DatagramSocket API

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

DatagramSocket 构造方法:

DatagramSocket 方法:

2.2 DatagramPacket API

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

DatagramPacket 构造方法:

DatagramPacket 方法:

2.3 UDP 回显代码示例:

UDP 客户端

public class UDPEchoClient {
    private String SeverIP;
    private int SeverPort;
    private DatagramSocket socket=null;
    public UDPEchoClient(int SeverPort,String SeverIP) throws SocketException {
        socket=new DatagramSocket();
        this.SeverPort=SeverPort;
        this.SeverIP=SeverIP;
    }
    public void start() throws IOException {
        Scanner scanner=new Scanner(System.in);
        while (true){
            System.out.print("->");
            String request=scanner.next();
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
                   InetAddress.getByName(SeverIP),SeverPort);
            socket.send(requestPacket);
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response=new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.printf("req: %s,resp: %s\n",request,response);
        }
    }
 
    public static void main(String[] args) throws IOException {
     //  UDPEchoClient udpEchoClient=new UDPEchoClient(9090,"127.0.0.1");
       UDPEchoClient udpEchoClient=new UDPEchoClient(9090,"114.115.149.19");
        udpEchoClient.start();
    }
}

UDP 服务器端

public class UDPEchoSever {
    //定义一个socket 对象,通过网络通信,必须要使用socket 对象.
    private DatagramSocket socket=null;
    //绑定一个端口,不一定能成功
    //如果某个端口已经被别的进程占用了,此时这里的绑定操作就会出错
    //同一个主机上,一个端口,同一时刻,只能被一个进程绑定
    public UDPEchoSever(int port) throws SocketException {
        socket=new DatagramSocket(port);
    }
    //启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
            //每次循环做三件事:
            //1.读取请求并解析,构造空饭盒(构造DatagramPacket对象获取数据报)
            DatagramPacket requestSocket=new DatagramPacket(new byte[4096],4096);
            //食堂大妈给饭盒里盛饭(将数据报中的内容全部放进上述构造的DatagramPacket对象中)
            socket.receive(requestSocket);
            //为了方便处理这个请求,把数据报转成String
            String request=new String(requestSocket.getData(),0, requestSocket.getLength());
            //2.根据请求计算响应(此处省略了这个步骤,没有明确的业务逻辑,直接返回了原内容)
            String response=process(request);
            //3.把响应结果写回到客户端
            //  格局response 字符串,构造一个DatagramPacket
            //  和请求 packet 不同,此处构造响应的时候,需要指定这个包发给谁
            DatagramPacket responseSocket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestSocket.getSocketAddress());
            socket.send(responseSocket);
            System.out.printf("[%s:%d]:req: %s,resp: %s\n",requestSocket.getAddress().toString(),requestSocket.getPort(),request,response);
        }
    }
    //这个方法希望根据请求计算响应,如果后续写个别的服务器,不再回显了,而是有具体的业务了,就可以修改 process方法,根据需要重新构造响应.
    private String process(String request) {
        return request;
    }
 
    public static void main(String[] args) throws IOException {
        UDPEchoSever udpEchoSever=new UDPEchoSever(9090);
        udpEchoSever.start();
    }
}

简单的单词查询服务器

public class UdpDictServer extends UDPEchoSever{
    private Map<String,String> map=new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        map.put("小猫","cat");
        map.put("小狗","dog");
        map.put("小猪","pig");
    }
    @Override
    public String process(String request) {
        return map.getOrDefault(request,"不包含要查找的单词!!");
    }
    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer=new UdpDictServer(9090);
        udpDictServer.start();
    }
}

三. TCP 流套接字

3.1 ServerSocket API

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

3.2 Socket API

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

3.3 TCP 回显代码:

服务器端:

package demo;
 
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.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhao3
 * Date: 2023-11-02
 * Time: 11:07
 */
public class TCPEchoServer {
    ServerSocket socket=null;
    public TCPEchoServer(int port) throws IOException {
        socket=new ServerSocket(port);
    }
    public void start() throws IOException {
        //无法同时处理多个客户的请求:如果直接调用,processConnection方法会影响这个循环的二次执行,导致 accept不及时. 法
        /*while (true){
            Socket clientSocket=socket.accept();
            processConnection(clientSocket);
        }*/
        //创建线程池,可以同时处理多个用户的请求
        ExecutorService pool= Executors.newCachedThreadPool();
        while (true){
            Socket clientSocket=socket.accept();
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
 
    private void processConnection(Socket clientSocket) throws IOException {
        try (InputStream inputStream=clientSocket.getInputStream();
             OutputStream outputStream=clientSocket.getOutputStream()){
            //套上一个套接字,将字节流改为字符流
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scanner=new Scanner(inputStream);
            while (true){
                //将字节流转换成字符流,每个请求用'\n'来  分割
                if(!scanner.hasNext()){
                    //请求结束
                    break;
                }
                String request=scanner.next();
                String response=process(request);
                printWriter.println(response);
                printWriter.flush();
                System.out.printf("[%s,%d]:req:%s,res:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            clientSocket.close();
        }
    }
 
 
    private String   process(String request) {
        return request;
    }
 
    public static void main(String[] args) throws IOException {
        TCPEchoServer tcpEchoServer=new TCPEchoServer(9090);
        tcpEchoServer.start();
    }
}

客户端:

package demo;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
 
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhao3
 * Date: 2023-11-02
 * Time: 11:07
 */
public class TCPEchoClient {
    Socket socket=null;
    public TCPEchoClient(String post, int port) throws IOException {
        socket=new Socket(post,port);
    }
    public void start() throws IOException {
        Scanner scanner=new Scanner(System.in);
        try (InputStream inputStream=socket.getInputStream();
             OutputStream outputStream= socket.getOutputStream()){
            Scanner scannerFromSocket=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while (true){
                System.out.print("->");
                String request=scanner.next();
                printWriter.println(request);
                printWriter.flush();
                String response= scannerFromSocket.next();
                System.out.printf("req:%s,res:%s\n",request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            socket.close();
        }
    }
 
    public static void main(String[] args) throws IOException {
        TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();
    }
}
相关文章
|
6月前
|
消息中间件 Kubernetes 网络协议
网络编程一些问题总结
网络编程一些问题总结
|
2月前
|
网络协议 程序员 API
初识网络编程
本文介绍了网络编程的重要概念,包括IP地址、端口号和协议。IP地址是设备在网络中的唯一标识,IPv4已用尽,IPv6提供了更多地址。端口号用于区分设备上的不同应用程序,取值范围为0~65535。协议定义了网络传输规则,常分为TCP/IP五层模型和OSI七层模型。文章还讨论了TCP与UDP的区别,并提供了UDP协议的简单示例。
47 0
初识网络编程
|
6月前
|
域名解析 网络协议 安全
网络编程
网络编程
|
网络协议 关系型数据库 MySQL
网络编程初识
网络编程初识
56 0
|
Java C++
4. 网络编程
4. 网络编程
57 0
|
网络协议
64.【网络编程】(一)
64.【网络编程】
41 0
|
应用服务中间件
64.【网络编程】(三)
64.【网络编程】
41 0
|
网络协议
64.【网络编程】(二)
64.【网络编程】
39 0