关于c语言的tcp通讯详细讲解

这篇具有很好参考价值的文章主要介绍了关于c语言的tcp通讯详细讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、TCP概览

1.1 TCP基本特征

1.2 TCP通信流程基本原理

2、TCP编程的函数接口说明

3、TCP通讯测试代码

1、TCP概览

TCP全称 Transmition Control Protocol,即:传输控制协议。是面向连接的协议。通常,TCP 通信还会被冠以 可靠传输协议 的头衔。但请注意,这里的可靠并非指发出去的数据对方一定能收到(这是不可能的),而仅指TCP能使发送方可靠地知道对方是否收到了数据。

1.1 TCP基本特征

  • 有连接:通信双方需要事先连接成功,方可传输数据
  • 有确认:一方收到对端的任何数据,都会给另一方发回执确认
  • 保证数据有序、不重复、丢失会重发
  • 如果网络拥堵,会自动调节发送量
  • 采用帧异步的流式通信方式(即通信双方每次的收发数据量不必相等)

简单来讲,TCP 类似于打电话,说话前需要花一定的时间接通电话,等到对方接听了之后双方才能开始通信,通信的过程中每个数据的传送,接收方都会给发送方回执确认,断开的时候也会互相通知以便于释放各自相关的资源。可以看出来,TCP 相对于 UDP 而言资源开销更大,提供更丰富的功能,TCP适合用在如下情形:

  • 传输质量要求较高,不能丢失数据
  • 大数据量的通信,以至于通信前后的连接和断开的开销可以忽略不计
  • 用户登录、账户管理等相关的功能

1.2 TCP通信流程基本原理

TCP的通信流程跟打电话是几乎一样的,因此可以将通信的过程细分为主动发起连接者(客户端)和被动接受连接者(服务端)两方来分别讨论。

被动的服务端

  1. 建立TCP套接字sockfd,即通信端点
  2. 绑定套接字sockfd与网络地址,即IP+端口
  3. 设定套接字sockfd进入被动监听状态,即将套接字设定为监听套接字
  4. 静静等待远程客户端的连接请求
  5. 收到连接请求后,得到一个专用于收发数据的连接套接字connfd
  6. 使用连接套接字connfd与客户端通信

主动的客户端

  1. 建立TCP套接字sockfd,即通信端点
  2. 对服务端发起连接请求
  3. 若连接成功,则直接通过套接字sockfd与服务端通信

关于c语言的tcp通讯详细讲解

注意点:

  • 在服务端中,监听套接字和连接套接字是严格区分的,不可混用
  • 服务端所绑定的地址(IP+PORT)需要对外公开,否则客户端无法发起连接
  • 客户端在发起连接前一般无需绑定地址,此时系统会为此连接自动分配恰当的地址资源

2、TCP编程的函数接口说明

TCP通讯的步骤如下:

客户端(client)

1、建立套接字(socket)

//2、绑定主机的IP地址和端口号(bind)(可以省略)

3、填充服务器的结构体,向服务器发起连接请求(connect)

4、聊天 -- 发送数据(send/write)

5、断开连接(close)

服务器(server)

1、建立套接字(socket)

2、填充服务器的结构体,绑定主机的IP地址和端口号(bind)

3、设置监听(listen)

4、阻塞等待接收客户端的连接(accept)新的套接字文件描述符

5、创建结构体存放客户端IP和端口,聊天 -- 接收数据 (recv/read)

6、断开连接(close)

函数接口说明如下:

1、建立套接字(socket)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
函数作用:建立套接字,返回套接字文件描述符
函数参数:domain:你要选择哪一种地址族
        PF_INET/AF_INET  IPV4网络协议  PF ---> Protocol Family
        PF_INET6/AF_INET6 IPV6网络协议    AF ---> Address Family
        type:你要选择哪一种协议
        SOCK_STREAM选择TCP -- 流式套接字   --->Stream Sockets
        SOCK_DGRAM选择UDP -- 数据报套接字  --->Datagram Sockets
        protocol:传0表示使用默认协议
返回值:成功:套接字文件描述符sockfd
        失败:-1

2、绑定主机的IP地址和端口号(bind)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
函数作用:绑定主机的IP地址和端口号
参数:sockfd:套接字文件描述符
     addr:自己的IP地址和端口号
     addelen:地址的大小长度
