【C++】网络编程(TCPUDP)

news2024/10/5 17:46:40

网络编程是C++ API操作中很重要的一部分,包含TCP和UDP。

网络传输模型可以抽象为7个层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

但在使用TCP/IP协议时,可以简化为这4层:网络接口、网络层、传输层、应用层

名词

TCP:可靠传输,三次握手建立连接,传出去一定接受的到(如聊天软件);

UDP:不可靠传输,不需要建立连接,只管发送,实时性好(如视频会议);

套接字:表示通信的端点。就像用电话通信,套接字相当于电话,IP地址相当于总机号码,而端口号则相当于分机号码。

TCP

服务端创建流程:

  1. 调用socket函数创建监听socket
  2. 调用bind函数将socket绑定到某个IP和端口号组成的二元组上
  3. 调用listen函数开启监听
  4. 当有客户端连接请求时,调用accept函数接受连接,产生一个新的socket(与客户端通信的socket)
  5. 基于新产生的socket调用send或recv函数开始与客户端进行数据交流
  6. 通信结束后,调用close函数关闭socket

客户端创建流程:

  1. 调用socket函数创建客户端socket
  2. 调用connect函数尝试连接服务器
  3. 连接成功后调用send或recv函数与服务器进行数据交流
  4. 通信结束后,调用close函数关闭监听socket

服务端代码:

#include <iostream>
#include <sys/types.h>	//基本系统数据类型
#include <arpa/inet.h>	//网络信息转换
#include <unistd.h>		//POSIX系统API访问
#include <string.h>

using namespace std;
int main() {

    // 创建一个监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);	
	//常见的AF_INET──指定为IPv4协议,AF_INET6──指定为IPv6,AF_LOCAL──指定为UNIX 协议域
	//套接口可能的类型有:SOCK_STREAM字节流、SOCK_DGRAM数据报、SOCK_SEQPACKET有序分组、SOCK_RAW原始套接口
	//传输协议TCP/UDP,这里默认0
    if (listenfd == -1) {
        cout << " create listen socket error " << endl;
        return -1;
    }
    // 初始化服务器地址
    struct sockaddr_in bindaddr;
    bindaddr.sin_family = AF_INET;
    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    bindaddr.sin_port = htons(8081);
	//如果只想在本机上进行访问,bind函数地址可以使用本地回环地址
	//如果只想被局域网的内部机器访问,那么bind函数地址可以使用局域网地址
	//如果希望被公网访问,那么bind函数地址可以使用INADDR_ANY or 0.0.0.0
    if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {
        cout << "bind listen socket error" << endl;
        return -1;
    }
    // 启动监听
    if (listen(listenfd, SOMAXCONN) == -1) {
        cout << "listen error" << endl;
        return -1;
    }
	cout << "开始监听" << endl;

    while (true) {
        // 创建一个临时的客户端socket
        struct sockaddr_in clientaddr;
        socklen_t clientaddrlen = sizeof(clientaddr);
        // 接受客户端连接
        int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);
        if (clientfd != -1) {
            char recvBuf[32] = {0};
            // 从客户端接受数据
            int ret = recv(clientfd, recvBuf, 32, 0);
            if (ret > 0) {
                cout << "recv data from cilent , data:" << recvBuf << endl;
                // 将接收到的数据原封不动地发给客户端
                ret = send(clientfd, recvBuf, strlen(recvBuf), 0);
                if (ret != strlen(recvBuf)) {
                    cout << "send data error" << endl;
                } else {
                    cout << "send data to client successfully, data " << recvBuf <<endl;
                }
            } else {
                cout << "recv data error" <<endl;
            }
            close(clientfd);
        }
    }

    // 关闭监听socket
    close(listenfd);
    return 0;
}


客户端代码:

#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 8081
#define SEND_DATA "helloworld"

using namespace std;

