c++网络编程

news2024/11/17 3:40:43

网络编程模型

    c/s 模型:客户端服务器模型

    b/s 模型:浏览器服务器模型

1.tcp网络流程

在这里插入图片描述
服务器流程:

    1.创建套接字

    2.完善服务器网络信息结构体

    3.绑定服务器网络信息结构体

    4.让服务器处于监听状态

    5.accept阻塞等待客户端连接信号

    6.收发数据

    7.关闭套接字

客户端流程:

    1.创建套接字

    2.连接connect

    3.收发数据

    4.关闭套接字

2.函数

2.1socket

#include <sys/types.h>          
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
功能:创建套接字

参数:domain:通信域

                    Name                                 Purpose                                                Man page
                    AF_UNIX,AF_LOCAL          Local communication(本地通信)          unix(7)
                    AF_INET                             IPv4 Internet protocols                         ip(7)
                    AF_INET6                           IPv6 Internet protocols                         ipv6(7)
                    AF_PACKET                        原始套接字                                          packet(7)

             type:套接字的类型

                        SOCK_STREAM            TCP

                        SOCK_DGRAM              UDP使用

                        SOCK_RAW                    原始套接字使用

              protocol:附加文件,没有写0

返回值:成功:创建的新套接字(文件描述符)

              失败:-1  重置错误码

2.2bind

#include <sys/types.h>          
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:绑定套接字和网络信息结构体

参数:sockfd:socket函数产生的套接字文件描述符(socket函数返回值)

                      addr:避免冲突警告

2.3listen

#include <sys/types.h>          
#include <sys/socket.h>

int listen(int sockfd, int backlog);产生半连接队列

功能:将套接字设置成被动监听状态,只有这个状态的套接字才能等待客户端连接

参数:sockfd:socket函数返回值

           backlog:半连接队列的长度,一般传 5 10 都行 不是0就行

返回值:成功:0

              失败:-1  重置错误码

accept

#include <sys/types.h>          
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);产生全连接队列,返回值创                                                                                                        建的文件描述符
功能:提取半连接队列中的第一个客户端连接,成功,放到函数产生的全连接队列中,专门             用于和当前客户端通信,返回的套接字和原套接字类型相同

参数:sockfd:listen后的sockfd

           addr:客户端的网络信息结构体首地址,不关心写NULL

           addrlen:客户端addr长度,不关心写NULL

返回值:成功:创建的新的文件描述符  专门用于和当前客户端通信

              失败:-1  重置错误码

connect

#include <sys/types.h>          
#include <sys/socket.h>

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

功能:与服务器建立连接 

参数:sockfd:accept函数返回值

          addr:服务器的网络信息结构体

          addrlen:addr的长度

返回值:成功:0

              失败:-1  重置错误码

服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#define ERRLOG(msg) do{\
        printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
        perror(msg);\
        exit(-1);\
    }while(0)
 
 
int main(int argc, char const *argv[])
{
    //1.创建套接字
    int sockfd=0;//需要接函数返回值,后面函数会用
    if(-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))){
        ERRLOG("socket error");
    }
    
    //2.服务器网络信息结构体填充
    //struct sockaddr addr;
    struct sockaddr_in ad;
    memset(&ad, 0, sizeof(ad));
    ad.sin_family=AF_INET;
    ad.sin_port=htons(7788);//自己随意指定,不冲突就行,冲突就换
    ad.sin_addr.s_addr=inet_addr("192.168.250.100");
   
    //3.绑定套接字和服务器网络信息结构体
    if(-1==bind(sockfd,(struct sockaddr*)&ad,sizeof(ad))){
        ERRLOG("bind error");
    }
 
    //4.让套接字处于被动监听状态
    if(-1==listen(sockfd,5)){
        ERRLOG("listen error");
    }
    
 
    //5.阻塞等待客户端连接
    printf("正在等待客户端发来信息\n");
    int newfd=0;
    if((newfd=accept(sockfd,NULL,NULL))==-1){
        ERRLOG("accept error");
    }
    printf("客户端连接成功\n");
 
    //6.收发数据
    char buff[128]={0};
    int lnum=0;
    char opr=0;
    int rnum=0;
    int result=0;
    read(newfd,buff,128);
    printf("客户端发来数据:%s\n",buff);
    sscanf(buff,"%d%c%d",&lnum,&opr,&rnum);
    switch(opr){
        case '+':
            result=lnum+rnum;
            break;
        case '-':
            result=lnum-rnum;
            break;
        case '*':
            result=lnum*rnum;
            break;
        case '/':
            result=(float)lnum/(float)rnum;
            break;
    }
    memset(buff,0,128);
    sprintf(buff,"result=%d",result);
    write(newfd,buff,128);
 
    //7.关闭套接字
    close(sockfd);
    close(newfd);
    return 0;
}

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
 