返回:成功

3、发起连接(connect)
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr * addr,socklen_t addrlen);
函数作用:发起连接
函数参数:sockfd:套接字文件描述符
        addr:对方的IP地址和端口号
        addrlen:地址的长度(sizeof(struct sockaddr_in)))
IPV4结构体
struct sockaddr_in
{
    short int sin_family;
    unsigned short int sin_port;
    struct in_addr sin_addr;
};
struct in_addr
{
  in_addr_t s_addr;/in_addr_t为32位的unsigned int,该无符号整数采用大端字节序/  
};
初始化 IP地址和端口号 --IPV4
struct sockaddr_in sereverAddr;
serverAddr.sin_family = PF_INET;
serverAddr.sin_port = htons(5000);
serverAddr.sin_addr.s_addr = inet_addr("主机IP地址");

4、聊天 -- 发送数据(send)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd,const void* buf,size_t len,int flags);
函数作用:用于网络中发送数据
参数:sockfd:套接字文件描述符
      buf:你要发送的数据
      len:你要发送数据的大小,以字节位单位
      flags:一般默认为0
返回值:成功:发送的字节数
        失败:-1

5、断开连接(close)

#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);//将主机端口号转成网络端口号
uint16_t ntohs(uint16_t netshort);//将网络端口号转成主机端口号
说明:h代表主机(host) n代表网络(network) s代表端口号(short)
返回值:成功:要转换的字节序
        失败:-1
从什么(h,n)端口号到(to)什么(n,h)端口号(s)

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);    //将主机IP转成网络IP
char* inet_ntoa(struct in_addr in);    //将网络IP转成主机IP
主机转网络addr(address) 网络转主机ntoa(network to address)

tcp的服务端需要设置端口复用

端口复用设置在服务器的代码里面

#include <sys/types.h>

#include <sys/socket.h>

int setsockopt(int s,int level,int optname,const void* optval,socklen_toptlen);

函数作用:设置端口号可以复用

参数:s:套接字文件描述符

level:代表欲设置的网络层,一般设成SOL_SOCKET以存取socket层

optname:SO_REUSEADDR端口号复用

optval:代表欲设置的值,比如给他一个1,表示使能 端口号复用

optlen则为optval的长度 //所以设置端口号可以复用,这两条语句放在绑定bind之前

int optval = 1;

setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));文章来源地址https://www.toymoban.com/news/detail-460231.html

3、TCP通讯测试代码

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

#define SERVER_PORT 60000          //-->1024 ~ 65535
#define SERVER_IP "192.168.5.184"  //-->虚拟机ip或主机ip

/*
    TCP的服务端的代码实现步骤
    1、建立套接字
    2、绑定(IP和端口号)
    3、监听(listen)
    4、阻塞连接(accept)
    5、接收数据(recv,read)
    6、关闭
*/

