网络编程day02(字节序、TCP编程)

news2024/11/18 3:44:54

目录

【1】字节序

1》大小端转换

2》端口转换 

 3》IP地址转换

主机字节序转换为网络字节序 (小端序->大端序)

网络字节序转换为主机字节序(大端序->小端序)

 【2】TCP编程

1》流程

2》函数接口

1> socket

2> bind

3> listen

4> accept

 5> recv

 6> connect

7> send

 3》代码展示

1>服务器代码

2> 客户端代码

 4》改进版

 1> 服务器代码

2> 客户端代码


【1】字节序

1》大小端转换

字节序:不同类型的cpu主机,内存存储大于一个字节类型的数据在内存中的存放顺序。

分类:

小端序(little-endian) - 低序字节存储在低地址 (主机字节序)

大端序(big-endian)- 高序字节存储在低地址 (网络字节序)

网络中传输的数据必须使用网络字节序,即大端字节序

终端显示的数据必须是主机字节序,即小端字节序

 写一个程序,判断当前主机的字节序

测试方式:共用体,数据类型强转,指针强转

共用体:

#include <stdio.h>
 
union w
{
   short a;
   char b;
}c;
 
 
int main(int argc, char const *argv[])
{
    c.a = 0x12345678;
    printf("%#x\n",c.b);      //0x78
    return 0;
}

 数据类型强转、指针强转:

#include <stdio.h>

int main()
{
int a = 0x12345678;
printf("数据类型强转: %#x\n",(char)a);
char *p=(char *)&a;
printf("指针强转:%#x\n",*p);
return 0;
}

网络传输中,需要将每个主机的主机字节序(CPU决定),转换为网络中统一顺序的网络字节序,才能供双方主机去识别。

只需要转换IP和port就可以,不需要转换传输的数据包的字节序,因为IP和port为 4个字节和2个字节, 而数据报一般都为char类型, 占一个字节,根据字节序的性质,内存存储大于一个字节类型的数据在内存中的存放顺序,所以char类型并不具有字节序的概念。

2》端口转换 

主机字节序转换为网络字节序(小端序-->大端序)

u_long htonl (u_long hostlong); //host to internet long

u_short htons (u_short short); //掌握这个

网络字节序转换为主机字节序(大端序-->小端序)

u_long ntohl (u_long hostlong);

u_short ntohs (u_short short);

 

 3》IP地址转换

主机字节序转换为网络字节序 (小端序->大端序)

in_addr_t inet_addr(const char *strptr); //该参数是字符串

typedef uint32_t in_addr_t;

struct in_addr {

in_addr_t s_addr;

};

功能: 主机字节序转为网络字节序

参数: const char *strptr: 字符串

返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示),

否则NULL

网络字节序转换为主机字节序(大端序->小端序)

char *inet_ntoa(stuct in_addr inaddr);

功能: 将网络字节序二进制地址转换成主机字节序。

参数: stuct in_addr in addr : 只需传入一个结构体变量

返回值: 返回一个字符指针, 否则NULL;

 【2】TCP编程

(C/S架构:client server         B/S架构:browser server)

1》流程

服务器-------------------------------------------------------------------》接电话者

1> 创建套接字(socket)---------------》有手机

2> 指定网络信息---------------------------》有号码

3> 绑定套接字(bind)------------------》绑定手机(插卡)

4> 监听套接字(listen)-----------------》待机

5> 接收客户端连接连接请求(accept)--》接电话

6> 接收、发送数据(recv send)---》通话

7> 关闭套接字(close)-----------------》挂电话

客户端-------------------------------------------------------------------》打电话者

1> 创建套接字(socket)------------》有手机

2> 指定(服务器)网络信息--------》有对方的号码

3> 连接(connect)-------------------》拨打电话

4> 接收发送消息(recv send)---》通话

5> 关闭套接字(close)------------》挂电话

2》函数接口

1> socket

int socket(int domain, int type, int protocol);

功能:创建套接字

参数:

