【Linux】网络---->套接字编程(TCP)

套接字编程TCP

  • TCP的编程流程
  • TCP的接口
  • TCP的代码(单线程、多进程、多线程代码)
    • 单线程
    • 多进程
    • 多线程

TCP的编程流程

在这里插入图片描述
TCP的编程流程:大致可以分为五个过程,分别是准备过程、连接建立过程、获取新连接过程、消息收发过程和断开过程。

1.准备过程:服务端和客户端需要创建各自的套接字,除此之外服务端还需要绑定自己的地址信息和进行监听。注意:服务端调用listen函数后,处理监听状态,这时由socket函数返回的描述符被称为”监听套接字“,本质上是一个文件描述符,但是客户端没有”监听套接字‘这个概念。
2.连接建立过程:三次握手的过程,请注意,三次握手需要在服务端开始监听之后才能进行的,并且在应用层只需要程序员调用connect接口就可以了,剩下的三次握手的具体过程是在操作系统内核进行的,无需程序员干预
3.获取新连接过程:当连接建立好之后,建立好的连接会被放入已完成连接队列,在服务端只需要调用accept接口获取新的套接字即可。注意:如果没有调用accept接口,新的套接字不会被拿到应用层。也就没有真正意义上的建立连接
4.消息收发过程:当本端给对端发送消息时,将消息发送后,对端会回复一个确认消息。这个确认消息并不是程序员调用发送接口发送的,而是TCP协议自己发送的。
5.关闭过程:调用close接口后,就会关闭本端的新连接套接字。

对于连接建立过程来说:当某个客户端发送了连接请求后,还处于正在建立连接的过程中,就会处于未完成连接队列,而已经完成了建立连接这个过程的,就处于已完成连接队列。accept函数就是从已完成连接队列中拿新连接套接字的。

在这里插入图片描述

TCP的接口

对于创建套接字和绑定的操作接口,在【Linux】网络---->套接字编程UDP中已介绍,这里就只介绍和UDP不同的接口。

监听:当TCP服务端调用listen函数时,属于listen状态,标志着服务端可以正常接收客户端的请求了。

int listen(int sockfd, int backlog);

参数:
sockfd:套接字描述符,也就是socket函数的返回值,被称为”监听套接字“
backlog:实际含义是:已完成连接队列的大小。引申含义是:TCP并发连接数:TCP瞬时能处理TCP连接的最大数量(瞬时没有调用accept函数)。当服务端不进行accept,服务端最多能建立连接的个数为backlog+1。
可以通过/proc/sys/net/ipv4/tcp_max_syn_backlog修改未完成连接队列的大小。

注意:TCP并发连接数不等同于TCP服务端最多能够处理多少个TCP连接

问题:那么TCP服务端到底最多能够处理多少个TCP连接呢?
这个问题其实就是TCP服务端到底能够打开多少个文件描述符?

而到底能够打开多少个文件描述符,实际上是可以通过命令查看的,也可以通过命令修改,但是当超过某个值后,硬件就限制了其无法真正的打开n多个文件描述符。

accept获取新连接套接字

//这个函数是一个阻塞函数,如果没有已完成连接的连接,则阻塞等待,如有则返回
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:
sockfd:套接字描述符
addr:地址信息结构体,描述客户端地址信息的结构体
addrlen:地址信息长度
返回值:成功返回新连接的套接字,失败返回-1

注意:返回的新连接套接字是后续客户端和服务端进行通信的。而监听套接字是在准备阶段,连接建立阶段和获取连接阶段使用的。

在这里插入图片描述

意味着:监听套接字负责接收来自各个客户端连接请求的,而连接完成,服务端进行accept之后,会为每一个客户端创建一个独有的专门的新套接字。服务端通过新连接套接字和对应的客户端进行通信。

由客户端调用connect函数

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:
sockfd:套接字描述符(客户端)
addr:描述服务端的地址信息(服务端的ip和端口)
addrlen:地址信息长度
返回值:成功返回0,失败返回-1

