TCP通信模型(C语言实现)

2023-05-16

        大家好,我是练习编程时长两年半的个人练习生昆工第一ikun,今天我们来分享TCP通信模型,并且用C语言实现它。


目录

一、我们将实现三个示例功能:

二、TCP服务器搭建流程

(1)创建套接字 --> socket();

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

(3)监听客户端连接请求 -->listen

(4)接收客户端连接请求 -->accept


​​​​​​​

一、我们将实现三个示例功能:

1.客户端给服务器发送一个字符串,服务器返回给客户端这个字符串的长度。

2.实现一个时间服务器,客户端发送time,服务器返回当前时间。

3.如果客户端发送get 1.txt的请求,服务器获取文件内容后,发送给客户端。

二、TCP服务器搭建流程

(1)创建套接字 --> socket();


#include  <sys/types.h>         
#include <sys/socket.h>
​
int socket(int family, int type, int protocol);
​
参数:
    family:协议族
        AF_INET:IPv4协议
        AF_INET6:IPv6协议
        AF_LOCAL:UNIX域协议
        AF_ROUTE:路由套接字
        AF_KEY:密钥套接字
    type:套接字类型
        SOCK_STREAM:流式套接字
        SOCK_DGRAM:数据报套接字
        SOCK_RAM:原始套接字
    protocol:0(原始套接字除外)
        
返回值:
        成功返回非负套接字描述符
        失败返回-1;
         
  

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

  • 端口:标识进程 无符号短整型: 0 ~ 65535 0 ~ 1024 被内核使用. 用户可指定端口:1025 ~ 65535

    
    字节序:大端序 与 小端序 
    ​
    网络字节序通常是大端序: 大端就是指低地址存放高字节 
    ​
    个人PC的字节序通常是小端序: 小段就是指低地址存放低字节 
    ​
    结论:需要在网络绑定port端口时进行字节序的转换..... 
      
  • 字节序转换函数:htons、htonl 等....

    
    可以使用man手册直接查看:man 3 htons 
    ​
    h: 主机host 
    ​
    to: 转换 
    ​
    n: net网络 
    ​
    s: short类型 
    ​
    htons就是把主机字节序转为网络字节序. 
      
  • IP地址:标识主机 ipv4占四个字节.

    
    点分十进制:"192.168.2.2" 是给人类看的 
    ​
    二进制形式:11000000 10101000 00000010 00000010 是计算机使用的 
      
  • IP地址转换函数

    
    点分转为二进制: 
    ​
    in_addr_t inet_addr(const char *cp); 
    ​
    cp: ip地址的字符串形式 
    ​
    in_addr_t: 转换后的二进制地址形式
      

        #include <sys/types.h>         
        #include <sys/socket.h>
​
        int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
​
        参数:
            sockfd:套接字描述符
            my_addr:绑定的地址
            addrlen:地址长度
​
        返回值:
                成功返回0;
                失败返回-1;
          

(3)监听客户端连接请求 -->listen


#include <sys/types.h>        
#include <sys/socket.h>
​
int listen(int sockfd, int backlog);
​
参数:
    sockfd:套接字描述符
    backlog:请求队列中允许的最大请求数,大多数系统默认值为5;
    
返回值:
    成功返回0;失败返回-1;
    

(4)接收客户端连接请求 -->accept

        注意:后续的数据通信全部使用通信套接字,不能使用监听套接字来通信


#include <sys/types.h>
#include <sys/socket.h>
​
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
​
参数:
    sockfd:套接字描述符
    addr:用于保护客户端地址,如果不需要客户端信息则传递 NULL
    addrlen:地址长度,如果不需要客户端信息则传递 NULL
    
返回值:
    成功返回用于通信的套接字(连接套接字 或 通信套接字)
    失败返回-1;
      

我们先来创建一个服务器:

#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

