当前位置: 首页 > news >正文

TCP客户端Linux网络编程设计详解

一、TCP 客户端设计流程

        TCP客户端模式的程序设计流程主要分为:套接字初始化( socket()函数),连接目标网络服务器 (connect()函数),向服务器端写入数据(write()函数)

1、socket() 函数

#include <sys/types.h>     /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

        socket()函数函数建立一个协议族为domain、协议类型为type、协议编号为 protocol的套接字文件描述符。如果函数调用成功,会返回一个表示这个套接字的文件描述符,失败的时候返回-1。

        函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h 中定义。

        函数socket()的参数 type用于设置套接字通信的类型。主要有SOCK_STREAM(流式套接字)、 SOCK_DGRAM(数据包套接字)等。
函数socket()的参数protocol一般设置为0。
        函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获 得。通常情况下造成函数 socket()失败的原因是输入的参数错误造成的,例如某个协议不存在等, 这时需要详细检查函数的输入参数。由于函数的调用不一定成功,在进行程序设计的时候,一定要检查返回值。
如果进行TCP编程,我们可以使用如下代码建立一个流式套接字:
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);

2、connect() 函数原型

#include <sys/types.h>  /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd,  const struct sockaddr *addr,  socklen_t addrlen);
        参数sockfd是建立套接字时返回的套接字文件描述符,它是由系统调用socket()函数返回的。参数addr,是一个指向数据结构 sockaddr的指针,其中包括客户端需要连接的服务器的目的端口和IP地 址,以及协议类型。参数addrlen表示第二个参数内容的大小,可以使用sizeof(struct sockaddr)而 获得,与bind()函数不同,这个参数是一个整型的变量而不是指针。
connect()函数的返回值在成功时为0,当发生错误的时候返回-1,可以查看errno获得错误的原因。

(1)包裹函数

//connect包裹函数
void Connect(int fd, const struct sockaddr*addr,socklen_t addrlen)
{int ret;while (1){ret = connect(fd, addr, addrlen);if (-1 == ret){dbgout("connect error\n");perror("eror");continue;}break;}
}

3、数据的发送

1) write()函数用于发送数据,函数原型如下:

#include <sys/types.h>
#include <sys/socket.h>

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

参数:

        sockfd:TCP Socket 描述符。

        buf:要发送的数据缓冲区。

        count:要发送的字节数。

示例代码:

write(fd, "hello", sizeof("hello"));

2) send()函数, 原型如下:

#include <sys/types.h>
#include <sys/socket.h>

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

参数:

        sockfd:TCP Socket 描述符。

        buf:要发送的数据缓冲区。

        len:要发送的字节数。

        flags:可选的标志参数,用于控制发送行为,如 MSG_DONTWAIT、MSG_NOSIGNAL 等。

示例代码:

send(fd, "hello", sizeof("hello"), MSG_DONTWAIT)

4、数据的接收

读取客户端的数据可以使用以下三个函数:

1)read() 数据接收函数

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

         read()函数从套接字sockfd中接收数据放到缓冲区buf中,buf的长度为len。 第1个参数sockfd是套接口文件描述符,它是由系统调用socket()返回的。第2个参数buf是一个指针, 指向接收网络数据的缓冲区。第3个参数len表示接收缓冲区的大小,以字节为单位。

使用示例代码:

char buf[1024];
//读取数据
memset(buf, 0, sizeof(buf));
int ret;
ret = read(fd, buf, sizeof(buf)-1); //阻塞
//如果主动断开了连接,read函数解除阻塞,并且返回0
if (0 == ret)
{close(cli_fd);return NULL;
}printf("cli: %d,  %s\n", cli_fd, buf)

2)recv() 数据接收函数

recv()函数用于接收数据,函数原型如下:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

前三个跟read函数一致,recv()函数的参数flags用于设置接收数据的方式,可选择的值及含义在下表中列出(flags 的值可以是表中值的按位或生成的复合值)

上表中的值的具体说明:

MSG_DONTWAIT:这个标志将单个IO操作设为非阻塞方式,而不需要在套接字 上打开非阻塞标志,执行IO操作,然后关闭非阻塞标志。

MSG_ERRQUEUE:该错误的传输依赖于所使用的协议。

MSG_OOB:这个标志可以接收带外数据,而不是接收一般数据。

MSG_PEEK:这个标志用于查看可读的数据,在recv()函数执行后,内核不会将这些数据丢弃。 MSG_TRUNC:在接收数据后,如果用户的缓冲区大小不足以完全复制缓冲区中的数据,则将 数据截断,仅靠复制用户缓冲区大小的数据。其他的数据会被丢弃。