注意:connect函数也可以绑定客户端的地址信息。(如果客户端没有进行绑定)

发送和接收

ssize_t send(int sockfd, const void* buf, size_t len, int flags);

参数:
sockfd:套接字描述符
buf:发送buf指向空间的内容
len:发送数据的长度
flags:0阻塞发送

注意:返回值很重要,成功返回发送的字节数,失败返回-1。

ssize_t recv(int sockfd, void* buf, size_t len, int flags);

参数:
sockfd:新连接套接字描述符
服务端:新连接套接字
客户端:socket 函数的返回值
buf,将接收到的数据存放在buf指定的空间,空间需要提前开辟好
len:期望接收的字节个数
flags:0阻塞接收

注意:返回值很重要,成功返回接收到的字节个数,接收失败返回-1, 对端关闭连接0

TCP的代码(单线程、多进程、多线程代码)

单线程

注:由于未使用多路转接,所以无法使用单线程完成—>既要accept又要send和recv,因此这个代码是有bug的。
服务端代码:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
  /*
   * 1.创建套接字
   * 2.绑定地址信息
   * 3.监听
   * 4.通信
   * 5.关闭
   * */
  int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(listen_sockfd < 0)
  {
    perror("socket");
    return 0;
  }

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(27878);
  addr.sin_addr.s_addr = inet_addr("0.0.0.0");

  int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
  if(ret < 0)
  {
    perror("bind");
    return 0;
  }
  ret = listen(listen_sockfd, 5);
  if(ret < 0)
  {
    perror("listen_sockfd");
    return 0;
  }
  while(1)
  {
    int newsockfd = accept(listen_sockfd, NULL, NULL);
    if(newsockfd < 0)
    {
      return 0;
    }
    printf("newsockfd:%d\n", newsockfd);
  
    //通信过程
    char buf[1024] = {0};
    ssize_t r_size = recv(newsockfd, buf, sizeof(buf)-1, 0);
    if(r_size < 0)
    {
      continue;
    }
    else if(r_size == 0)
    {
      printf("%d connect shutdown\n", newsockfd);
      close(newsockfd);
    }else{
      printf("client say : %s\n", buf);
      send(newsockfd, buf, strlen(buf), 0);
    }
  }
  while(1)
  {
    sleep(1);
  }

  return 0;
}

客户端代码:

#include<stdio.h>
#include<unistd.h>
#include<iostream>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

using namespace std;
int main()
{
  /*
   * 1.创建套接字
   * 2.发起连接
   * */
  int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(sockfd < 0)
  {
    perror("socket");
    return 0;
  }

  /*要描述服务端的ip和port*/
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(27878);
  addr.sin_addr.s_addr = inet_addr("10.0.12.14");

  int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
  if(ret < 0)
  {
    perror("connect");
    return 0;
  }
  while(1)
  {
    char buf[1024] = {0};
    printf("please enter: ");
    fflush(stdout);
    cin >> buf;
    send(sockfd, buf, strlen(buf), 0);

    memset(buf, '\0', sizeof(buf));
    ssize_t r_size = recv(sockfd, buf, sizeof(buf)-1, 0);
    if(r_size < 0)
    {
      continue;
    }
    else if(r_size == 0)
    {
      printf("%d connect shutdown\n", sockfd);
      close(sockfd);
    }else{
      printf("server recall ------- %s\n", buf);
    }
  }

  return 0;
}

多进程

注:由于未使用多路转接,所以对于服务端来说,只能通过创建多个进程来实现父进程accept,子进程和客户端进行通信
服务端代码:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>


void signalcallback(int sig)
{
  wait(NULL);
}

