网络编程套接字

目录

Socket套接字定义

分类

 UDP数据报套接字编程

DatagramSocket API

DatagramPacket API

基于UDP socket最简单的客户端服务器程序

 改进:请求是一个英文字母,返回的响应是翻译的中文

TCP流套接字编程

ServerSocket API

Socket API

基于TCP socket最简单的客户端服务器程序


Socket套接字定义:

是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元

基于Socket套接字的网络程序开发就是网络编程

程序员编写代码,主要编写的是应用层的代码,真正要发数据,需要上层协议调用下层协议,即应用层要调用传输层 

socket api:传输层给应用层提供的一组api

分类:

基于UDP的api:

  • 无连接:使用UDP通信的双方,不需要刻意保存对端的相关信息 (eg:发短信)
  • 不可靠传输:信息发了,不关注结果 (不需要接受连接就能通信)
  • 面向数据报:以一个UDP数据报为基本单位
  • 全双工:一条路径,双向通信

基于TCP的api

  • 有连接:使用TCP通信的双方,需要刻意保存对端的相关信息 (eg:打电话)
  • 可靠传输:尽可能传输过去,并要关注是否发送成功(需要接受连接才能通信)
  • 面向字节流:以字节为基本单位,读写方式非常灵活
  • 全双工:一条路径,双向通信

 UDP数据报套接字编程:

要想进行网络通信,需要socket这样的对象(相当于遥控器),借助这个对象才能间接操作网卡

往这个socket对象中写数据,相当于通过网卡发送信息

往这个socket对象中读数据,相当于通过网卡接受信息

DatagramSocket API:

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

DatagramSocket 构造方法:

DatagramSocket() :

创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口 (一般用于客户端) 

DatagramSocket(int port) :

创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端) 

这里的socket对象可能被客户端或者服务器使用,服务器往往要关联一个具体的端口号(必须不变),客户端则不需要手动指定,系统会自动分配

DatagramPacket API:

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

DatagramPacket 构造方法:

DatagramPacket(byte[] buf, int length)

构造一个DatagramPacket以用来接收数据报,接收的数据保存在 字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

构造一个DatagramPacket以用来发送数据报,发送的数据为字节 数组(第一个参数buf)中,从0到指定长度(第二个参数 length)。address指定目的主机的IP和端口号

基于UDP socket最简单的客户端服务器程序:

回显服务器:客户端发送一个请求,服务器返回一个一模一样的响应

服务器的核心工作

  • 读取请求并解析
  • 根据请求返回响应(省略)
  • 把响应返回客户端

客户端的核心工作

  • 读取用户输入
  • 构造请求,并发送
  • 读取服务器发来的响应
  • 把响应转换成字符串,并显示出来 
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class updEcoServer {
    private DatagramSocket socket=null;//先定义一个socket对象
    public updEcoServer(int port) throws SocketException {
        //抛异常的原因:同一个主机,一个端口,同一时刻,只能被一个一个进程绑定
        //如果某个端口被其他端口占用了,此时的绑定会出错
        socket=new DatagramSocket(port);//指定要关联的端口
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            //1.读取请求并解析
            //构造空饭盒
            DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
            //装满饭(饭从网卡来)
            socket.receive(requestPacket);
            String request=new String(requestPacket.getData(),0,requestPacket.getLength());
            //为了方便处理,把数据报转为string
            String response=process(request);//2.根据请求计算响应
            //3.把响应写回客户端
            //和上面的datagrampacket不同,这里要指定这个报发给谁
            DatagramPacket responsePacket= new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());//requestPacket是从客户端收来的,
            // getSocketAddress()会得到客户端的IP和端口号
            socket.send(responsePacket);//发送
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }
    public static String process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        updEcoServer updEcoServer=new updEcoServer(9090);
        updEcoServer.start();
    }
}