int main() {
    // 创建一个socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1) {
        cout << " create client socket error " << endl;
        return -1;
    }
    // 连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
    serveraddr.sin_port = htons(SERVER_PORT);
    if (connect(clientfd, (struct sockaddr *)& serveraddr, sizeof(serveraddr)) == -1) {
        cout << "connect socket error" << endl;
        return -1;
    }
    // 向服务器发送数据
    int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);
    if (ret != strlen(SEND_DATA)) {
        cout << "send data error" << endl;
        return -1;
    } else {
        cout << "send data to client successfully, data " << SEND_DATA <<endl;
    }
    // 从服务器拉取数据
    char recvBuf[32] = {0};
    ret = recv(clientfd, recvBuf, 32, 0);
    if (ret > 0) {
        cout << "recv data to client successfully, data " << recvBuf <<endl;
    } else {
        cout << "recv data to client error" << endl;
    }
    // 关闭socket
    close(clientfd);
    return 0;
}

效果如下:

在这里插入图片描述

UDP

接收端创建流程:

  1. 创建套接字
  2. 将套接字绑定到一个本地地址和端口上(bind)
  3. 等待接受数据(recv)
  4. 关闭套接字。

发送端创建流程:

  1. 创建套接字
  2. 向服务器发送数据(send)
  3. 关闭套接字

UDP通信时,不强调server和client,重在实现两者互通;接收端需要bind,而发送端不需要。bind的一方只有接收到消息后才能开始发送。

设置两个端口实现互通:

接收端:

#include <sys/select.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstdlib>	//标准库头文件
#include <cstdio>	//c标准库
#include <cstring>	//c字符操作
#include <iostream>

int main(){

    //同一台电脑测试,需要两个端口
    int port_in  = 12321;
    int port_out = 12322;
    int sockfd;

    // 创建socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1==sockfd){
        return false;
        puts("Failed to create socket");
    }

    // 设置地址与端口
    struct sockaddr_in addr;
    socklen_t          addr_len=sizeof(addr);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;       // Use IPV4
    addr.sin_port   = htons(port_out);    //
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    // Time out
    struct timeval tv;
    tv.tv_sec  = 0;
    tv.tv_usec = 200000;  // 200 ms
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));

    // Bind 端口,用来接受之前设定的地址与端口发来的信息,作为接受一方必须bind端口,并且端口号与发送方一致
    if (bind(sockfd, (struct sockaddr*)&addr, addr_len) == -1){
        printf("Failed to bind socket on port %d\n", port_out);
        close(sockfd);
        return false;
    }

    char buffer[128];
    memset(buffer, 0, 128);

    int counter = 0;
    while(1){
        struct sockaddr_in src;
        socklen_t src_len = sizeof(src);
        memset(&src, 0, sizeof(src));

        int sz = recvfrom(sockfd, buffer, 128, 0, (sockaddr*)&src, &src_len);
        if (sz > 0){
            buffer[sz] = 0;
            printf("Get Message %d: %s\n", counter++, buffer);
        }
        else{
            puts("timeout");
        }
    }

    close(sockfd);
    return 0;
}

发送端:

#include<sys/select.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>

int main(){
    int port_in  = 12321;
    int port_out = 12322;
    int sockfd;

    // 创建socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1==sockfd){
        return false;
        puts("Failed to create socket");
    }

    // 设置地址与端口
    struct sockaddr_in addr;
    socklen_t          addr_len=sizeof(addr);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;       // Use IPV4
    addr.sin_port   = htons(port_in);    //
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    // Time out
    struct timeval tv;
    tv.tv_sec  = 0;
    tv.tv_usec = 200000;  // 200 ms
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));

    // 绑定获取数据的端口,作为发送方,不绑定也行
    if (bind(sockfd, (struct sockaddr*)&addr, addr_len) == -1){
        printf("Failed to bind socket on port %d\n", port_in);
        close(sockfd);
        return false;
    }

    int counter = 0;
    while(1){


        addr.sin_family = AF_INET;
        addr.sin_port   = htons(port_out);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);

        sendto(sockfd, "hello world", 11, 0, (sockaddr*)&addr, addr_len);
        printf("Sended %d\n", ++counter);
        sleep(1);
    }

    close(sockfd);
    return 0;
}

设置一个端口实现互通:

接收端:

//只有在server接收到消息后才能实现互发数据
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888//唯一端口号
int main(){
    int ser_sockfd;
    int len;
    fd_set rfds;
    socklen_t addrlen;
    char seraddr[100];
    struct sockaddr_in ser_addr;
    int retval, maxfd;
    //建立socket
    ser_sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(ser_sockfd<0){
        printf("I cannot socket success\n");
        return 1;
     }
    //设置地址与端口
    addrlen=sizeof(struct sockaddr_in);
    bzero(&ser_addr,addrlen);
    ser_addr.sin_family=AF_INET;
    ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    ser_addr.sin_port= htons (SERVER_PORT);
    //server绑定,才能接受client的数据
    if(bind(ser_sockfd,(struct sockaddr *)&ser_addr,addrlen)<0){
        printf("connect");
        return 1;
    }
    while(1){
        bzero(seraddr,sizeof(seraddr));
        len=recvfrom(ser_sockfd,seraddr,sizeof(seraddr),0,(struct sockaddr*)&ser_addr,&addrlen);
        /*显示client端的网络地址*/
        printf("receive from %s\n",inet_ntoa(ser_addr.sin_addr));
        /*显示客户端发来的字串*/
        printf("recevce:%s",seraddr);
        /*输入字串返回给client端*/
        while(1)
        {
             /*把可读文件描述符的集合清空*/
             FD_ZERO(&rfds);
             /*把标准输入的文件描述符加入到集合中*/
             FD_SET(0, &rfds);
             maxfd = 0;
             /*把当前连接的文件描述符加入到集合中*/
             FD_SET(ser_sockfd, &rfds);
             /*找出文件描述符集合中最大的文件描述符*/
             if(maxfd < ser_sockfd)
                maxfd = ser_sockfd;
             retval = select(maxfd+1, &rfds, NULL, NULL, NULL);
             if(FD_ISSET(ser_sockfd,&rfds))//client发消息来会出发进入
             {
             len=recvfrom(ser_sockfd,seraddr,sizeof(seraddr),0,(struct sockaddr*)&ser_addr,&addrlen);                   printf("recevce:%s",seraddr);
              }
                 if(FD_ISSET(0, &rfds))//键盘输入会触发进入
                 {
                     len=read(STDIN_FILENO,seraddr,sizeof(seraddr));
                     sendto(ser_sockfd,seraddr,len,0,(struct sockaddr*)&ser_addr,addrlen);
                 }
        }
    }
    return 0;
}

发送端:

#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888   //唯一端口
int main(int argc,char **argv){
    int cli_sockfd;
    int len;
    fd_set rfds;
    socklen_t addrlen;
    struct sockaddr_in cli_addr;
    char buffer[256];
    char buffer1[256];
    int retval, maxfd;
    //创建socket
    cli_sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if(cli_sockfd<0){
        printf("I cannot socket success\n");
        return 1;
    }
    //配置地址与端口
    addrlen=sizeof(struct sockaddr_in);
    bzero(&cli_addr,addrlen);
    cli_addr.sin_family=AF_INET;
    cli_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任何主机地址
    cli_addr.sin_port=htons(SERVER_PORT);
	//这里作为发送方,不需要绑定bind()
    while(1)
    {
        bzero(buffer,sizeof(buffer));
        /* 从标准输入设备取得字符串*/   len=read(STDIN_FILENO,buffer,sizeof(buffer));
        /* 将字符串传送给server端*/
        sendto(cli_sockfd,buffer,len,0,(struct sockaddr*)&cli_addr,addrlen);
        /* 接收server端返回的字符串*/
        while(1)
        {
                     /*把可读文件描述符的集合清空*/
                     FD_ZERO(&rfds);
                     /*把标准输入的文件描述符加入到集合中*/
                     FD_SET(0, &rfds);
                     maxfd = 0;
                     /*把当前连接的文件描述符加入到集合中*/
                     FD_SET(cli_sockfd, &rfds);
                     /*找出文件描述符集合中最大的文件描述符*/
                     if(maxfd < cli_sockfd)
                         maxfd = cli_sockfd;

                     retval = select(maxfd+1, &rfds, NULL, NULL, NULL);

                    if(FD_ISSET(cli_sockfd,&rfds))//server发来数据将会触发进入循环
                    {

                        len=recvfrom(cli_sockfd,buffer1,sizeof(buffer1),0,(struct sockaddr*)&cli_addr,&addrlen);
                        printf("receive: %s",buffer1);
                    }
                     if(FD_ISSET(0, &rfds))//键盘输入会触发进入
                     {
                         len=read(STDIN_FILENO,buffer,sizeof(buffer));
                         sendto(cli_sockfd,buffer,len,0,(struct sockaddr*)&cli_addr,addrlen);
                     }

        }
    }
    close(cli_sockfd);
    return 0;
}