domain:协议族

          AF_UNIX, AF_LOCAL        本地通信

          AF_INET                             IPv4

          AF_INET6                           IPv6

type:套接字类型

          SOCK_STREAM:流式套接字

          SOCK_DGRAM:数据报套接字

          SOCK_RAW:原始套接字

protocol:协议 - 填0 自动匹配底层 ,根据type

系统默认自动帮助匹配对应协议

传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP

网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)

返回值:

成功 文件描述符

失败 -1,更新errno

2> bind

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

功能:绑定

参数:

socket:套接字

addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信当时socket第一个参数确定)

addrlen:结构体大小

返回值:成功 0 失败-1,更新errno

通用结构体:

struct sockaddr {

sa_family_t sa_family;

char sa_data[14];

}

ipv4通信结构体:

struct sockaddr_in {

sa_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

};

struct in_addr {

uint32_t s_addr;

};

本地通信结构体:

struct sockaddr_un {

sa_family_t sun_family; /* AF_UNIX */

char sun_path[108]; /* pathname */

};

3> listen

int listen(int sockfd, int backlog);

功能:监听,将主动套接字变为被动套接字

参数:

sockfd:套接字

backlog:同时响应客户端请求链接的最大个数,不能写0.

不同平台可同时链接的数不同,一般写6-8个

(队列1:保存正在连接)

(队列2,连接上的客户端)

返回值:成功 0 失败-1,更新errno  

4> accept

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

accept(sockfd,NULL,NULL);

功能:阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,

则accept()函数返回,返回一个用于通信的套接字文件;

参数:

Sockfd :套接字

addr: 链接客户端的ip和端口号

如果不需要关心具体是哪一个客户端,那么可以填NULL;

addrlen:结构体的大小

如果不需要关心具体是哪一个客户端,那么可以填NULL;

返回值:

成功:文件描述符; //用于通信

失败:-1,更新errno

 5> recv

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

功能: 接收数据

参数:

sockfd: acceptfd ;

buf 存放位置

len 大小

flags 一般填0,相当于read()函数

MSG_DONTWAIT 非阻塞

返回值:

< 0 失败出错 更新errno

==0 表示客户端退出

>0 成功接收的字节个数

 6> connect

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

功能:用于连接服务器;

参数:

sockfd:socket函数的返回值

addr:填充的结构体是服务器端的;

addrlen:结构体的大小

返回值

-1 失败,更新errno

正确 0

7> send

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

功能:发送数据

参数:

sockfd:socket函数的返回值

buf:发送内容存放的地址

len:发送内存的长度

flags:如果填0,相当于write();

 3》代码展示

1>服务器代码

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    // 1.创建套接字(socket)---------------》有手机
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0)
    {
        perror("socket err\n");
        return -1;
    }
    printf("socketfd: %d\n", socketfd);
    // 2.指定(自己)网络信息--------------------》有号码
    // struct sockaddr_in
    // {
    //     sa_family_t sin_family;  /* address family: AF_INET */
    //     in_port_t sin_port;      /* port in network byte order */
    //     struct in_addr sin_addr; /* internet address */
    // };

    // /* Internet address. */
    // struct in_addr
    // {
    //     uint32_t s_addr; /* address in network byte order */
    // };

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;//协议组 IPv4
    saddr.sin_port = htons(8888);//端口号
    saddr.sin_addr.s_addr = inet_addr("192.168.253.145");//本机地址
    // 3.绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err\n");
        return -1;
    }
    printf("bind ok\n");
    // 4.监听套接字(listen)-----------------》待机
    if (listen(socketfd, 6) < 0)
    {
        perror("listen err\n");
        return -1;
    }
    printf("listen ok\n");
    // 5.接收客户端连接连接请求(accept)--》接电话
    int acceptfd = accept(socketfd, NULL, NULL);
    if (acceptfd < 0)
    {
        perror("accept err\n");
        return -1;
    }
    printf("acceptfd: %d\n", acceptfd);
    // 6.接收、发送数据(recv send)---》通话
    int ret;
    while (1)
    {
        ret = recv(acceptfd, buf, sizeof(buf), 0);//接收客户端的数据
        if (ret < 0)
        {
            perror("recv error\n");
            return -1;
        }
        else if (ret == 0)//客户端退出
        {
            printf("client exit\n");
            break;
        }
        else
        {
            printf("buf: %s\n", buf);//打印接收到的数据
            memset(buf, 0, sizeof(buf));//清空缓存区的数据,防止影响下次接收
        }
    }

    // 7.关闭套接字(close)-----------------》挂电话
    close(acceptfd);
    close(socketfd);

    return 0;
}