void *send_data(void *arg)
{
	int connfd = *(int*)arg;
	char s[64] = {0};
	while(1)
	{
		fgets(s, 64, stdin);
		s[strlen(s)-1] = '\0';
		write(connfd, s, 64);
		memset(s, 0, 64);
	}

}

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    
    //端口复用函数
    int on = 1;
    int k = setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &on, sizeof(on));
    if(k == -1)
    {
    	perror("setsockopt");
    	return -1;
    }
    
    
    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(11111);
    ser.sin_addr.s_addr = inet_addr("0.0.0.0");

    int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
    if(ret == 0)
    {
        printf("绑定成功\n");
    }
    else
    {
        perror("bind");
        return -1;
    }

    ret = listen(sockfd, 5);
    if(ret == 0)
    {
        printf("监听成功\n");
    }
    else
    {
        perror("listen");
        return -1;
    }

    struct sockaddr_in client;
    int n = sizeof(client);

    int connfd;
    while(1)
    {
        connfd = accept(sockfd, (struct sockaddr *)&client, &n);
        if(connfd == -1)
        {
            perror("accept");
            return -1;
        }
        else
        {
            printf("连接成功\n");
        }

        printf("client ip:%s\nclient port:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        pthread_t pid;
        pthread_create(&pid, NULL, send_data, (void *)&connfd);
        pthread_detach(pid);

        

while(1)
{
	char buf[64] = {0};
        ret = read(connfd, buf, 64);
        if(ret < 0)
        {
            perror("read");
            return -1;
        }
        else if(ret == 0)
        {
            close(connfd);
            break;
        }
        	printf(">>:%s\n", buf);
       

            if(strcmp(buf, "time") == 0)
            {              	
               
                time_t now = time(NULL);
                char *p = ctime(&now);
                char data[64];
                strcpy(data, p);
                write(connfd, data, strlen(data));
                 
                memset(buf, 0, 64);
                memset(data, 0, 64);
            }

           else if(strncmp(buf, "get", 3) == 0)
            {
            	
                int i, j = 0;
                char str[64] = {0};
                for(i = 4; i <= strlen(buf)-1; i++)
                {
                    str[j] = buf[i];
                    j++;
                }
                memset(buf, 0, 64);
                int fd = open(str, O_RDONLY);
                while((ret = read(fd, buf, 64)) != 0)
                {
                    write(connfd, buf, ret);
                    memset(buf, 0, 64);
                }
   
                write(connfd, "over", 64);
                memset(buf, 0, 64);
                 memset(str, 0, 64);
                close(fd);
            }
            else
            {
            char arr[64];
             
            sprintf(arr, "%ld", strlen(buf));
            write(connfd, arr, strlen(arr));
            memset(buf, 0, 64);
            memset(arr, 0, 64);
             
            }

    }
    }

    close(sockfd);
    

    return 0;
} 

 

 再来创建一个客户端:

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