在这里插入图片描述

以上。

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

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

相关文章

物理层基本概念

目录物理层的基本概念物理层传输方式串行传输并行传输同步传输异步传输单向通信&#xff08;单工&#xff09;双向交替通信&#xff08;半双工&#xff09;双向同时通信&#xff08;全双工&#xff09;编码与调制常用编码基本调制方法信道极限容量物理层的基本概念 物理层考虑的…

路由 OSPF常见4种网络类型MA、P2P、NBMA、P2MP、OSPF报头字段信息简介。

4.2.1 路由 OSPF&#xff08;OSPF常见4种网络类型、OSPF报头信息&#xff09; 目录OSPF常见的4种网络类型广播类型&#xff08;Broadcast 或 MA&#xff09;P2PNBMAP2MPOSPF报文发送形式对于不同OSPF网络类型的组网OSPF报头信息实际抓包分析OSPF常见的4种网络类型 OSPF应用于不…

Kali最强渗透工具- metasploit

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 metasploit是什么&#xff1f; msf是一款开源安全漏洞利用和测试工具&#xff0c;集成了…

C++GUI之wxWidgets(9)-编写应用涉及的类和方法(4)-事件处理(3)

目录动态事件处理如何处理事件事件如何向上传播事件处理程序链动态事件处理 void MyFrameHandler::OnFrameExit(wxCommandEvent&) {// Do something useful. }MyFrameHandler myFrameHandler;MyFrame::MyFrame() {Bind(wxEVT_MENU, &MyFrameHandler::OnFrameExit,&…

Java数组的定义与使用

Java数组的定义与使用 文章目录Java数组的定义与使用数组的基本概念什么是数组数组的创建数组的初始化数组的使用数组中元素访问遍历数组数组是引用类型初始JVM的内存分布基本类型变量与引用类型变量的区别引用变量几道例题认识null数组的应用场景保存数据作为函数的参数参数传…

TypeScript中的泛型

泛型&#xff08;Generics&#xff09;是指在定义函数、接口或类的时候&#xff0c;不预先指定具体的类型&#xff0c;而在使用的时候再指定类型的一种特性。 通常用T来指代任意输入的类型&#xff0c;除了T之外&#xff0c;以下是常见泛型变量代表的意思&#xff1a; K(Key…

信息安全技术 可信计算规范 可信平台控制模块 学习笔记(一)

声明 本文是学习信息安全技术 可信计算规范 可信平台控制模块. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 可信计算规范 缩略语 下列缩略语适用于本文件。 I/O&#xff1a;输入输出&#xff08;Input/Output&#xff09; IP&#xff1…

第三十八章 贪心算法——区间问题及证明(上)

第三十八章 贪心策略——区间相关问题一、什么贪心策略&#xff1f;二、区间问题合集1、思路&#xff1a;2、问题1&#xff1a; 区间选点&#xff08;1&#xff09;问题&#xff08;2&#xff09;思路和证明a.思路b.证明&#xff08;3&#xff09;代码3、问题2&#xff1a;&…

java:文件分片上传

代码下载地址&#xff1a;https://download.csdn.net/download/u013938578/87358484 1 文件上传、断点续传服务端 1.1 新建maven项目 文件结构如下&#xff1a; 1.2 引入百度开源上传组件webuploader 1.3 前端页面upload.html <!DOCTYPE html> <html lang"en&…

Java个人家乡博客源码

概述 个人博客相册家乡主题&#xff0c;用户注册后可以发布关于家乡的特色文章介绍&#xff0c;可以发布照片&#xff0c;相册管理&#xff0c;留言&#xff0c;评论&#xff0c;回复&#xff0c;收藏&#xff0c;关注 演示视频 https://www.bilibili.com/video/BV1iy4y1x7w6…

SSM框架-注解开发

11 注解开发 11.1 注解开发定义Bean 代码 接口BookDao public interface BookDao {void save(); }实现类BookDaoImpl【更改之处】 在最上面Component(“bookDao”) Component("bookDao") //或者Repository("bookDao") public class BookDaoImpl impl…

【C++】-- 哈希(上万字详细配图配代码从执行一步步讲解)

目录 哈希 常见哈希函数 除留余数法 哈希冲突 哈希冲突解决 闭散列 a、线性探测 插入 查找 删除 线性探测的实现代码 b、二次探测 二次探测的实现 开散列 开散列实现 插入 查找 删除 析构函数 代码汇总 哈希 常见哈希函数 直接定址法 -- (常用)-- 不存在哈…

2022 年博客总结

时间过的飞快&#xff0c;孩子也快4岁了&#xff0c;1号带孩子去玩雪&#xff0c;发生了一件有趣的事&#xff0c;发个视频。 带孩子玩雪我拉着闺女&#xff0c;闺女拉着儿子&#xff0c;忽略了力的作用&#xff0c;我以为只有我在使劲&#xff0c;实际上闺女需要需要更大的力拉…

java8新特性——函数式编程

文章目录1.函数式编程思想1.1概念1.2函数式编程的思想2.Lambda表达式2.1概述2.2核心原则2.3基本格式2.4Lambda表达式练习2.5省略规则3.Stream流3.1概述3.2案例准备3.3Steam流操作案例3.3.1需求3.3.2实现3.4Stream常用操作3.4.1创建stream流方式3.4.2中间操作3.4.2.1filter3.4.2…

SpringMVC 底层机制的简易实现

SpringMVC 底层机制的简易实现项目基础配置 xml 文件开发指南开发步骤1.初始化数据2.中央控制器 - 分发请求3.开发者角度4.视图解析器开发总结项目基础 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId&g…

【Python基础】常用数据结构及处理

1. KeyValue dict.clear() 删除字典内所有元素dict.copy()返回一个字典的浅复制[dict.fromkeys(seq, val])创建一个新字典&#xff0c;以序列 seq 中元素做字典的键&#xff0c;val 为字典所有键对应的初始值 dict.get(key, defaultNone) 返回指定键的值&#xff0c;如果值不在…

prompt模型详解之文本生成

prompt在生成方面的应用从两个方面进行介绍&#xff1a; 评估手段 具体任务 评估手段 生成任务的评估手段主要分为四种类型&#xff1a; 1). 基于N-gram匹配 2). 基于编辑距离 3). 基于词向量 4). 基于可学习方式。 本小节主要介绍BARTSCORE&#xff0c;其使用prompt方…

Python杂题

目录 一、前言 二、例题1——修剪灌木 三、例题2—— 付账问题 四、例题3——最少砝码 五、例题四——矩形拼接 六、例题五——蜂巢 一、前言 竞赛题有很多不需要什么算法的题目&#xff0c;只要学过编程语言就能做&#xff0c;其考核思维、逻辑、编码能力。而这种题有“…

【算法题解】 8. K 个一组翻转链表

文章目录题目解题思路代码实现复杂度分析题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持…

Python中编码(encode)解码(decode)讲解

嗨害大家好鸭&#xff01;我是小熊猫~ 这次也是给大家带来一点干货~ 所用素材:点击此处跳转文末名片获取 一、python3中str与unicode 在python3中&#xff0c;字符串有两种形式&#xff1a;str和bytes&#xff0c;两者区别如下&#xff1a; unicode string(str类型)&#xf…