#define ERRLOG(msg) do{\
        printf("%s %s %d\n", __FILE__, __func__, __LINE__);\
        perror(msg);\
        exit(-1);\
    }while(0)
 
int main(int argc, char const *argv[])
{
    //创建套接字
    int sockfd=0;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
        ERRLOG("socket error");
    }
 
    //填充服务器结构体
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(6666);
    addr.sin_addr.s_addr=inet_addr("192.168.2.84");
    
    //连接服务器
    if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr))==-1){
        ERRLOG("connect error");
    }
 
    //收发数据
    char buff[128]={0};
    //写入的数据从终端获取
    fgets(buff,128,stdin);
    buff[strlen(buff)-1]='\0';
    write(sockfd,buff,128);
 
    memset(buff,0,128);
    read(sockfd,buff,128);
    printf("服务器发来的信息是:%s\n",buff);
    
    //7.关闭套接字
    close(sockfd);
    return 0;
}

send

#include <sys/types.h>          
#include <sys/socket.h>

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

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

功能:发送数据

参数:sockfd:accept函数返回值

           buf:要发送的数据的缓冲区首地址

           len:要发送数据的长度

           flags:发送的标志位,0用法和write就一样了,MSG_DONTWAIT 表示非阻塞

返回值:成功:实际发送的字节数

              失败:-1  重置错误码

注意:

            如果对方关闭了套接字或者断开连接

            第一次send没有反应,第二次send会产生SIGPIPE信号

下面三种用法是等价的:

                write(sockfd, buf, 128);

                send(sockfd, buf, 128, 0);

                sendto(sockfd, buf, 128, 0, NULL, NULL);

recv

#include <sys/types.h>          
#include <sys/socket.h>

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

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);

功能:接收数据 

参数:

           sockfd:accept函数返回值

           buf:要接收的数据的缓冲区首地址

           len:要接收数据的长度

           flags:发送的标志位,flags:接收的标志位,0用法和read就一样了,                                                   MSG_DONTWAIT 表示非阻塞

返回值:成功:实际接收的字节数

              失败:-1  重置错误码

注意: 如果对方关闭了套接字或者断开连接 recv 会返回0

下面三种用法是等价的:

         read(sockfd, buf, 128);

         recv(sockfd, buf, 128, 0);

         recvfrom(sockfd, buf, 128, 0, NULL, NULL);

粘包问题产生原因

tcp在传输的时候不是一调用send就直接将数据发送给客户端的,而是将数据放到发送缓冲区里

tcp底层的nagle算法,会将一定短时间内发送的数据包组装成一个整体,发送给对方

而接受方无法区分消息的边界和类型,就可能会导致有冲突的情况发生

也就是说,当文件只剩最后一部分的时候,很有可能将最后的结束字符和文件最后一部分作为一个整体发送给接收方,也有可能将三个128和最后的30+12一起发给接收方,但不管是哪种方式,接收方写入的时候是以128为单位接受的,所以接收方缓冲区里是无法单独比较over的,所以会产生粘包问题。