void *recv_data(void *arg)
{
	int sockfd = *(int *)arg;
	char s[64] = {0};
	while(1)
	{
		int ret = read(sockfd, s, 64);
		if(ret < 0)
		{
			perror("read");
			return NULL;
		}
		printf("%s\n", s);
		memset(s, 0, 64);
	}

}
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    
    pthread_t pid;
    pthread_create(&pid, NULL, recv_data, (void *)&sockfd);
    pthread_detach(pid);

    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(11111);
    //ser.sin_addr.s_addr = inet_addr("0.0.0.0");
    ser.sin_addr.s_addr = htonl(INADDR_ANY);


    int ret = connect(sockfd, (struct sockaddr *)&ser, sizeof(ser));
    if(ret == -1)
    {
        perror("connect");
        return -1;
    }

    char buf[64];
    while(1)
    {
        fgets(buf, 64, stdin);
        buf[strlen(buf)-1] = '\0';
        write(sockfd, buf, 64);
        
        if(strcmp(buf, "time") == 0)
        {
        	char data[64];
            read(sockfd, data, 64);
            printf(">>:%s\n", data);
            memset(buf, 0, 64);
            memset(data, 0, 64); 
        }
        else if(strncmp(buf, "get", 3) == 0)
        {
        	char str[64];
            while(read(sockfd, str, 64) != 0)
            {
            if(strcmp(str, "over") == 0)
            {
 		memset(str, 0, 64);        
             break;
            }
            printf(">>:%s\n", str);
            memset(buf, 0, 64);
            memset(str, 0, 64); 
            }
        }
        else
        {	char arr[64] = {0};
            read(sockfd, arr, 64);
            printf(">>:%s\n", arr);
            memset(buf, 0, 64);
            memset(arr, 0, 64); 
        }
    }
    close(sockfd);

    return 0;
}

 运行结果如下:

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TCP通信模型(C语言实现) 的相关文章

  • C# 服务器和 Java 客户端:TCP 套接字通信问题

    我用 C 编写了一个服务器程序TCPListner和一个使用套接字的 Java 客户端程序 但我无法将复杂的对象从 Java 客户端发送到 C 服务器 当我通过将字符串转换为字节数组从 Java 客户端发送到 C 服务器时 当转换回字符串时
  • IPv4 允许的最大 TCP/IP 网络端口号是多少?

    可以使用的最大端口号是多少 端口号是一个无符号 16 位整数 即 65535
  • TCPServer 具有同时全双工通信

    我正在尝试编写一个 C 服务器 客户端 它将同时通过 TCP 相互发送字节数组 我正在努力思考如何实现这一目标 我见过的所有示例都等待消息 然后发送响应 我需要同时进行沟通 我是否需要为服务器和客户端上的传入和传出创建 2 个单独的 TCP
  • 如何监控 TCP 连接的 cwnd 和 ssthresh 值? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我希望在通过套接字连接发送或接收数据包时确定这些值 有没有现有的工具可以做到这一点 The ss http linux die net m
  • 如何识别用户空间和内核空间之间的特定套接字?

    我在用户空间中有一个库 可以拦截套接字层调用 例如socket connect accept 等等 我只处理 TCP 套接字 在内核空间中 我有一个网络内核模块 它处理所有 TCP 连接 我需要能够在驱动程序中识别哪些套接字被用户空间库拦截
  • 连接被对等方重置:套接字写入错误。我的 Java 代码出了什么问题

    我正在尝试从独立的 java 应用程序创建并维护到主机的 TCP 连接 本地端口和服务器端口相同 8999 连接后 我必须向服务器发送一条消息 lt STX gt username fred password abcd lt ETX gt
  • 套接字编程Python:如何确保收到完整消息?

    我正在使用 python 3 x 和套接字模块 服务器在 ipv4 地址上运行并使用 tcp 我阅读了一些有关如何发送和接收数据的教程 对于服务器或客户端 要确保发送整个消息 您可以简单地检查发送的数据量是否等于消息的大小 def myse
  • Boost ASIO:服务器如何知道客户端是否仍然连接?

    我在用boost asio对于服务器 客户端应用程序 服务器一次只接受一个连接 我想知道服务器验证客户端是否仍然连接的最佳方法是什么 这样做的目的是我希望能够知道客户端是否崩溃 以便我可以重新开始侦听新的连接尝试 在我的应用程序中 我使用以
  • 为什么我无法发送这个IP数据包?

    我正在尝试使用 C 发送 IP 数据包 destAddress IPAddress Parse 192 168 0 198 destPort 80 Create a raw socket to send this packet rawSoc
  • 是什么导致 MSSQL 中出现“非阻塞套接字上的操作将阻塞”错误?

    错误 异常查询为 CREATE NONCLUSTERED INDEX I1 ON AllAccounts BAK Master received Day ASC 出现异常 发生一个或多个错误 错误 异常内部异常无法从传输连接读取数据 非阻塞
  • 我的代码中某处存在无限循环

    我有这个 Java 游戏服务器 最多可处理 3 000 个 tcp 连接 每个玩家或每个 tcp 连接都有自己的线程 每个线程的运行情况如下 public void run try String packet char charCur ne
  • 什么是消息边界?

    什么是 消息边界 在以下情况下 TCP 和 UDP 之间的区别之一是 UDP 保留消息 边界 我理解之间的区别TCP and UDP 但我不确定的定义 消息边界 由于 UDP 在每个单独的数据包中包含目的地和端口信息 因此是否可以为消息提供
  • 在 C# 中通过 TCP 发送 C 结构体

    我正在编写一个程序 通过 TCP 与一台设备的管理界面进行交互 问题是 设备的文档是用C写的 而我写的程序是用C 写的 我的问题是 文档指定 通信基于基于C结构的API缓冲区 再多的谷歌搜索似乎也无法让我找到这个 API 或如何通过 TCP
  • 如何在NodeJS中测试socket.setKeepAlive

    我尝试在NodeJS中测试setKeepAlive 的功能 我在同一本地网络中的不同计算机上运行 Server js 和 client js 然后 我关闭了客户端计算机上的 WiFi 连接 断开互联网连接 15分钟后 仍然没有消息抛出 这是
  • Go TCP 读取是非阻塞的

    我正在尝试用 Go 创建服务器和客户端 我已经成功地与服务器和客户端进行通信 但我遇到的问题是golang中的TCP读取是非阻塞的 我想知道 golang 中的读取是否有可能像 C 中的读取一样阻塞 谢谢 EDIT 这是服务器的源代码 fu
  • 在 Python 中通过 TCP 套接字发送文件

    我已经成功地将文件内容 图像 复制到新文件 然而 当我通过 TCP 套接字尝试同样的事情时 我遇到了问题 服务器循环未退出 客户端循环在到达 EOF 时退出 但服务器无法识别 EOF 这是代码 Server import socket Im
  • 在 Golang Server 中接受持久的 tcp 连接

    我正在尝试使用 Go 并且想创建一个 TCP 服务器 我可以通过 telnet 访问该服务器 发送命令并接收响应 const CONN HOST localhost CONN PORT 3333 CONN TYPE tcp func mai
  • iOS 上的多个 HTTP 请求与单个 TCP 连接

    我正在开发一个 iPhone 应用程序 它使用我控制的基于 Web 的 API 连接到持续打开的 TCP 端口并通过 TCP API 发出请求 或者为我想要获取的所有数据发出新的 HTTP 请求 会更快或更高效吗 我认为差异可以忽略不计 但
  • TcpClient 在异步读取期间断开连接

    我有几个关于完成 tcp 连接的问题 客户端使用 Tcp 连接到我的服务器 在接受客户端后listener BeginAcceptTcpClient ConnectionEstabilishedCallback null 我开始阅读netw
  • ADO.NET SQLServer:如何防止关闭的连接持有S-DB锁?

    i Dispose http msdn microsoft com en us library system data sqlclient sqlconnection close aspx一个 SqlConnection 对象 但是当然它并