2> 客户端代码

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    
    char buf[128] = {0};
    // 1.创建套接字(socket)------------》有手机
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0)
    {
        perror("socket err\n");
        return -1;
    }
    printf("%d\n", socketfd);
    // 2.指定(服务器)网络信息--------》有对方的号码
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8888);
    saddr.sin_addr.s_addr = inet_addr("192.168.253.145");
    // 3.连接(connect)-------------------》拨打电话
    if (connect(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("connect err\n");
        return -1;
    }
    printf("connect ok\n");
    // 4.接收发送消息(recv send)---》通话
    while (1)
    {
        scanf("%s", buf);//输入数据
        // fgets(buf,sizeof(buf),stdin);
        if(strcmp(buf,"exit")==0)//判断是否为exit
        {
            break;
        }
        // write(socketfd, buf, sizeof(buf));
        send(socketfd, buf, sizeof(buf), 0);//发送到服务器
        
    }

    // 5.关闭套接字(close)------------》挂电话
    close(socketfd);
    return 0;
}

 4》改进版

地址和端口都通过参数传入

自动获取本机地址0.0.0.0

增加来电显示功能:显示客户端连入的地址

 1> 服务器代码

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        perror("argc err\n");
        return -1;
    }
    char buf[128] = {0};
    // 1.创建套接字(socket)---------------》有手机
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0)
    {
        perror("socket err\n");
        return -1;
    }
    printf("socketfd: %d\n", socketfd);
    // 2.指定(自己)网络信息--------------------》有号码
    // struct sockaddr_in
    // {
    //     sa_family_t sin_family;  /* address family: AF_INET */
    //     in_port_t sin_port;      /* port in network byte order */
    //     struct in_addr sin_addr; /* internet address */
    // };

    // /* Internet address. */
    // struct in_addr
    // {
    //     uint32_t s_addr; /* address in network byte order */
    // };

    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//全局地址,自动获取本机地址
    // saddr.sin_addr.s_addr = INADDR_ANY;
    // 3.绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err\n");
        return -1;
    }
    printf("bind ok\n");
    // 4.监听套接字(listen)-----------------》待机
    if (listen(socketfd, 6) < 0)
    {
        perror("listen err\n");
        return -1;
    }
    printf("listen ok\n");
    // 5.接收客户端连接连接请求(accept)--》接电话
    while (1)//循环等待客户端链接
    {
        int len = sizeof(caddr);
        int acceptfd = accept(socketfd, (struct sockaddr *)&caddr,&len);
        if (acceptfd < 0)
        {
            perror("accept err\n");
            return -1;
        }
        printf("acceptfd: %d\n", acceptfd);
        printf("port: %d  ip: %s\n",ntohs(caddr.sin_port),inet_ntoa(caddr.sin_addr));//来电显示,显示客户端信息
        // 6.接收、发送数据(recv send)---》通话
        int ret;
        while (1)
        {
            ret = recv(acceptfd, buf, sizeof(buf), 0);
            if (ret < 0)
            {
                perror("recv error\n");
                return -1;
            }
            else if (ret == 0)
            {
                printf("client exit\n");
                break;
            }
            else
            {
                printf("buf: %s\n", buf);
                memset(buf, 0, sizeof(buf));
            }
        }

        // 7.关闭套接字(close)-----------------》挂电话
        close(acceptfd);
    }
    close(socketfd);

    return 0;
}