int main(int argc,char** argv)
{
    //手动输入端口号和IP地址
    // if(argc != 3)
    // {
    //     perror("./a.out ip port");
    //     return -1;
    // }

    int ret = 0;
    char buf[1024] = { 0 };

    //1、建立套接字
    //参数:              地址族 流式套接字 默认协议
    int socketfd = socket(AF_INET,SOCK_STREAM,0);
    if(socketfd == -1)
    {
        perror("socket fail");
        return -1;
    }

    //设置端口复用
    int optval = 1;
    //参数  套接字文件描述符    网络层   端口复用    设置值   设置值的大小
    setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));

    
    //2、填充服务端结构体的端口和IP
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    //server_addr.sin_port = htons(atoi(argv[2]));              //传参方式
    server_addr.sin_port = htons(SERVER_PORT);                  //宏定义方式
    //server_addr.sin_addr.s_addr = inet_addr(argv[1]);         //传参方式
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);         //宏定义方式

    //3、绑定
    //参数  套接字文件描述符 IP和端口号(旧结构体指针强转取地址)新结构体的大小
    ret = bind(socketfd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror("bind fail");
        return -1;
    }

    //4、监听
    //参数 套接字文件描述符 支持的客户端连接最大数量
    ret = listen(socketfd,20);
    if(ret == -1)
    {
        perror("bind fail");
        return -1;
    }

    //5、阻塞等待客户端连接
    //printf("绑定服务器IP:%s 端口号PORT:%hu\n",argv[1],atoi(argv[2]));
    printf("绑定服务器IP:%s 端口号PORT:%hu\n",SERVER_IP,SERVER_PORT);

    struct sockaddr_in client_addr;
    int len = sizeof(struct sockaddr_in);
    //参数           套接字文件描述符 IP和端口号(旧结构体指针强转取地址)长度取址
    //返回新的套接字文件描述符
    int newClientfd = accept(socketfd,(struct sockaddr*)&client_addr,&len);

    //6、拿到客户端的地址和端口号
    char* ip = inet_ntoa(client_addr.sin_addr);
    int port = ntohs(client_addr.sin_port);
    printf("客户端的IP:%s 端口号:%d\n",ip,port);

    //7、接收数据
    while(1)
    {
        //缓存区清零
        bzero(buf,sizeof(buf));

        //接收数据,计算返回值(buf真实数据大小)
        //参数  套接字文件描述符 缓存区 缓存区大小 默认属性
        //使用返回的新套接字文件描述符
        //ret = recv(newClientfd,buf,sizeof(buf),0);
        ret = read(newClientfd,buf,sizeof(buf));
        if(ret == 0)
        {
            printf("客户端掉线,服务器退出。。。\n");
            return -1;
        }

        printf("收到数据:%s 大小:%d\n",buf,ret);

        //做主动退出的判断条件
        if(!strcmp(buf,"exit"))
            break;

    }

    //8、关闭套接字
    close(socketfd);
    close(newClientfd);

    return 0;
}

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

#define SERVER_PORT 60000          //-->1024 ~ 65535
#define SERVER_IP "192.168.5.184"  //-->服务器的IP

/*
    TCP的客户端的代码实现步骤
    1、建立套接字
    2、绑定(IP和端口号)(可有可无)
    3、发送连接请求(connect)
    4、发送数据(send,write)
    5、关闭
*/

int main(int argc,char** argv)
{
    //手动输入端口号和IP地址
    // if(argc != 3)
    // {
    //     perror("./a.out ip port");
    //     return -1;
    // }

    int ret = 0;
    char buf[1024] = { 0 };

    //1、建立套接字
    //参数:              地址族 流式套接字 默认协议
    int socketfd = socket(AF_INET,SOCK_STREAM,0);
    if(socketfd == -1)
    {
        perror("socket fail");
        return -1;
    }

    //2、填充服务端结构体的端口和IP
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    //server_addr.sin_port = htons(atoi(argv[2]));              //传参方式
    server_addr.sin_port = htons(SERVER_PORT);        //宏定义方式
    //server_addr.sin_addr.s_addr = inet_addr(argv[1]);   //传参方式
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);   //宏定义方式

    //3、连接服务器
    //参数   套接字文件描述符  IP和端口号(旧结构体指针强转取地址)新结构体的大小
    ret = connect(socketfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in));
    if(ret < 0)
    {
        perror("connect fail");
        return -1;
    }
    printf("连接服务器成功[%s][%d]\n",SERVER_IP,SERVER_PORT);
    //printf("连接服务器IP:%s 端口号PORT:%hu\n",argv[1],atoi(argv[2]));

    //4、与服务器之间发送数据
    while(1)
    {
        //缓存区清零    
        bzero(buf,sizeof(buf));

        //发送数据,计算返回值(buf真实数据大小)
        scanf("%s",buf);

        //参数  套接字文件描述符 缓存区 缓存区真实大小 默认属性
        //ret = send(socketfd,buf,strlen(buf),0);
        ret = write(socketfd,buf,strlen(buf));
        if(ret  == -1)
        {
            printf("服务端掉线,客户端发送数据失败。。。\n");
            return -1;
        }

        printf("发送成功 ret:%d\n",ret);

        //做主动退出的判断条件
        if(!strcmp(buf,"exit"))
            break;
    }

    //关闭套接字
    close(socketfd);

    return 0;
}