int main()
{
  signal(SIGCHLD, signalcallback);
  /*
   * 1.创建套接字
   * 2.绑定地址信息
   * 3.监听
   * 4.通信
   * 5.关闭
   * */
  int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(listen_sockfd < 0)
  {
    perror("socket");
    return 0;
  }

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(27878);
  addr.sin_addr.s_addr = inet_addr("0.0.0.0");

  int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
  if(ret < 0)
  {
    perror("bind");
    return 0;
  }
  ret = listen(listen_sockfd, 5);
  if(ret < 0)
  {
    perror("listen_sockfd");
    return 0;
  }
  while(1)
  {
    int newsockfd = accept(listen_sockfd, NULL, NULL);
    if(newsockfd < 0)
    {
      return 0;
    }
    printf("newsockfd:%d\n", newsockfd);
    
    /*
     * 1.创建子进程
     * 2.子进程和客户端通信
     * */

    //通信过程
    
    pid_t pid = fork();
    if(pid < 0)
    {
      close(newsockfd);//客户端通过接收的返回值可以感知到连接断开
      continue;
    }else if(pid == 0)
    {
      //子进程进行通信
      
      close(listen_sockfd);
      while(1)
      {
        char buf[1024] = {0};
        ssize_t r_size = recv(newsockfd, buf, sizeof(buf)-1, 0);
        if(r_size < 0)
        {
          continue;
        }
        else if(r_size == 0)
        {
          printf("%d connect shutdown\n", newsockfd);
          close(newsockfd);
          exit(1);  //结束子进程
        }else{
      }
        printf("client say : %s\n", buf);
      }
    }
    else{
      //父进程
      //自定义SIGCHLD信号,让父进程可以继续accept
    }
  }
  return 0;
}

多线程

注:由于未使用多路转接,因此除了使用多进程之外,还可以使用多线程进行,让主线程进行accept,工作线程和客户端通信。
服务端代码:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

struct NewSockfd
{
  int _sockfd;
};

void* thread_worker(void* arg)
{
  struct NewSockfd* wokerfd = (struct NewSockfd*)arg;
  int newsockfd = wokerfd->_sockfd;
  pthread_detach(pthread_self());

  //通信过程
  while(1)
  {
    char buf[1024] = {0};
    ssize_t r_size = recv(newsockfd, buf, sizeof(buf)-1, 0);
    if(r_size < 0)
    {
      continue;
    }
    else if(r_size == 0)
    {
      printf("%d connect shutdown\n", newsockfd);
      close(newsockfd);
      delete wokerfd;
      pthread_exit(NULL); //结束线程
    }else{
        printf("client say : %s\n", buf);
        send(newsockfd, buf, strlen(buf), 0);
    }

  return NULL;
}

void signalcallback(int sig)
{
  wait(NULL);
}

int main()
{
  signal(SIGCHLD, signalcallback);
  /*
   * 1.创建套接字
   * 2.绑定地址信息
   * 3.监听
   * 4.通信
   * 5.关闭
   * */
  int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(listen_sockfd < 0)
  {
    perror("socket");
    return 0;
  }

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(27878);
  addr.sin_addr.s_addr = inet_addr("0.0.0.0");

  int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
  if(ret < 0)
  {
    perror("bind");
    return 0;
  }
  ret = listen(listen_sockfd, 5);
  if(ret < 0)
  {
    perror("listen_sockfd");
    return 0;
  }
  while(1)
  {
    int newsockfd = accept(listen_sockfd, NULL, NULL);
    if(newsockfd < 0)
    {
      return 0;
    }
    printf("newsockfd:%d\n", newsockfd);
    
    /*
     * 1.创建工作线程
     * 2.工作线程和客户端通信
     * */

    struct NewSockfd* fd = new struct NewSockfd;
    fd->_sockfd = newsockfd;
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, thread_worker, (void*)fd);
    if(ret < 0)
    {
      close(newsockfd);
      continue;
    }


    //通信过程
 
      }
    }
  return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/18830.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《花雕学AI》ChatGPT 的 Prompt 用法,不是随便写就行的,这 13 种才是最有效的