解决方法:

1.既然是一定短时间内的数据成为一个整体,那么最后发送的over就不和前面一块发送了,在over之前加一个延时函数,让他单独发送

但是不常用,因为服务器里禁止使用sleep

2.发送定长的数据包,

解决方法一代码

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++那些事之template disambiguator

template disambiguator 1.背景 最近看到一段代码&#xff1a; auto chunk_left first_sort_key.template GetChunk<ArrayType>(left); 请问&#xff0c;这里的.template代表什么意义&#xff1f; 本节将从实际例子出发&#xff0c;探讨这个意义。 2.template disambigu…

mac不识别移动硬盘导致无法拷贝资源

背景 硬盘插入到Mac电脑上之后&#xff0c;mac不识别移动硬盘导致无法拷贝资源。 移动硬盘在Mac上无法被识别的原因可能有很多&#xff0c;多数情况下&#xff0c;是硬盘的格式与Mac电脑不兼容。 文件系统格式不兼容 macOS使用的文件系统是HFS或APFS&#xff0c;如果移动硬盘是…

【java】【面对对象高级4】内部类、枚举、泛型

目录 1、内部类 1.1 成员内部类【了解】 1.1.1 定义 1.1.2 扩展变量 1.2 静态内部类【了解】 1.2.1 定义 1.2.2 扩展变量 1.3 局部内部类【了解】 1.4 匿名内部类【重点】 1.4.1 定义 1.4.1.1 常规写法 1.4.1.2 匿名内部类改造 1.4.2 匿名内部类的常见使用场景 1.4.2…

超卖等高并发秒杀场景的问题及解决方案

超卖等高并发秒杀场景的问题及解决方案 1. 超卖问题&#xff08;多人秒杀&#xff09;1.1 原因1.2 解决方案1.3 总结 2. 锁失效问题&#xff08;单人重复抢&#xff09;2.1 原因2.2 解决方案 3. 事务边界问题&#xff08;单人重复抢&#xff09;3.1 原因3.2 解决方案3.3 总结 4…

【踩坑】三种方式解决 Homebrew failing to install - fatal: not in a git directory

问题描述 解决方法一 添加安全目录&#xff0c;没有测试。 git config --global --add safe.directory /opt/homebrew/Library/Taps/homebrew/homebrew- git config --global --add safe.directory /opt/homebrew/Library/Taps/homebrew/homebrew-cask 解决方法二 取消挂载这…

Redis 主从同步原理

一、什么是主从同步&#xff1f; 主从同步&#xff0c;就是将数据冗余备份&#xff0c;主库&#xff08;Master&#xff09;将自己库中的数据&#xff0c;同步给从库&#xff08;Slave&#xff09;。 从库可以一个&#xff0c;也可以多个&#xff0c;如图所示&#xff1a; 二…

Acwing.291 蒙德里安的梦想

题目 求把NM的棋盘分割成若干个12的的长方形&#xff0c;有多少种方案。 例如当N2&#xff0c;M4时&#xff0c;共有5种方案。当N2&#xff0c;M3时&#xff0c;共有3种方案。如下图所示: 输入格式 输入包含多组测试用例。 每组测试用例占一行&#xff0c;包含两个整数N和M…

STM32 CAN通讯实验程序

目录 STM32 CAN通讯实验 CAN硬件原理图 CAN外设原理图 TJA1050T硬件描述 实验线路图 回环实验 CAN头文件配置 CAN_GPIO_Config初始化 CAN初始化结构体 CAN筛选器结构体 接收中断优先级配置 接收中断函数 main文件 实验现象 补充 STM32 CAN通讯实验 CAN硬件原理图…

JavaScript的函数中this的指向

JavaScript的函数中this的指向 JavaScript 语言之所以有 this 的设计&#xff0c;跟内存里面的数据结构有关系。 以下例子来简单描述this在不同情况下所指向的对象。 var obj {aa: function(){console.log(this.num)},num: 5 };var aa obj.aa; var num 10;obj.aa(); // …