运行结果:

  61656是系统给客户端分配的空闲端口

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket=null;
    private String serverIP;
    private int serverPort;
    public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
        socket=new DatagramSocket();//不用指定端口,因为系统会自动分配
        this.serverIP=serverIP;
        this.serverPort=serverPort;
    }
    public void start() throws IOException {
        Scanner scanner=new Scanner(System.in);
        while (true){
            System.out.println("请输入内容");
            //1.控制台读取一个字符串
            String request=scanner.next();
            //2.把字符串构造成UDP packet
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);//发送
            //3.客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);//接收
            //把响应数据转换成String
            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("127.0.0.1",9090);
        udpEchoClient.start();
    }
}

运行结果:

  执行步骤:

 改进:请求是一个英文字母,返回的响应是翻译的中文

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

// 使用继承, 是为了复用之前的代码.
public class UdpDictServer extends updEcoServer{
    private Map<String, String> dict = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);

        dict.put("dog", "小狗");
        dict.put("cat", "小猫");
        dict.put("fuck", "卧槽");
    
    }

    @Override
    public String process(String request) {
        return dict.getOrDefault(request, "该单词没有查到!");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer = new UdpDictServer(9090);
        udpDictServer.start();
    }
}

 执行结果:

TCP流套接字编程:

ServerSocket API:

 ServerSocket是创建TCP服务端socket的API

ServerSocket 构造方法:

ServerSocket(int port) :创建一个服务端流套接字Socket,并绑定到指定端口(必须指定)

ServerSocket 方法:

Socket accept() :开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待 (可以先简单理解为接受连接)

Socket API:

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

Socket 构造方法:

Socket(String host, int port) :创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的 进程建立连接(表示服务器的IP和端口)

Socket 方法:

InetAddress getInetAddress() :返回套接字所连接的地址

InputStream getInputStream() :返回此套接字的输入流

OutputStream getOutputStream(): 返回此套接字的输出流

短连接:只能收发一次数据

长连接:可以多次收发数据 

基于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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket=null;
    //serverSocket只有一个
     public TcpEchoServer(int port) throws IOException {
         serverSocket=new ServerSocket(port);
     }
     public void start() throws IOException {
         ExecutorService executorService= Executors.newCachedThreadPool();
         System.out.println("服务器启动");
         while (true){
             Socket clientSocket=serverSocket.accept();//clientSocket可以有很多个
             executorService.submit(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         processConnection(clientSocket);
                     } catch (IOException e) {
                         throw new RuntimeException(e);
                     }
                 }
             });
         }
     }
     private void processConnection(Socket clientSocket) throws IOException {
         //1.读取请求
         System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress().toString(),
                 clientSocket.getPort());//请求与请求之间用换行分离
         try (
             InputStream inputStream=clientSocket.getInputStream();
             OutputStream outputStream=clientSocket.getOutputStream()){
             //使用scanner和printWriter即给in和out套了个壳使用字符流代替字节流,不用一个一个字节的扣,更方便
             Scanner scanner=new Scanner(inputStream);
             PrintWriter printWriter=new PrintWriter(outputStream);
             while (true){
                 if(!scanner.hasNext()){//即读到了末尾
                     System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress().toString(),
                             clientSocket.getPort());
                     break;
                 }
                 String request=scanner.next();//读取一段字符串
                 //2.根据请求计算响应
                 String response=process(request);
                 //3.把响应写回客户端
                 printWriter.println(response);
                 printWriter.flush();
                 System.out.printf("[%s:%d] req:%s;resp:%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();
    }
}
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 {
    private Socket socket=null;
    private TcpEchoClient(String serverIP,int port) throws IOException {
        socket=new Socket(serverIP,port);
    }
    public void start(){
        Scanner scanner=new Scanner(System.in);
        try {
            InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream();
            Scanner scannerF=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while (true){
                //键盘录入用户输入的内容
                System.out.println("- >");
                String request=scanner.next();
                //2.构成请求,发给服务器
                //请求以换行分隔
                printWriter.println(request);
                printWriter.flush();
                //3.读取响应内容
                String response =scannerF.next();
                //4.显示到控制台
                System.out.printf("res:%s;resp:%s\n",request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcp=new TcpEchoClient("127.0.0.1",9090);
               tcp.start();
    }
}

运行结果:

执行步骤:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

发呆的百香果子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值