网络编程套接字之tcp(代码片段)

熬夜磕代码丶 熬夜磕代码丶     2023-03-02     529

关键词:

文章目录

一、TCP流套接字编程

我们来一起学习一下TCP socket api的使用,这个api与我们之前学习的IO流操作紧密相关,如果对IO流还不太熟悉的,可以看看这篇IO流操作

ServerSocket

顾名思义,ServerSocket是创建TCP服务器的Socket对象

构造方法作用
ServerSocket(int port)创建一个服务器套接字Socket,并指定端口号
方法作用
Socket accept()开始监听指定端口,有客户端连接时,返回一个服务端Socket对象,并基于该Socket对象与客户端建立连接,否则阻塞等待
void close()关闭此套接字

Socket

我们这里的Socket既是客户端的Socket,也可能是服务器接收到客户端连接后,返回的服务器Socket,不论是那个Socket,都是双方建立连接后,保存对方信息,进行收发数据的。

构造方法作用
Socket(String host,int port)创建一个客户端Socket对象,并与对应IP主机,对应端口的进程进行连接
方法作用
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回套接字的输入流
OutputStream getOutputStream()返回套接字的输出流

TCP长短连接

顾名思义,我们的TCP的长短连接,就表示我们TCP建立连接后,什么时候关闭连接就决定了是长连接还是短链接。
短连接: 在每次接收到数据并返回响应后,关闭连接。也就是说短连接只能收发一次数据。
长连接: 一直保持连接的状态,不关闭连接,双方可以不停的收发数据。

两者各有优缺,短连接适用于客户端请求频率不高的场景,浏览网页等。长连接适用于客户端与服务器频繁通信的场景,视频通话等。

二、TCP回显服务器客户端

服务器

有了昨天UDP实现的基础,今天我们的TCP实现已经有些部分就比较容易理解了。

public class EchoServer 
    private ServerSocket serverSocket = null;
    
    public EchoServer(int port) throws IOException 
        serverSocket = new ServerSocket(port);
    

先创建TCP服务器Socket对象,并指定端口号。

Socket clientSocket = serverSocket.accept();

与客户端进行连接,如果没有获取到连接,则会发生阻塞等待,每获取到一个客户端连接就会返回一个Socket对象,该Socket对象是专门负责与连接的客户端进行通信的。
我们获取到的每一个Socket对象,可能会进行多次通信,所以我们获取到连接后,将通信封装成一个方法。

private void processCoonection(Socket clientSocket) 
        System.out.printf("[%s:%d] 客户端上线\\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) 
            
        catch (IOException e) 
            e.printStackTrace();
        finally 
            try
                clientSocket.close();
            catch (IOException e) 
                e.printStackTrace();
            
        
    

我们先获取与输出输入流,因为需要释放我们直接将它们放到try()中,然后在finally里释放Socket对象资源。

Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) 
                    //数据已经读完了
                    System.out.printf("[%s:%d] 客户端下线\\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                
                String request = scanner.next();

我们将输入流封装到Scanner里,从控制台输入,然后判断控制台是否还有数据输入,如果没有数据输入就退出,然后获取客户端的请求。

/直接返回客户端的请求
                String response = process(request);
                //将输出流封装成打印流
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s;resp: %s\\n",clientSocket.getInetAddress().toString(), clientSocket.getPort(),
                        request, response);

public class EchoServer 
    private ServerSocket serverSocket = null;

    public EchoServer(int port) throws IOException 
        serverSocket = new ServerSocket(port);
    

    public void start() throws IOException 
        System.out.println("服务器启动!");
        while (true) 
            Socket clientSocket = serverSocket.accept();
            processCoonection(clientSocket);
        
    

    private void processCoonection(Socket clientSocket) 
        System.out.printf("[%s:%d] 客户端上线\\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) 
            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;resp: %s\\n",clientSocket.getInetAddress().toString(), clientSocket.getPort(),
                        request, response);
            
        catch (IOException e) 
            e.printStackTrace();
        finally 
            try
                clientSocket.close();
            catch (IOException e) 
                e.printStackTrace();
            
        
    

    public String process(String request) 
        return request;
    

我们现在实现的这个TCP server有个致命的缺点,一次只能处理一个客户端,等我们写完客户端再来分析。

客户端

public class EchoClient 
    private Socket socket = null;
    public EchoClient(String serverIp,int serverPort) throws IOException 
        //我们TCP的Socket对象能够识别点分十进制的IP
        //我们在创建对象的时候,就会与服务器进行连接
        socket = new Socket(serverIp,serverPort);
    

