socket编程|TCP

news2025/1/20 3:53:28

一.套接字概念

套接字(Socket)是一种用于网络通信的编程接口,它提供了一种机制,使得不同计算机上的应用程序能够通过网络进行通信和交换数据。

套接字可以看作是应用程序和网络之间的端点,它定义了应用程序与网络之间的通信规则和数据格式。通过套接字,应用程序可以创建、连接、发送和接收数据,实现与其他计算机上的应用程序进行通信。

套接字通常基于TCP/IP协议栈进行通信,其中常用的套接字类型有两种:

  1. 流式套接字(Stream Socket):使用基于流的TCP协议,提供可靠的面向连接的通信。流式套接字通过建立连接,提供可靠的、无差错的数据传输,保证数据的有序性和完整性。

  2. 数据报套接字(Datagram Socket):使用基于数据报的UDP协议,提供无连接的通信。数据报套接字以短的数据包(数据报)的形式传输数据,适用于实时性要求较高但不需要可靠性的情况。

使用套接字进行通信通常需要进行以下步骤:

  1. 创建套接字:应用程序通过调用系统API来创建一个套接字。

  2. 绑定套接字:将套接字与本地网络地址绑定,以便其他应用程序可以通过网络找到它。

  3. 监听连接请求(对于服务器):在服务器端,套接字会监听传入的连接请求。

  4. 建立连接(对于客户端):在客户端,套接字会发起连接请求与服务器建立连接。

  5. 发送和接收数据:已建立连接的套接字可以通过发送和接收数据来进行通信。

  6. 关闭套接字:通信完成后,应用程序可以关闭套接字来释放资源。

套接字编程提供了灵活和强大的网络通信功能,广泛应用于互联网、网络通信和分布式计算等领域。
在这里插入图片描述

网络通信过程中,套接字一定是成对出现的,一个套接字描述符有两个缓冲区,分别是写缓冲区,读缓冲区

二.预备知识

2.1网络字节序

网络字节序(Network Byte Order)是一种规定的数据表示方式,在网络通信中用于保证不同计算机之间的数据交换的正确性和一致性。

在计算机内部,数据一般以主机字节序(Host Byte Order)存储和处理。主机字节序可以是大端字节序(Big-Endian)或小端字节序(Little-Endian),具体取决于计算机体系结构。大端字节序表示高位字节在前、低位字节在后;小端字节序表示低位字节在前、高位字节在后。

为了在不同的计算机之间进行数据交换,需要使用统一的网络字节序。网络字节序采用的是大端字节序,因此在网络通信中,数据会按照从高位到低位的顺序进行传输。

为了方便在不同字节序之间进行转换,可以使用以下函数进行字节序转换:

  • htons():将16位(2字节)主机字节序转换为网络字节序。
  • ntohs():将16位(2字节)网络字节序转换为主机字节序。
  • htonl():将32位(4字节)主机字节序转换为网络字节序。
  • ntohl():将32位(4字节)网络字节序转换为主机字节序。

使用这些函数进行字节序的转换可以确保在不同计算机之间进行数据交换时,数据的字节顺序是一致的。

网络字节序–>大端法–>高位存低地址
主机字节序–>小端法–>高位存高地址

大小端区别

例如十六进制数值 0x12345678,我们可以将其分解成四个字节进行表示。

在大端存储中,高位字节存储在低地址处,低位字节存储在高地址处。因此,0x12 是高位字节,0x34 是次高位字节,0x56
是次低位字节,0x78 是低位字节。所以在大端存储中,表示为:

高地址 -> 低地址:0x12 0x34 0x56 0x78

在小端存储中,低位字节存储在低地址处,高位字节存储在高地址处。因此,0x78 是低位字节,0x56 是次低位字节,0x34
是次高位字节,0x12 是高位字节。所以在小端存储中,表示为:

高地址 -> 低地址:0x78 0x56 0x34 0x12

大小端判断

使用 intchar 类型的联合体可以进行字节序的判断,而不使用数组。

以下是一个示例代码:

#include <stdio.h>

union EndianTest {
    int value;
    char byte;
};

int isLittleEndian() {
    union EndianTest test;
    test.value = 1;

    // 判断低字节的值是否为1
    return (test.byte == 1);
}

int main() {
    if (isLittleEndian()) {
        printf("小端字节序 (Little-Endian)\n");
    } else {
        printf("大端字节序 (Big-Endian)\n");
    }

    return 0;
}

在这个示例中,我们定义了一个联合体 EndianTest,它包含一个 int 类型的成员 value 和一个 char 类型的数组成员 byte

函数 isLittleEndian() 中,我们将数值 1 赋值给 value,然后通过检查 byte[0] 的值是否为 1 来判断字节序。如果 byte[0] 的值为 1,则表示是小端字节序;如果不为 1,则表示是大端字节序。