2> 客户端代码

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        perror("argc err\n");
        return -1;
    }
    char buf[128] = {0};
    // 1.创建套接字(socket)------------》有手机
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0)
    {
        perror("socket err\n");
        return -1;
    }
    printf("%d\n", socketfd);
    // 2.指定(服务器)网络信息--------》有对方的号码
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    // 3.连接(connect)-------------------》拨打电话
    if (connect(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("connect err\n");
        return -1;
    }
    printf("connect ok\n");
    // 4.接收发送消息(recv send)---》通话
    while (1)
    {
        // scanf("%s", buf);
        fgets(buf, sizeof(buf), stdin);
        if (strcmp(buf, "exit\n") == 0)
        {
            break;
        }
        write(socketfd, buf, sizeof(buf));
        // send(socketfd, buf, sizeof(buf), 0);
    }

    // 5.关闭套接字(close)------------》挂电话
    close(socketfd);
    return 0;
}

今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!

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

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

相关文章

免费的 Mac 应用清理工具Pearcleaner v3.8.6

免费的 Mac 应用清理工具。这是一款免费开源的 Mac 应用清理工具&#xff0c;能够彻底卸载应用并清理残留文件。它采用 SwiftUI 开发&#xff0c;提供了简单易用的界面&#xff0c;支持右键卸载、迷你模式和 Homebrew 清理等功能。 下载链接&#xff1a;https://pan.quark.cn/s…

【Visual Studio 报错】vs 在使用二进制写入文件时弹窗报错:使用简体中文 gb2312 编码加载文件

如以下报错 解决办法 解决方法&#xff1a;文件->高级保存选项->将文件编码形式改为“UTF-8带签名” 若找不到高级保存选项&#xff0c;可以跟着下面路径把该选项调出来 &#xff1a;工具->自定义->命令->菜单栏中改成文件->预览右边点添加命令->类别中…

BRAM IP Native模式使用

简介 BRAM&#xff08;‌Block RAM&#xff09;是‌FPGA&#xff08;Field-Programmable Gate Array&#xff09;中的一种专用RAM资源&#xff0c;固定分布在FPGA内部的特定位置。该内容主要对BRAM&#xff08;Block RAM”的缩写&#xff09;Native模式下IP界面做详细描述和使用…

C语言进阶版第9课—指针(3)

文章目录 1. 字符指针变量2. 数组指针变量3. 二维数组传参的本质4. 函数指针变量5. typedef关键字6. 函数指针数组7. 函数指针数组的应用—转移表 1. 字符指针变量 练习题 2. 数组指针变量 在学习数组指针前&#xff0c;我们先回忆一下什么是指针数组指针数组 → 存放指针的数…

基于单片机的人脸识别的智能门禁系统设计

文章目录 前言资料获取设计介绍功能介绍设计清单核心代码具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等…

Unity Addressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统&#xff0c;并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址&#xff08;Addressable&#xff09;时&#xff0c;您可以使用该资源的地址从任何地方加载它。无论资源是在…

【C++ Primer Plus习题】14.2

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include "wine.h" …

【PWN · 栈溢出 | GOT劫持】[2024 · 长城杯]consumption

通过代码审计&#xff0c;找到栈溢出漏洞点&#xff0c;覆盖关键栈变量&#xff0c;实现任意地址写 前言 本题主要是套壳了Cjson&#xff0c;实则是约定了输入格式。通过仔细代码审计&#xff0c;即可找到栈溢出&#xff0c;并实现利用 一、题目 查阅网上资料&#xff0c;得知…

如何提取视频中的音频?新手也能轻松搞定

在数字媒体处理中&#xff0c;从视频文件中提取音频是一个常见需求。无论你是希望为视频制作单独的音频版本&#xff0c;还是想将某段视频的背景音乐用于其他项目&#xff0c;掌握音频提取技术都至关重要。本文将详细介绍几种提取视频中音频的方法&#xff0c;帮助你轻松实现这…