我们在new 这个对象的过程,就会触发TCP建立连接的过程,如果我们客户端没有这部分代码,那么服务器就会在accept进行阻塞等待。

public void start() 
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) 
            while (true) 
                System.out.print("> ");
                String request = scanner.next();
                if(request.equals("exit")) 
                    System.out.println("客户端退出!");
                    break;
                
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.flush();
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                System.out.println(response);
            
        catch (IOException e) 
            e.printStackTrace();
        
    

我们客户端的收发数据与服务器大差不差,这里就不在一一解释了。


分别启动客户端服务器程序。



可以成功的收发数据,但是我们当前代码有一个很严重的问题,服务器同一时刻只能处理一个连接,这样是很鸡肋的,我们对服务器进行更新。

并发服务器

我们看观察一下我们服务器的代码。

当我们客户端连接上这个服务器的时候,就执行到processConnection方法的while循环中,只要该方法不结束,我们的accpet就无法获取到第二个客户端socket对象。
那么我们如何解决这个问题呢?
使用多线程,我们的主线程专门负责进行accept,每收到一个连接,创建新线程,由新线程来负责处理这个新的客户端。

public void start() throws IOException 
        System.out.println("服务器启动!");
        while (true) 
            Socket clientSocket = serverSocket.accept();
            Thread t = new Thread(() -> 
                processCoonection(clientSocket);
            );
            t.start();
        
    

我们可以使用多线程来解决这个问题,但是现在每获取到一个连接就会创建一个线程,如果同一时刻连接过多,我们创建了大量线程,资源全部耗费在了线程切换上面了,我们可以使用线程池来提升效率。

public void start() throws IOException 
        System.out.println("服务器启动!");
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) 
            Socket clientSocket = serverSocket.accept();
            threadPool.submit(() -> 
                processCoonection(clientSocket);
            );
        
    

尽管我们使用了线程池了,但还是不够,如果我们的客户端非常多,而且都迟迟不断开,就会导致我们会有很多线程,对于我们来说是一个很大的负担。
能否有办法解决单机支持更大量客户端的问题呢?也是经典的C10M(单机处理1KW个客户端)问题
这里并不是说单机真正能处理1KW个客户端,只是表达说客户端的量非常大,针对我们上述多线程的版本,我们的机器是承受不了这么多线程的开销的,那么是否有办法一个线程处理很多客户端连接呢? 这就是IO多路复用,IO多路转接技术
给线程安排一个集合,这个集合放了一堆连接,我们线程负责监听集合,那个连接有数据来了,就处理那个连接。虽然我们的连接有很多,但是我们这里的连接并不是严格意义上的同时,也是有先后的,我们的操作系统里,提供了一些API,比如select,poll,epoll,我们的java里,也提供了一组NIO这样的类,封装了上述技术。

UDP与TCP

我们学习了TCP与UDP的网络编程后,来进行一个对比。
TCP:

UDP:

网络编程套接字之三tcp(代码片段)

目录1.ServerSocketAPI(给服务器端使用的类)2.SocketAPI(既给服务器使用,也给客户端使用)3.写TCP回显—服务器4.使用线程池后的TCP服务器代码(最终)5.写回显-客户端6.TCP回显—客户端代码7.运行回显服务器和客户... 查看详情

网络编程之tcp客户端开发和tcp服务端开发(代码片段)

开发TCP客户端程序开发步骤创建客户端套接字对象和服务端套接字建立连接发送数据接收数据关闭客户端套接字 importsocketif__name__==‘__main__‘:#创建tcp客户端套接字#1.AF_INET:表示ipv4#2.SOCK_STREAM:tcp传输协议tcp_client_socket=socket.so... 查看详情

网络编程套接字之tcp(代码片段)

文章目录一、TCP流套接字编程ServerSocketSocketTCP长短连接二、TCP回显服务器客户端服务器客户端并发服务器UDP与TCP一、TCP流套接字编程我们来一起学习一下TCPsocketapi的使用,这个api与我们之前学习的IO流操作紧密相关,如果... 查看详情

linux网络编程之套接字--tcp(代码片段)

...数🍨1.4、accept函数🍰1.5、connect函数🌺2、TCP网络编程🍡2.1、简单TCP通信程序--多进程版本🍢2.2、简单TCP通信程序--多线程版本🍧2.3、简单TCP通信程序--线程池版本🍀3、部署服务器🍡3.1、会话和进... 查看详情