通过这个方法,我们可以使用 intchar 类型的联合体来判断当前计算机的字节序是大端还是小端。

2.2 IP地址转换函数

主机字节序转换为网络字节序

不常用方法

在这里插入图片描述

常用方法
头文件: #include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
声明:
in_addr_t inet_addr(const char *strptr); //该参数是字符串

      typedef uint32_t in_addr_t;  (通过vi -t 追一下)
        struct in_addr { 
                    in_addr_t   s_addr;
        }; 
 
 
功能:  主机字节序转为网络字节序
参数:  const char *strptr: 字符串
返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示), 
      否则NULL

网络字节序转换为主机字节序

不常用

在这里插入图片描述

常用

头文件 :    #include <arpa/inet.h>  
           #include<sys/socket.h>  
           #include<netinet/in.h>  
声明:   char *inet_ntoa(stuct in_addr inaddr);
功能:   将网络字节序二进制地址转换成主机字节序。 
参数:  stuct in_addr in addr  : 只需传入一个结构体变量
返回值:  返回一个字符指针, 否则NULL;

2.3 sockaddr数据结构

在这里插入图片描述

    头文件:
           #include<sys/types.h>  
           #include<sys/socket.h> 
           #include<netinet/in.h>  
           #include<netinet/ip.h>

   ipv4通信结构体:
            struct sockaddr_in {
                sa_family_t    sin_family;   ----协议族
                in_port_t      sin_port;     ----端口
                struct in_addr sin_addr;     ----ip结构体
            };
        
            struct in_addr {
                uint32_t   s_addr;           --ip地址
            };

三.网络套接字函数

3.1 soket模型创建流程图

在这里插入图片描述

3.2 socket创建套接字

int socket(int domain, int type, int protocol);
头文件: #include <sys/types.h>
        #include <sys/socket.h>
功能:创建套接字
参数:
   domain:协议族
     AF_UNIX, AF_LOCAL  本地通信
     AF_INET            ipv4
     AF_INET6            ipv6
   type:套接字类型
     SOCK_STREAM:流式套接字
     SOCK_DGRAM:数据报套接字
   protocol:协议 - 填0 自动匹配底层 ,根据type
      系统默认自动帮助匹配对应协议
       传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
       网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
 返回值:
    成功 文件描述符 0 -> 标准输入  1->标准输出  2->标准出错 
                  3->socket
    失败 -1,更新errno

3.3 bind绑定套接字

头文件: #include<sys/types.h>  #include<sys/socket.h> 
         #include<netinet/in.h>  #include<netinet/ip.h>

功能:绑定协议, IP以及port

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

参数:
    sockfd:套接字
    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;     ----ip结构体
};
struct in_addr {
    uint32_t       s_addr;     --ip地址
};

3.4 listen监听

int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同一时间可以响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8个

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

3.5 accept阻等待连接

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept(sockfd,NULL,NULL);
功能:阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
             则accept()函数返回,返回一个用于通信的套接字文件;
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值: 
     成功:文件描述符; //用于通信
     失败:-1,更新errno新errno 

3.6 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   成功接收的字节个数

​3.7 connect

connect:使用现有的socket与服务器建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:socket 函数返回值
addr:传入参数。地址结构(服务器的地址结构)
addlen:addr长度
成功:0
失败:-1 errno
如果不使用bind绑定客户端地址结构,采用"隐式绑定"

3.8 send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd:指定要发送数据的socket描述符。
buf:指向要发送数据的缓冲区的指针。
len:指定要发送数据的长度(以字节为单位)。
flags:指定发送操作的标志,如`0`或`MSG_DONTWAIT`等。可以在调用时传递特定的标志,也可以用`0`表示无特殊标志。

返回值:
成功: 发送的字节数,
错误:  -1。

需要注意的是,send()
函数并不保证立即将所有数据发送出去,它会尽力将数据发送给远程主机,但在网络条件不理想或发送缓冲区已满的情况下,可能会导致部分数据未能立即发送。

四.案例

利用socket编程。实现一个服务器,客户端发送文本数据给服务器后,服务器将字母大写转小写,小写转大写。转换完成后发送给客户端。客户端输入"quit#"后退出。并且客户端连接后,服务器端打印客户端的ip地址和端口号。

server.c

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