随机推荐

  • Windows下GCC安装和使用

    GCC是由GNU开发的编程语言译器 最近复现代码时需要编译源文件 xff0c 总是报错 xff0c 后来查验报错原因后 xff0c 是由于电脑没能安装GCC C 语言编译器用于把源代码编译成最终的可执行程序 但是本人不是很懂编译原理 xff
  • AUTOSAR——AUTOSAR基础

    一 AUTOSAR AUTOSAR全称为 AUTomotive Open System ARchitecture xff0c 译为 汽车开放系统体系结构 二 AUTOSAR核心思想 1 xff09 提倡 在标准上合作 xff0c 在实现上竞
  • 麦克纳姆轮(麦轮)原理

    一 麦轮原理 麦克纳姆轮 xff1a 简称麦轮 xff0c 由轮毂和围绕轮毂的辊子组成 辊子 xff1a 没有动力的从动小滚轮 麦克纳姆轮辊子轴线和轮毂轴线夹角是45度 A轮 xff08 左旋 xff09 与B轮 xff08 右旋 xff0
  • 数据结构——二叉树的先中后序遍历

    本节内容为Bilibili王道考研 数据结构 P43 P45视频内容笔记 目录 一 二叉树的先中后序遍历 1 先中后序遍历 2 举例 3 先中后序遍历和前中后缀的关系 4 代码实现 5 求遍历序列 6 应用 xff1a 求树的深度 二 二叉
  • Ubuntu下使用sshfs/fusemount挂载/卸载远程目录到本地

    Ubuntu下使用sshfs挂载远程目录到本地 访问局域网中其他Ubuntu机器 在不同机器间跳来跳去 很是麻烦 如果能够把远程目录映射到本地无疑会大大方面使用 xff0c 就像Windows下的网络映射盘一样 在Linux的世界无疑也会有
  • 反向代理解决跨域问题

    为什么会产生跨域 js采用的是同源策略 同源策略是指浏览器的一项安全策略 xff0c 浏览器只允许js代码请求和当前所在服务器域名 xff0c 端口号协议相同的数据接口上的数据 xff0c 这就是同源策略 也就是说当协议 xff0c 域名
  • C++ auto关键字 和 基于范围的for循环 语法糖

    目录 auto关键字基于范围的for循环 auto关键字 在C 43 43 11中 xff0c 规定了一个关键字 xff1a auto 下面看一下auto的用法 xff1a span class token keyword int span
  • ubuntu下ROS2-foxy中安装serial串口包

    1 新开一个终端 ctrl 43 alt 43 t 2 因为ros2中还没有集成serial串口包 xff0c 所以需要第三方下载对应serial串口包 xff0c 新建一个文件夹 xff0c 包名字为第三方库 mkdir Third pa
  • 简单输出一个数组

    简单输出一个数组 xff0c 对于数组 xff0c 我想每一个初始C语言的人 xff0c 刚刚经历过了for循环的喜悦 xff0c switch的喜庆 xff0c for循环的挣扎 因此 xff0c 数组应运而来 xff0c 当头一击 xf
  • 详解strstr函数:查找子字符串函数及其模拟实现

    详解strstr函数 xff1a 在一个字符串中查找另一共字符串是否存在 xff01 对于strstr查找子字符串 xff0c 笔者如果不是对C语言学习的更加深入 xff0c 可能还是不知道有这个函数 xff01 xff01 xff01 之
  • 51--定时器/计数器,串口,中断的巧妙关系

    目录 什么是中断 什么是定时器计数器 什么是串口 开始 直接配置中断 中断 43 定时器 计数器 串口 43 中断 总结 中断查询次序号 xff1a 什么是中断 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的 xff0c 中
  • 字符串查找函数Strstr函数的实现(详细易懂)

    首先 xff0c 字符串查找函数是在目的字符串中查找源字符串的首次出现的具体位置 xff0c 若找到了便返回该位置的地址 xff0c 若没有找到 xff0c 则返回空指针NULL char strstr const char arr1 co
  • ROS安装与Rviz的摄像头视频采集与标定

    文章目录 一 ROS的安装与配置1 添加 ROS 软件源 xff0c 将下列命令输入到 Ubuntu 的终端执行2 添加密钥 xff0c 将下列命令输入到 Ubuntu 的终端执行3 安装desktop full4 初始化rostep5 设
  • ros2的基本使用/基础知识——ros2节点

    1 节点是什么 每个节点只负责单独的模块 xff08 例如 xff1a 控制车轮转动 xff1b 从激光雷达处获得数据 xff1b 处理激光雷达的数据 xff1b 负责定位 xff09 照相机 地盘 控制 2 节点之间如何交互 xff1f
  • SSH秘钥登录配置与系统日志管理

    配置 sshd 服务 SSH xff08 Secure Shell xff09 是一种能够以安全的方式提供远程登录的协议 xff0c 也是目前远程管理 Linux 系统 的首选方式 在此之前 xff0c 一般使用 FTP 或 Telnet
  • 关于Ubuntu中Could not get lock /var/lib/dpkg/lock解决方案

    问题现象 xff1a root 64 gvt NUC6CAYH apt get install net tools E Could not get lock var lib dpkg lock frontend open 11 Resour
  • vscode报错vue/multi-word-component-names处理

    vue multi word component names是用于检测当前的组件名称是否使用驼峰或多单词命名 错误截图如下 xff1a 1 可以修改 eslintrc js文件的配置 module exports 61 root true
  • Android SDK的安装步骤

    1 Android SDK下载 https www androiddevtools cn 2 解压Android SDK压缩包 放在没有中文的目录里面 3 打开Android sdk windows文件夹 xff0c 双击SDK manag
  • Apollo control之PID算法

    Apollo studio 官网 xff1a Apollo开发者社区 baidu com 目录 1 PID简介 2 PID调参思路 3 代码 4 解决积分饱和的方法 4 1 IC 积分遇限削弱法 4 2 BC 反馈抑制抗饱和 1 PID简介
  • TCP通信模型(C语言实现)

    大家好 xff0c 我是练习编程时长两年半的个人练习生昆工第一ikun xff0c 今天我们来分享TCP通信模型 xff0c 并且用C语言实现它 目录 一 我们将实现三个示例功能 xff1a 二 TCP服务器搭建流程 xff08 1 xff