简要介绍 | 走向自然的身份认证:步态识别技术简介

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对步态识别进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 走向自然的身份认证&#xff1a;步态识别技术简介 Gait Recognition Based on Deep Learning: A Survey | ACM Computing Surveys 背景…

一文谈谈Git

"And if forever lasts till now Alright" 为什么要有git&#xff1f; 想象一下&#xff0c;现如今你的老师同时叫你和张三&#xff0c;各自写一份下半年的学习计划交给他。 可是你的老师是一个极其"较真"的人&#xff0c;发现你俩写的学习计划太"水&…

【弹力设计篇】聊聊异步通讯设计

为什么需要异步设计 刚开始参加工作&#xff0c;发现有一些API设计中回落数据之后&#xff0c;然后将数据写入到消息队列中&#xff0c;当时很是不理解为什么要这么做&#xff0c;直到后边系统学习消息队列之后才发现原来这其实就是异步处理&#xff0c;当流量很多的时候&…

一张表中几列字段以不同的条件规则去统计计数展示实现思路设计

今天在写一个业务的时候&#xff0c;遇到这样一个需求 一、需求描述 一张表中其中几列字段需要以不同的条件规则去统计计数&#xff0c;求实现方式 因为项目业务涉及隐私&#xff0c;我就想了一个类似的情景 二、情景描述 有一张月考成绩表&#xff0c;包含学生和他的各科…

区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归多输入单输出区间预测

区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归多输入单输出区间预测 目录 区间预测 | MATLAB实现QRBiGRU双向门控循环单元分位数回归多输入单输出区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现QRBiGRU双向门控循环单元分位数回归…

EXCEL,如何比较2个表里的数据差异(使用数据透视表)

目录 1 问题: 需要比较如下2个表的内容差异 1.1 原始数据喝问题 1.2 提前总结 2 使用EXCEL公式方法 2.1 新增辅助列&#xff1a; 辅助index 2.2 具体公式 配合条件格式 使用 3 数据透视表方法 3.1 新增辅助列&#xff1a; 辅助index 3.2 需要先打开 数据透视表向导 …

基于CNN卷积神经网络的调制信号识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 1. 卷积神经网络&#xff08;CNN&#xff09; 2. 调制信号识别 3.实现过程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心程序 % 构建调制类型…

支付宝短视频平台创作分成激励项目

没想到支付宝也开通了中视频计划&#xff0c;这波羊毛算是蒿定了&#xff0c;最近啊&#xff0c;马爸爸火速上线了支付宝创作分成计划&#xff0c;明显就是抄的抖音中视频计划&#xff0c;目前还在内测阶段&#xff0c;补贴的力度非常大&#xff0c;错过的话就只能拍大腿了&…

Prometheus 的应用服务发现及黑河部署等

目录 promtool检查语法 部署Prometheus Server 检查语法是否规范 部署node-exporter 部署Consul 直接请求API进行服务注册 使用register命令注册服务&#xff08;建议使用&#xff09; 单个和多个注册&#xff0c;多个后面多加了s 在Prometheus上做consul的服务发现 部署…

windows安装linux

https://www.cnblogs.com/liuqingzheng/p/16271895.html 咱们安装linux系统是centos7 准备工作&#xff1a; 安装软件&#xff1a;vmware -------虚拟机 官网下载地址&#xff1a;下载 VMware Workstation Pro | CN 也可以从这里面下载 链接&#xff1a;https://pan.bai…

MySQL优化(面试)

文章目录 通信优化查询缓存语法解析及查询优化器查询优化器的策略 性能优化建议数据类型优化索引优化 优化关联查询优化limit分页对于varchar end mysql查询过程: 客户端向MySQL服务器发送一条查询请求服务器首先检查查询缓存&#xff0c;如果命中缓存&#xff0c;则立刻返回存…