int main(int argc, char const *argv[])
{
    //1.socket建立文件描述符
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket is err");
    }

    //2.bind绑定服务器ip地址和端口号
    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");
    int len = sizeof(caddr);
    int flage = bind(fd, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (flage < 0)
    {
        perror("bind is err");
        return -1;
    }

    //3.listen监听,设置最大同时连接数量
    flage = listen(fd, 5);
    if (flage < 0)
    {
        perror("listen is err");
        return -1;
    }
    printf("listern is ok\n");
    //4.accept等待客户端连接

    int acceptfd = accept(fd, (struct sockaddr *)&caddr, &len);
    if (acceptfd < 0)
    {
        perror("flage is err");
        return -1;
    }
    printf("ip:%s   port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

    //5. 不断读取客服端的内容,大小写转换后,发送给客户端
    char buf[1024] = {0};
    int size = 0;
     while (1)
    {
        flage = recv(acceptfd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else if (flage == 0)
        {
            printf("ip:%s is close\n", inet_ntoa(caddr.sin_addr));
        }
        else
        {
            size = strlen(buf);
            
            for (int i = 0; i < size; ++i)
            {
                if (buf[i] >= 'a' && buf[i] <= 'z')
                    buf[i] = buf[i] + ('A' - 'a');
                else
                    buf[i] = buf[i] + ('a' - 'A');
            }
            
            send(acceptfd, buf, sizeof(buf), 0);
        }
    }
    close(fd);
    return 0;
}

client.c

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

int main(int argc, char const *argv[])
{
    //1.socket建立文件描述符
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket is err");
    }

    //2.connect连接服务器
    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]);
    int flage = connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (flage < 0)
    {
        perror("connect is err");
    }

    //3.服务器端不断发送数据,接受服务器转化后的数据
    char buf[1024] = {0};
    while (1)
    {
        //memset(buf,0,sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        if (strncmp(buf,"quit#",5)==0)
        {
            break;
        }
        
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';
        send(fd, buf, sizeof(buf), 0);
        flage = recv(fd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else
        {
            fprintf(stdout, "%s\n", buf);
        }
    }
    close(fd);
    return 0;
}

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

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

相关文章

400电话:提供全方位客户服务的热线电话

随着现代社会的发展&#xff0c;企业和机构越来越重视客户服务的质量和效率。为了更好地满足客户的需求&#xff0c;提供及时的帮助和解决方案&#xff0c;许多企业纷纷引入了400电话这一全方位客户服务的热线电话。 400电话是一种以4开头的电话号码&#xff0c;通过该号码可以…

[论文阅读]Coordinate Attention for Efficient Mobile Network Design