到了这里,关于关于c语言的tcp通讯详细讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言 进程通讯 socket套接字(TCP/UDP)示例

    主机字节序(host-byte):指处理器存储数据的字节顺序,分两种         大端存储(big-endian):低地址存储数据高位(符合书写规则),由ARM、Motorola等采用         小端存储(little-endian):低地址存储数据低位(将数据不重要的部分保存在低地址,重要的部分保存在高地

    2024年02月05日
    浏览(50)
  • 关于Attention的超详细讲解

    动物需要在复杂环境下有效关注值得注意的点。 心理学框架:人类根据随意线索 (主观) 和不随意 (客观) 线索选择注意点。 比如如下例子,第一眼我们会看到红色的杯子,它相比于其它物品颜色偏亮,属于不随意线索。假设拿起杯子喝了之后,接下来想读书,那这就是

    2023年04月08日
    浏览(30)
  • 【计算机网络】TCP协议超详细讲解

    TCP协议广泛应用于可靠性要求较高的应用场景,如网页浏览、文件传输、电子邮件等。它提供了可靠的数据传输和流控制机制,能够确保数据的完整性和有序性。然而,由于TCP协议在传输过程中引入了较多的控制信息,因此相比于UDP协议,TCP的传输速度较慢。 TCP UDP 有连接

    2024年02月14日
    浏览(38)
  • 关于IPv6(超详细讲解)

    本文章收录至《网络》专栏,点击右上角专栏图标可访问本专栏! IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址[1]。 由于IPv4最大的问

    2024年02月08日
    浏览(27)
  • 关于Kafka事务处理的详细讲解

    Kafka事务 producer可能给多个topic,多个partition发送消息,这些消息组成一个事务,这些消息需要对consumer同时可见或者同时不可见。Kafka事务需要在producer端处理,consumer端不需要做特殊处理,跟普通消息消费一样  整个流程步骤: 事务初始化: InitTransactions,事务初始化是一次性

    2024年02月02日
    浏览(26)
  • C++关于string的详细讲解

    C++中的string类是字符串类,它在C++中非常常用,有了string类以后,我们不需要像C语言那样定义字符数组来表示字符串,操作起来是非常方便的,因为string类底层已经将增、删、查、改以及扩容这些机制封装好了,我们只需要直接使用即可。 string类其实是basic_string模板类的一

    2024年02月08日
    浏览(28)
  • 【C语言】动态通讯录(超详细)

    通讯录是一个可以很好锻炼我们对结构体的使用,加深对结构体的理解,在为以后学习数据结构打下结实的基础 这里我们想设计一个有 添加联系人,删除联系人,查找联系人,修改联系人,展示联系人,排序 这几种功能的通讯录 注意:我们按照三个区域划分 上图所示进行

    2024年02月08日
    浏览(34)
  • TCP和UDP的由浅到深的详细讲解

    目录 前言 一.TCP 1.1 什么是TCP? 1.2TCP的连接与释放(确认应答机制) 1.2.1三次握手 1.2.2四次挥手 1.3TCP滑动窗口(效率机制) 1.4流量控制(安全机制) 1.5拥塞控制(安全机制) 1.6延迟应答(效率机制) 1.7捎带应答(效率机制) 1.8心跳机制(安全机制) 1.9粘包问题 二.UDP  2.1什么是

    2024年02月07日
    浏览(42)
  • 蓝牙聊天App设计3:Android Studio制作蓝牙聊天通讯软件(完结,蓝牙连接聊天,结合生活情景进行蓝牙通信的通俗讲解,以及代码功能实现,内容详细,讲解通俗易懂)

    前言:蓝牙聊天App设计全部有三篇文章(一、UI界面设计,二、蓝牙搜索配对连接实现,三、蓝牙连接聊天),这篇文章是:三、蓝牙连接聊天。 课程1:Android Studio小白安装教程,以及第一个Android项目案例“Hello World”的调试运行 课程2:蓝牙聊天App设计1:Android Studio制作蓝

    2024年02月12日
    浏览(39)
  • 用最通俗的语言讲解 TCP “三次握手,四次挥手”

    目录 一. 前言 二. TCP 报文的头部结构 三. 三次握手 3.1. 三次握手过程  3.2. 为什么要三次握手 四. 四次挥手 4.1. 四次挥手过程 4.2. 为什么要四次挥手 五. 大白话说 5.1. 大白话说三次握手 5.2. 大白话说四次挥手 六. 总结     TCP 是一种面向连接的、可靠的、基于字节流的传输层

    2024年01月17日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包