python之socket编程(代码片段)

一、socket简介socket(套接字)是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口,将复杂的TCP/IP协议族隐藏在接口后面,让socket去组织数据以符合指定的协议。 下图左为socket在tcp/ip协议中的角色,右为socket的工作流... 查看详情

java网络编程之udp和tcp套接字(代码片段)

文章目录一.网络编程概述二.UDP网络编程1.UDP套接字2.UDP客户端回显服务器程序2.1UDP回显服务器2.2UDP客户端2.3UDP实现查词典的服务器三.TCP网络编程1.TCP套接字2.TCP客户端回显服务器程序2.1TCP回显服务器2.2TCP客户端2.3解决服务器无法... 查看详情

java网络编程编程之tcp编程和udp编程(代码片段)

网络编程基础1.TCP编程1.1网络相关基础概念1.2TCP协议介绍1.3TCP编程案例2.UDP编程2.1UDP协议介绍2.2UDP编程案例1.TCP编程1.1网络相关基础概念我们在学习网络编程前先来复习一下IP地址端口号协议,套接字的相关概念。IP地址:用来... 查看详情

网络编程之socket编程(代码片段)

套接字介绍socket介绍Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket... 查看详情

网络编程之tcp-socket(代码片段)

一.Socket  ?两个Java应用程产可通过一个双向的网络通信连接实现数据文换,这个及向链路的一端称   为个Socket。Socket通常用来实现client-server(网络释义:主从式架构)连接。   ?java.net包中定义的两个类So... 查看详情

javaweb实战15-计算机网络之网络编程套接字(代码片段)

文章目录一.网络编程中的基本概念1.1网络编程1.2客户端(client)/服务器(server)1.3请求(request)/响应(response)1.4客户端和服务器之间的交互数据1.4.1一问一答1.4.2多问一答1.4.3一问多答1.4.4多问多答二.socket套接字2.1UDP的SocketAPI2.1.1引子2.1.2... 查看详情

网络编程之基于udp协议套接字(代码片段)

1.UDP协议介绍(数据报协议)  UDP协议不同于TCP,通信时,它不需要事先建立双向链接,并且不区分客户端先启动还是服务端前起,工作原理:基于udp协议传输的信息,协议会将数据自动加上自定义的报头,这样每一个数据都是... 查看详情

网络编程之socket&serversocket(代码片段)

网络编程之Socket&ServerSocketSocket:网络套接字,网络插座,建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;socket用... 查看详情

python网络编程—socket套接字编程(tcp)(代码片段)

套接字介绍1.套接字:实现网络编程进行数据传输的一种技术手段2.Python实现套接字编程:importsocket3.套接字分类流式套接字(SOCK_STREAM):以字节流方式传输数据,实现tcp网络传输方案。(面向连接--tcp协议--可靠的--流式套接字)数据... 查看详情

ipc之socket的使用(代码片段)

...现,那当然用它来实现进程间通信更是不成问题。Socket即套接字,是一个对TCP/IP协议进行封装的编程调用接口(API)。通过Socket,我们才能在Andorid平台上通过TCP/IP协议进行开发。Socket不是一种协议,而是一个编程调用接口(API)... 查看详情

网络通信与信息安全之深入解析tcp与udp传输协议(代码片段)

...文原义是“孔”或“插座”。在编程中,Socket被称做套接字,是网络通信中的一种约定。Socket编程的应用无处不在,我们平时用的QQ、微信、浏览器等程序,都与Socket编程有关。那么我们使用浏览器查资料,这... 查看详情

网络linuxlinux网络编程-tcp,udp套接字编程及代码示范(代码片段)

这里写目录标题UDP类UDP服务端单执行流UDP客户端TCP类TCP单执行流服务器TCP客户端TCP多执行流(线程)TCP多执行流(进程)本文我们分为TCP和UDP协议网络程序的编写:他们的区别如下:项目特点UDP用户数据报协议,无需连接&#... 查看详情

网络编程套接字(tcp)(代码片段)

目录1、实现一个TCP网络程序(单进程版)        1.1、服务端serverTcp.cc文件                 服务端创建套接字                 服务端绑定                 服务端监听               ... 查看详情

tcp套接字编程常用函数(代码片段)

...h>intsocket(intfamily,inttype,intprotocol);//调用成功返回非负的套接字描述符,出错返回-1connect函数TCP客户端用connect函数来建立与TCP服务器的连接#include<sys/socket.h 查看详情