MSG_WAITALL:这个标志告诉内核在没有读到请求的字节数之前不使读操作返回。

示例代码:

char buf[1024];
//读取数据
memset(buf, 0, sizeof(buf));
int ret;
ret = recv(cli_fd, buf, sizeof(buf)-1,MSG_DONTWAIT); 
if (0 == ret)
{close(cli_fd);return NULL;
}printf("cli: %d,  %s\n", cli_fd, buf)

3)recvfrom() 数据接收函数

ssize_t recvfrom(int sockfd,  void *buf,  size_t len,  int flags, struct sockaddr *src_addr, socklen_t *addrlen)

        第四个参数保存接收的客户端的相关信息,但是因为TCP是基于连接的传输协议,在调用accept函数 时就能够获取客户端的信息,所以recvfrom一般用于udp通信。

7、示例TCP客户端编程代码:

#include <stdio.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define dbgout(arg...) \do{ \char b__[1024]; \sprintf(b__, arg);\fprintf(stdout, "[%s,%s,%d] %s", __FILE__, __func__, __LINE__, b__); \} while (0)//connect包裹函数
void Connect(int fd, const struct sockaddr*addr,socklen_t addrlen)
{int ret;while (1){ret = connect(fd, addr, addrlen);if (-1 == ret){dbgout("connect error\n");perror("eror");continue;}break;}
}int main(int argc, char* argv[])
{//初始化一个socket套接字int fd;fd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == fd){dbgout("socket error\n");perror("erro");return 0;}//连接服务器struct sockaddr_in serv;serv.sin_family = AF_INET;serv.sin_addr.s_addr = inet_addr("172.29.98.213");serv.sin_port = htons(8888);//和服务器建立连接Connect(fd, (struct sockaddr*)&serv, sizeof(struct sockaddr));char buf[128];while (1){//向服务端发送数据write(fd, "hello", 6);//读取服务端发送的数据memset(buf, 0, sizeof(buf));int ret = read(fd, buf, sizeof(buf) - 1);if (ret > 0){dbgout("read: %s\n", buf);}sleep(1);}close(fd);return 0;
}

http://www.dtcms.com/a/329068.html

相关文章:

  • docker+nginx+keepalived+openappsec+web ui+crowdsec部署安全代理
  • IDEA创建一个VUE项目
  • SVN提交服务器拒绝访问的问题
  • 服务器硬件电路设计之 I2C 问答(五):I2C 总线数据传输方向如何确定、信号线上的串联电阻有什么作用?
  • 从零开始搭建私服务器
  • opencv:直方图
  • 【AI论文】GLM-4.5:具备智能体特性、推理能力与编码能力的(ARC)基础模型
  • Visual Studio Code 跨平台快捷键指南:Windows 与 macOS 全面对比
  • 第十三节:后期处理:效果增强
  • 开发避坑指南(24):RocketMQ磁盘空间告急异常处理,CODE 14 “service not available“解决方案
  • 2025年,Javascript后端应该用 Bun、Node.js 还是 Deno?
  • python基于Hadoop的超市数据分析系统
  • 高防CDN和高防IP的各自优势
  • Sklearn 机器学习 异常值检测 孤立深林
  • 《设计模式之禅》笔记摘录 - 15.观察者模式
  • 【完整源码+数据集+部署教程】军事伪装目标分割系统源码和数据集:改进yolo11-EMSC
  • 最新去水印小程序系统 前端+后端全套源码 多套模版 免授权
  • Four.Meme 重大更新:Bonding Curve Cap 从 24 BNB 降至 18 BNB,这意味着什么?
  • 浏览器面试题及详细答案 88道(23-33)
  • 【密码学实战】国密SM2算法介绍及加解密/签名代码实现示例
  • 用了Cursor AI之后,我的编程效率翻倍了?——一位程序员的真实体验分享
  • Java毕业设计选题推荐 |基于SpringBoot的水产养殖管理系统 智能水产养殖监测系统 水产养殖小程序
  • 二层业务端口相关配置
  • STM32H743开发周记问题汇总(串口通讯集中)
  • 免费生成视频,Coze扣子工作流完全免费的视频生成方案,实现图生视频、文生视频
  • Windows 系统 上尝试直接运行 .sh(Shell 脚本)文件
  • 从感知到执行:人形机器人低延迟视频传输与多模态同步方案解析
  • 基于大数据spark的医用消耗选品采集数据可视化分析系统【Hadoop、spark、python】
  • ABP vNext 的工业时间序列治理:InfluxDB vs TimescaleDB 落地对比
  • Python 环境隔离实战:venv、virtualenv 与 conda 的差异与最佳实践