【自考zt】【软件工程】【21.04】(部分)

一、单选 二、填空 三、简答 四、应用 小结&#xff1a;

DeepSeek缓存命中技术,成本降低10倍

DeepSeek系列升级&#xff1a; DeepSeek发布最新的缓存命中技术&#xff0c;有效降低成本至0.1元/百万tokens&#xff0c;适用于文件读取和固定提示词。 点评&#xff1a;由于token消耗大部分是在系统提示词中&#xff0c;妥善使用确实可以极大降低成本&#xff0c;同时还能保证…

音视频入门基础:WAV专题(11)——FFmpeg源码中计算WAV音频文件每个packet的pts_time、dts_time的实现

音视频入门基础&#xff1a;WAV专题系列文章&#xff1a; 音视频入门基础&#xff1a;WAV专题&#xff08;1&#xff09;——使用FFmpeg命令生成WAV音频文件 音视频入门基础&#xff1a;WAV专题&#xff08;2&#xff09;——WAV格式简介 音视频入门基础&#xff1a;WAV专题…

ssm微信小程序校园失物招领论文源码调试讲解

第二章 开发技术与环境配置 以Java语言为开发工具&#xff0c;利用了当前先进的SSM框架&#xff0c;以MyEclipse10为系统开发工具&#xff0c;MySQL为后台数据库&#xff0c;开发的一个微信小程序校园失物招领。 2.1 Java语言简介 Java是由SUN公司推出&#xff0c;该公司于20…

[git操作] git创建仓库上传github报错

操作流程如下 使用 git init使用 git remote add origin 项目ssh链接git add . 报错如下 Bus error (core dumped)然后执行任何别的操作都会报错&#xff1a; fatal: Unable to create path .. /.git/index.lock: File exists.Another git process seems to be running in …

​全国计算机软件中级资料分享

全国计算机软件中级资料分享 软考资料分析 软件设计师中间 免费资料 链接:https://pan.baidu.com/s/1GqbgIq_D0uZd-uuhIhG-Sg?pwd2atp 提取码:2atp 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 求个免费关注不过分吧。

vue实现评论滚动效果

vue插件实现滚动效果 一、安装组件 官网地址&#xff1a;https://chenxuan0000.github.io/vue-seamless-scroll/ 1、vue2安装 npm install vue-seamless-scroll --savevue3安装 npm install vue3-seamless-scroll --save二、组件引入 <template><div v-if"…

OpenCV 与 Matplotlib 的结合使用:轮毂检测与目标跟踪

目录 绘制轮廓图像 实现思路 1. 读取图像并转换为灰度图像 2. 二值化处理 3. 查找轮廓 4. 绘制轮廓 5. 显示结果 代码实现 效果展示 动态逐步显示轮廓结果 实现思路 1. 读取图像并缩放 2. 转换为灰度图像 3. 二值化处理 4. 查找轮廓 5. 动态显示轮廓 6. 显示最…

基于图神经网络的最大独立集问题的目标分支

文章目录 Abstract1 Introduction2 Related Work分支顶点选择图神经网络Abstract 分支归约方法结合了分支约束原则和归约规则,在处理以前无法管理的现实世界实例方面特别成功。分支策略决定下一个要在哪个顶点上进行分支。最近,最广泛使用的策略是选择最高度的顶点。 在这项…

C++为什么要引入智能指针?

智能指针的必要性 C 引入智能指针主要是为了解决手动管理动态分配内存时可能出现的几个问题&#xff0c;特别是内存泄漏、野指针和异常安全等问题。智能指针通过封装原始指针的操作&#xff0c;提供自动化的内存管理机制&#xff0c;以减少这些问题的发生。 具体来说&#xff…

c++基础补充

c基础补充 函数补充函数声明函数的分文件编写 指针补充占用大小空指针坏指针野指针 C内存分区模型 函数补充 函数声明 #include <iostream> using namespace std;int max(int a,int b);int main() {int result max(1,3);cout << result << endl;return 0;…