ChatGPT 是一款基于 GPT-3 模型的人工智能写作工具&#xff0c;它可以根据用户的输入和要求&#xff0c;生成各种类型和风格的文本内容&#xff0c;比如文章、故事、诗歌、对话、摘要等。ChatGPT 的强大之处在于它可以灵活地适应不同的写作场景和目的&#xff0c;只要用户给出合…

MySQL多表查询之连接查询

0. 数据源 /*Navicat Premium Data TransferSource Server : localhost_3306Source Server Type : MySQLSource Server Version : 80016Source Host : localhost:3306Source Schema : tempdbTarget Server Type : MySQLTarget Server Version…

两小时让你全方位的认识文件(一)

想必友友们在生活中经常会使用到各种各样的文件&#xff0c;那么我们是否了解它其中的奥秘呢&#xff0c;今天阿博就带领友友们深入地走入文件&#x1f6e9;️&#x1f6e9;️&#x1f6e9;️ 文章目录 一.为什么使用文件二.什么是文件三.文件的打开和关闭四.文件的顺序读写 一…

时间复杂度

学习《代码随想录》 时间复杂度为什么要引入时间复杂度和空间复杂度&#xff1f;什么是时间复杂度&#xff1f;这个O是什么意思&#xff1f;时间复杂度越低越好&#xff1f; 内存管理什么是内存空间&#xff1f;&#xff08;C为例&#xff09;为什么总说C/C更偏向底层&#xff…

T-SQL游标的使用

一.建表 INSERT INTO cloud VALUES( 你 ) INSERT INTO cloud VALUES( 一会看我 ) INSERT INTO cloud VALUES( 一会看云 ) INSERT INTO cloud VALUES( 我觉得 ) INSERT INTO cloud VALUES( 你看我时很远 ) INSERT INTO cloud VALUES( 你看云时很近 ) 二.建立游标 1.游标的一般格…

判断大小端的错误做法

这里不详细讲解大小端的区别&#xff0c;只讲解判断大小端的方法。 1.大端&#xff0c;小端的区别 0x123456 在内存中的存储方式 大端是高字节存放到内存的低地址 小端是高字节存放到内存的高地址 2.大小端的判断 1.错误的做法 int main() {int a0x1234;char c(char)a;if(…

2022年宜昌市网络搭建与应用竞赛样题(三)

网络搭建与应用竞赛样题&#xff08;三&#xff09; 技能要求 &#xff08;总分1000分&#xff09; 竞赛说明 一、竞赛内容分布 “网络搭建与应用”竞赛共分三个部分&#xff0c;其中&#xff1a; 第一部分&#xff1a;网络搭建及安全部署项目&#xff08;500分&#xff0…

基于SpringBoot3从零配置SpringDoc

为了方便调试&#xff0c;更好的服务于前后端分离式的工作模式&#xff0c;我们给项目引入Swagger。 系列文章指路&#x1f449; 系列文章-基于SpringBoot3创建项目并配置常用的工具和一些常用的类 文章目录 1. SpringFox2. SpringDoc2.1 引入依赖2.2 配置文件2.3 语法2.4 使…

PCL学习八:Keypoints-关键点

参考引用 Point Cloud Library黑马机器人 | PCL-3D点云 PCL点云库学习笔记&#xff08;文章链接汇总&#xff09; 1. 引言 关键点也称为兴趣点&#xff0c;它是 2D 图像或 3D 点云或曲面模型上,可以通过检测标准来获取的具有稳定性、区别性的点集。从技术上来说&#xff0c;关键…

Microsoft Edge新功能测评体验

Microsoft Edge使用体验 Microsoft Edge是一款现代化的浏览器&#xff0c;它拥有众多功能和强大的性能&#xff0c;为用户带来更加流畅的浏览体验。 Edge最近推出了分屏功能&#xff0c;支持一个窗口同时显示两个选项卡&#xff0c;这可以大大提高生产力和多任务处理能力。 一…

API接口对程序员的帮助有哪些,参考值简要说明

API接口对程序员的帮助有哪些 提高开发效率&#xff1a;通过API接口&#xff0c;程序员能够在不用重复编写代码的情况下&#xff0c;直接获取其他应用程序提供的服务或数据&#xff0c;极大地提高了开发效率。 减少错误率&#xff1a;使用API接口可以避免手动输入数据容易出现…

洛谷P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II(离线区间逆序对+莫队二次离线)

题目 给你一个长为n(1<n<1e5)的序列a(0<ai<1e9)&#xff0c; m(1<m<1e5)次询问&#xff0c;每次查询一个区间[l,r]的逆序对数&#xff0c;可离线。 思路来源 登录 - 洛谷 三道经典分块题的更优复杂度解法&[Ynoi2019模拟赛]题解 - 博客 - OldDriverT…

Flutter性能分析工具使用

使用前提 flutter常用的性能分析工具&#xff0c;这些工具都集成在android studio中&#xff0c;基本能满足我们的需求了。在下面介绍的几个工具中&#xff0c;Flutter Performance和Flutter Inspector都能够直接在debug模式下使用&#xff0c;但是DevTools只能在profile模式下…

typescript:熟练掌握typescript

一、简介 TypeScript 教程 | 菜鸟教程 TypeScript (简称:TS)是JavaScript的超集 (JS有的TS 都有)。 TypeScriptType JavaScript (在JS 基础之上&#xff0c;为JS添加了类型支持)。 哔哩哔哩_教程_TypeScript 二、TypeScript为什么要为js增加类型支持&#xff1f; 背景&am…

4年外包出来,5次面试全挂....

我的情况 大概介绍一下个人情况&#xff0c;男&#xff0c;毕业于普通二本院校非计算机专业&#xff0c;18年跨专业入行测试&#xff0c;第一份工作在湖南某软件公司&#xff0c;做了接近4年的外包测试工程师&#xff0c;今年年初&#xff0c;感觉自己不能够再这样下去了&…

鲲鹏展翅 信安高飞 | 鲲鹏开发者峰会2023-麒麟信安技术论坛成功举办!

2023年5月6日-7日&#xff0c;以“创未来 享非凡”为主题的鲲鹏开发者峰会2023在东莞松山湖举办。鲲鹏产业生态繁荣&#xff0c;稳步发展&#xff0c;正在成为行业核心场景及科研领域首选&#xff0c;加速推动数字化转型。 作为鲲鹏生态重要合作伙伴&#xff0c;麒麟信安受邀举…

Notion Ai中文指令使用技巧

Notion AI 是一种智能技术&#xff0c;可以自动处理大量数据&#xff0c;并从中提取有用的信息。它能够 智能搜索&#xff1a;通过搜索文本和查询结果进行快速访问 自动归档&#xff1a;可以根据关键字和日期自动将内容归档 内容分类&#xff1a;可以根据内容的标签和内容的…

交互式数据分析和处理新方法:pandas-ai =Pandas + ChatGPT

Python Pandas是一个为Python编程提供数据操作和分析功能的开源工具包。这个库已经成为数据科学家和分析师的必备工具。它提供了一种有效的方法来管理结构化数据(Series和DataFrame)。 在人工智能领域&#xff0c;Pandas经常用于机器学习和深度学习过程的预处理步骤。Pandas通…

基于JavaWeb实现的汽车维修管理系统

【简介】 本系统基于springboot mybatis jps架构开发&#xff0c;前后端分离&#xff0c;开发环境为jdk1.8、mysql、maven。系统功能主要分为汽车维修管理、配件管理、财务管理、基础数据管理、系统维护5大模块。 【功能结构】 【技术架构】 系统架构&#xff1a;springboot …

深度学习第J6周:ResNeXt-50实战解析

目录 一、模型结构介绍 二、前期准备 三、模型 三、训练运行 3.1训练 3.2指定图片进行预测 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] &#x1f4cc; …