摘要 最近关于移动网络设计的研究已经证明了通道注意力(例如&#xff0c; the Squeeze-and-Excitation attention)对于提高模型的性能有显著的效果&#xff0c;但它们通常忽略了位置信息&#xff0c;而位置信息对于生成空间选择性注意图非常重要。在本文中&#xff0c;我们提出…

Redis 三种特殊的数据类型 - Geospatial地理位置 - Hyperloglog基数统计的算法 - Bitmaps位图(位存储)

目录 Redis 三种特殊的数据类型&#xff1a; Geospatial&#xff1a;地理位置 Geospatial类型常用的命令&#xff1a; GEOADD&#xff1a;添加地理位置 GEOPOS&#xff1a;获取地理位置 GEODIST&#xff1a;返回两个给定位置之间的距离 GEORADIUS&#xff1a;以给定的经纬…

公司电脑文件加密防泄密软件系统——「天锐绿盾」

天锐绿盾是一款功能强大的公司电脑文件加密防泄密软件系统&#xff0c;旨在保护企业的知识产权和商业机密。 PC访问地址&#xff1a;http://985.so/2y2n9 它具有以下几个主要特点&#xff1a; 文件加密&#xff1a;天锐绿盾会对存储在公司电脑上的所有敏感文件进行自动加密&am…

uniapp——实现电子签名功能——基础积累

话说&#xff0c;2020年刚来杭州的时候&#xff0c;有用到过uniapp&#xff0c;距今已有三年时间了&#xff0c;果然全忘了&#xff0c;哈哈[笑中带泪] 昨天遇到一个需求&#xff1a;就是要实现pdf文件的预览&#xff0c;着实费了我很多的时间&#xff0c;连晚饭都没有吃好。。…

uniapp——实现在线选座功能——技能提升

首先声明一点&#xff1a;下面的内容是从一个uniapp的程序中摘录的&#xff0c;并非本人所写&#xff0c;先做记录&#xff0c;以免后续遇到相似需求抓耳挠腮。 这里写目录标题 效果图代码——html部分cu-custom组件anil-seat组件 代码——jscss部分 效果图 代码——html部分 …

用区熔拉晶法和光谱分析法评价多晶硅棒的规程.

声明 本文是学习GB-T 29057-2023 用区熔拉晶法和光谱分析法评价多晶硅棒的规程. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 12 试验数据处理 12.1 通过测量取得样品的施主、受主杂质和代位碳、间隙氧杂质含量&#xff0c;再按公式(3)计算多晶硅棒…

掌握MyBatis动态SQL:从标签到实战的全面解析

&#x1f600;前言 在我们日常的软件开发中&#xff0c;很多时候都会涉及到与数据库的交互操作。在使用MyBatis框架进行数据库操作时&#xff0c;我们可以利用它提供的一系列XML标签来构建动态SQL语句&#xff0c;以满足不同的业务需求。 . 本文主要探讨了如何使用MyBatis的, ,…

数据结构基础7:二叉树【链式结构】实现和递归思想。

二叉树的链式结构实现 一.二叉树链式结构的实现&#xff1a;1.前置说明&#xff1a;1.创建二叉树&#xff1a;2.二叉树的结构&#xff1a; 2.二叉树的遍历&#xff1a;1.二叉树的前中后序遍历&#xff1a;2.内容拓展&#xff1a; 二.二叉树链式(题目)题目一&#xff1a;计算节点…

Python文件操作(04):常见功能

一、read&#xff0c;读 1、读所有 f open(info.txt, moder, encodingutf-8) # 模式是r/rt&#xff0c;必须在内部使用encoding将文本转换成字符串类型 data f.read() f.close()f open(info.txt, moderb) # 模式是rb&#xff0c;不能加encoding&#xff0c;否则报错&…

栈的应用-综合计数器的实现

目录 前言 一、思路分析 二、代码实现 总结 前言 在实现综合计数器之前,大家应该先了解一下什么是前中后缀表达式 前缀、中缀和后缀表达式是表示数学表达式的三种不同方式。 前缀表达式&#xff08;也称为波兰式或前缀记法&#xff09;&#xff1a;操作符位于操作数之前。…

基于51单片机超市快递寄存自动柜 GSM远程密码手机验证码系统

一、系统方案 本设计采用52单片机作为主控器&#xff0c;GSM模块&#xff0c;液晶1602显示&#xff0c;矩阵键盘输入&#xff0c;蜂鸣器报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 /*******************************************…

阿里云服务器配置选择指南(2023新版教程)

阿里云服务器配置选择_CPU内存/带宽/存储配置_小白指南&#xff0c;阿里云服务器配置选择方法包括云服务器类型、CPU内存、操作系统、公网带宽、系统盘存储、网络带宽选择、安全配置、监控等&#xff0c;阿小云分享阿里云服务器配置选择方法&#xff0c;选择适合自己的云服务器…

多线程JUC 第2季 synchronized锁升级过程

一 synchronized的概述 1.1 synchronized的特性 用锁能够实现数据的安全&#xff0c;但是会代理性能下降。Synchronized是一个重量级锁&#xff0c;锁的升级过程&#xff1a;无锁->偏向锁->轻量级锁->重量级锁。 1.2 synchronized锁性能低效原因 在java中早期版本…

React TypeScript 样式报错

代码如下&#xff1a; 报错内容&#xff1a; Type ‘{ flexDirection: string; }’ is not assignable to type ‘Properties<string | number, string & {}>’. Types of property ‘flexDirection’ are incompatible. Type ‘string’ is not assignable to ty…

【uvgRTP】win32 v143 不带pthread、不带crypto 构建

cryptopp 依赖库 https://github.com/weidai11/cryptopp 先不启用试试。自动下载deps 工程 if (NOT UVGRTP_DISABLE_TESTS)# PThreadset(CMAKE_THREAD_PREFER_PTHREAD TRUE)set(THREADS_PREFER_PTHREAD_FLAG TRUE)find_package( Threads REQUIRED )

阿里云服务器部署安装hadoop与elasticsearch踩坑笔记

2023-09-12 14:00——2023.09.13 20:06 目录 00、软件版本 01、阿里云服务器部署hadoop 1.1、修改四个配置文件 1.1.1、core-site.xml 1.1.2、hdfs-site.xml 1.1.3、mapred-site.xml 1.1.4、yarn-site.xml 1.2、修改系统/etc/hosts文件与系统变量 1.2.1、修改主机名解…

【Java基础篇 | 面向对象】--- 聊聊什么是多态(上篇)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习JavaSE的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、什么是多态二、多…

【操作系统】聊聊进程是如何调度的

进程的引入是为了让操作系统可以同时执行不同的任务。而进程从创建到销毁也就对应不同的状态&#xff0c;进程状态&#xff0c;本质上就是为了用有限的计算机资源合理且高效地完成更多的任务 而不同的任务如何进行合理的分配&#xff0c;被CPU执行&#xff0c;其实就是不同的调…

网工内推 | 国企网络运维,大专以上即可,有厂商认证优先

01 北京新明星电子技术开发有限公司 招聘岗位&#xff1a;运维工程师 职责描述&#xff1a; 1、负责所在特定客户的技术支持服务工作&#xff0c;负责各厂商服务器、存储设备的日常操作、系统升级、故障处理工作。 2、对运维工作进行总结、提出问题或隐患&#xff0c;给出建议…