网络编程套接字socket

文章分以下几个部分:

网络编程

socket套接字

UDP数据报套接字编程

TCP流套接字编程

一.网络编程

网络编程基础:

我们为什么需要网络编程?为了获取丰富的网络资源。                                                                  用户在浏览器中,打开在线视频网站,比如通过优酷去看视频,实则是通过网络,获取到网上一个视频资源。与本地打开视频文件类似,只是这个视频文件的资源的来源是网络。相比本地资源来说,网络提供了更为丰富的网络资源。                                                                                               所谓的网络资源,其实就是在网络中可以获取到的各种数据资源。而所有的网络资源,都是通过网络编程来进行数据传输的。

什么是网络编程?网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信。

 我们只需要满足进程不同就行;所以即便是同一个主机,只要是不同的进程,基于网络来进行数据传输,也属于网络编程。

网络编程中的基本概念:

在一次网络数据传输时:

发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。

接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。

收发端:发送端和接收端。

发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。

 请求和响应:

一般来说,获取一个网络资源,涉及到两次网络数据传输。

第一次:请求数据的发送;

第二次:响应数据的发送;

客户端和服务端:

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以对外提供服务。

客户端:获取服务的一方进程,称为客户端。

对于服务端来说,一般是提供:客户端获取服务资源;客户端保存资源在服务端。

常见的客户端服务端模型:最常见的场景:客户端是指给用户使用的程序,服务端是提供用户的服务的程序:

1.客户端先发送请求到服务端。

2.服务端根据请求计算响应。

3.服务端将响应返回给服务端。

4.客户端展示服务端返回的响应。

二.Socket套接字

概念:

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

分类:

Socket套接字主要针对传输层协议划分为如下三类:

流套接字:使用传输层TCP协议。TCP:即Transmission Control Protocol(传输控制协议)。以下是TCP的特点:1.有连接。   2.可靠传输。   3.面向字节流。   4.有接收缓冲区,也有发送缓冲区。  5.大小不限。  

对于字节流来说,可以简单的理解为:传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

数据报套接字:使用传输层UDP协议。UDP:即User Datagram Protocol(用户数据报协议)。以下是UDP的特点:1.无连接。  2.不可靠传输。  3.面向数据报。  4.有接收缓冲区,无发送缓冲区。5.大小受限,一次最多传输64K。

对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

原始套接字:原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

三.UDP数据报套接字编程

对于UDP协议来说,具有无连接、面向数据报的特征,即每次都是没有建立连接,并且一次发送全部的数据报,一次接收全部的数据报。

java中使用UDP协议通信,主要基于DatagramSocket类来创建数据报套接字,并使用DatagramPacket类作为发送或接收的UDP数据报。

DatagramSocket  API:

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

DatagramSocket  构造方法:

1.方法签名DatagramSocket();方法说明:创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)。

2.方法签名:DatagramSocket(int port);方法说明:创建一个UDP数据报套接字的Socket,绑定到本机指定的端口号(一般用于服务器)。

DatagramSocket  方法:

1.方法签名:void  receive(DatagramPacket  p);方法说明:从此套接字(参数)接收数据报。如果没有接收数据报,该方法会阻塞等待。

2.方法签名:void  send(DatagramPacket  p);方法说明:从此套接字(参数)发送数据报。该方法不会阻塞等待,直接发送。

3.方法签名:void  close();方法说明:关闭此数据报套接字。

DatagramPacket API:

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

DatagramPacket  构造方法:

1.方法签名:DatagramPacket(byte[] buf,int length);方法说明:构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)。

2.方法签名:DatagramPacket(byte[] buf,int offset,int length,SocketAddress address);方法说明:构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP地址和端口号。

DatagramPacket  方法:

1.方法签名:InetAddress  getAddress();方法说明:从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址。

2.方法签名:int  getPort();方法说明:从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机的端口号。

3.方法签名:byte[]  getData();方法说明:获取数据报中的数据。

InetSocketAddress  API:

构造UDP发送的数据报时,需要传入SocketAddress,该对象可以使用InetSocketAddress类(SocketAddress的子类)来创建。

InetSocketAddress的构造方法:

1.方法签名:InetSocketAddress(InetAddress addr,int port);方法说明:创建一个Socket地址,包含IP地址和端口号。

java数据报套接字通信模型:

我们以回显客户端-服务器为例来理解java数据报套接字通信过程:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

//UDP服务端:

public class UdpEchoServer {

    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());

            //然后处理客户端发来的请求:
            String response=process(request);

            //然后将回应写回到客户端:
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),
                    response.getBytes().length,requestPacket.getSocketAddress());

            socket.send(requestPacket);

            System.out.printf("[%s:%d] request:%s; response:%s;",
                    requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }

    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server=new UdpEchoServer(9090);
        server.start();
    }
}
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

//UDP客户端:

public class UdpEchoClient {

    //该java文件实现一个客户端模式:
    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.print("->  ");
            String request=scanner.next();

            //然后构造一个UDP请求发送给服务器:
            DatagramPacket requestPacket=new DatagramPacket(
                    request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(this.serverIP),this.serverPort
            );
            socket.send(requestPacket);

            //然后从服务器读取响应并解析:
            DatagramPacket responsePacket=new DatagramPacket(new byte[
                    4096],4096);
            socket.receive(responsePacket);

            //然后将收到的响应转换成字符串:
            String response=new String(responsePacket.getData(),0,
                    requestPacket.getLength());

            //然后将响应显示到屏幕上:
            System.out.println(response);
        }
    }

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

四.TCP流套接字编程

ServerSocket   API:

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

ServerSocket  的构造方法:

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

ServerSocket  的方法:

1.方法签名:Socket  accept();方法说明:开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待。

2.方法签名:void  close();方法说明:关闭此套接字。

Socket  API:

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

Socket  的构造方法:

1.方法签名:Socket(String  host,int  port);方法说明:创建一个客户端流套接字Socket,并于对应IP上的主机,对应端口上的进程建立连接。

Socket  的方法:

1.方法签名:InetAddress  getInetAddress();方法说明:返回此套接字所连接的地址;

2.方法签名:InputStream  getInputStream();方法说明:返回此套接字的输入流;

3.方法签名:OutputStream  gerOutputStream();方法说明:返回此套接字的输出流;

java流套接字通信模型

我们以回显客户端-服务器为例来理解java流套接字通信过程:

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

public class TCPEchoServer {

    //实现TCP版本的服务器:
    private ServerSocket listenSocket=null;
    public TCPEchoServer(int port) throws IOException {
        listenSocket=new ServerSocket(port);
    }

    public void start() throws IOException {

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

        ExecutorService service= Executors.newCachedThreadPool();
        Socket clientSocket=listenSocket.accept();
        //然后去处理链接:
//        Thread t=new Thread(()->{
//            processConnection(clientSocket);
//        });
//        t.start();

        service.submit(new Runnable() {
            @Override
            public void run() {
                processConnection(clientSocket);
            }
        });
    }

    private void processConnection(Socket clientSocket){
        System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());

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

            while(true){
                //首先读取请求并解析:
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    System.out.printf("[%s:%d] 客户端下线!\n",
                            clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;

                }

                String request=scanner.next();
                //然后根据请求计算响应:
                String response=process(request);

                //将响应写回给客户端:
                PrintWriter printWriter=new PrintWriter(outputStream);
                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);
        }
    }

    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 {

//TCP版本的客户端:

    private Socket socket=null;

    public TCPEchoClient(String serverIp,int serverPort) throws IOException {
        socket=new Socket(serverIp,serverPort);
    }

    public void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){

            System.out.printf("->");
            String request=scanner.next();

            //将请求发送给服务器:
            PrintWriter printWriter=new PrintWriter(outputStream);
            printWriter.println(request);
            printWriter.flush();

            //然后从服务器中读取响应:
            Scanner responseScanner=new Scanner(inputStream);
            String response=responseScanner.next();

            System.out.println(response);


        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

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

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值