C语言通过socket实现TCP客户端

news/2025/3/13 20:00:48/文章来源:https://www.cnblogs.com/crazy3min/p/18235928

socket概念

​ 从wiki上了解,socket这个词追溯到 1971 年 RFC 147 的发布。

​ 目前我的理解:常用于指操作系统提供的 API,该 API 允许使用 TCP、UDP 进行连接,但不仅限于 TCP、UDP 协议。

实现目的

利用系统提供函数接口,通过C语言实现对TCP 服务器(IP地址)的连接,以及收发数据。

实现过程

1、socket(2) 创建套接字

2、connect(2) 连接服务器。服务器已打开,否则会直接返回错误。

3、send(2) 向服务器发送数据。连接成功后,即可与服务器通信。

4、recv(2) 接收服务器发送过来的数据。

5、close(2) 关闭套接字。

实现代码

/****************************************************************************** file name: mytcp_client.c* author   : crazy3min@outlook.com* date     : 2024-06-05* function : TCP协议的客户端操作。* note     :*              测试编译指令: gcc ./src/mytcp_client.c ./src/mytime.c -o ./bin/mytcp_client -I ./include*              通过命令行输入服务器ip和端口,示例:./bin/mytcp_client IP PORT** CopyRight (c)   2024   crazy3min@outlook.com   Right Reseverd*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <netinet/in.h>
#include "mytime.h" //时间头文件#define DEBUG // 开启调试模式/******************************* 全局变量 START *******************************/
char timebuf[128]; // 时间输出缓冲区
/******************************* 全局变量 END *******************************//****************************************************************************** function name     : tcp_v4_hton* function          : 将ipv4服务器的信息从本地字节序转换为网络字节序,并存储在destinfo指针下。* parameter         :*                      @destinfo: 存储转换后的信息指针。*                      @address: 需要转换的点分十进制IPv4地址,例如 "192.168.5.1"*                      @port: 需要转换的端口,例如:60000** return value      : None* note              : None** author            : crazy3min@outlook.com* date              : 2024-06-06* version           : V1.0* revision history  : None*****************************************************************************/
void tcp_v4_hton(struct sockaddr_in *destinfo, const char *address, const int port)
{destinfo->sin_family = AF_INET;                 // 协议,AF_INET代表IPV4协议destinfo->sin_port = htons(port);               // 服务器端口,必须将目标端口转为网络字节序(大端)destinfo->sin_addr.s_addr = inet_addr(address); // 服务器ip,必须将目标ip转为网络字节序(大端)
}/****************************************************************************** function name     : tcp_v4_connect* function          : 连接IPv4 TCP服务器* parameter         :*                      @socketfd: socket指针。*                      @destinfo: 储存 IPv4 TCP服务器信息的指针** return value      : 成功返回true,失败返回false* note              : None** author            : crazy3min@outlook.com* date              : 2024-06-06* version           : V1.0* revision history  : None*****************************************************************************/
bool tcp_v4_connect(int *socketfd, struct sockaddr_in *destinfo)
{// 创建ipv4 TCP 通信端点*socketfd = socket(destinfo->sin_family, SOCK_STREAM, 0);if (-1 == *socketfd){fprintf(stderr,"[%s] [%s] 创建ipv4 TCP 通信端点,Error code: %d, Error message: %s\n",__FILE__,__func__,errno,strerror(errno));return false;}// 请求连接 tcp服务器if (-1 == connect(*socketfd, (const struct sockaddr *)destinfo, sizeof(struct sockaddr_in))){fprintf(stderr,"[%s] [%s] 连接ipv4 TCP 通信端点失败,Error code: %d, Error message: %s\n",__FILE__,__func__,errno,strerror(errno));return false;}// 成功连接
#ifdef DEBUGtime_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[DEBUG][%s]成功连接服务器 \n", timebuf);
#endifreturn true;
}/****************************************************************************** function name     : tcp_v4_send* function          : 向IPv4 TCP服务器发送一条数据* parameter         :*                      @socketfd: 已连接服务器的socket句柄*                      @buf: 存储待发送数据的缓冲区指针。*                      @bufsize: 数据的大小,单位字节。** return value      : 成功返回true,失败返回false* note              :*                      必须先使用tcp_v4_connect()连接服务器后再使用。** author            : crazy3min@outlook.com* date              : 2024-06-06* version           : V1.0* revision history  : None*****************************************************************************/
bool tcp_v4_send(const int socketfd, const char *buf, const int bufsize)
{if (bufsize != send(socketfd, buf, bufsize, 0)){
#ifdef DEBUGtime_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[DEBUG][%s] 发送 [%s] 失败!!!\n", timebuf, buf);
#endifreturn false;}#ifdef DEBUGtime_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[DEBUG][%s] 成功发送 [%s] \n", timebuf, buf);
#endifreturn true;
}/****************************************************************************** function name     : tcp_client_recv* function          : 线程任务,连接TCP IPv4服务器后,接收服务器发来的信息并输出。* parameter         :*                      @arg: 已连接服务器的socket句柄指针** return value      : None* note              :*                      必须先使用tcp_v4_connect()连接服务器后再使用。** author            : crazy3min@outlook.com* date              : 2024-06-06* version           : V1.0* revision history  : None*****************************************************************************/
void *tcp_client_recv(void *arg)
{int socketfd = *((int *)arg); // 转换通过参数传入socket套接字句柄char buffer[512] = {0};       // 接收数据缓冲区// 循环阻塞等待服务器发送的数据while (1){if (0 == (recv(socketfd, buffer, sizeof(buffer), 0))){// 服务器终止连接,结束程序time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[%s] 服务器已经终止连接\n", timebuf);close(socketfd);exit(EXIT_SUCCESS);}time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[%s]收到服务器发来的信息:[%s]\n", timebuf, buffer);bzero(buffer, sizeof(buffer)); // 清空缓存}
}int main(int argc, char const *argv[])
{// 通过终端传入服务器的信息if (3 != argc){printf("参数无效,请输入 [./xxx IP PORT] 执行\n");return -1;}int socketfd;                // 创建套接字char buffer[512] = {0};      // 发送数据缓冲区struct sockaddr_in destinfo; // 定义IPv4 地址和端口的结构体 变量保存服务端信息tcp_v4_hton(&destinfo, argv[1], atoi(argv[2])); // 转换为网络字节序// 连接服务器if (!(tcp_v4_connect(&socketfd, &destinfo)))return -1;// 创建线程接收服务器发送的信息pthread_t tcp_recv_task;if (0 != (pthread_create(&tcp_recv_task, NULL, tcp_client_recv, &socketfd))){printf("********** 创建接收服务器发送的信息线程失败! **********\n");}
#ifdef DEBUGelse{time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[DEBUG][%s] 成功创建接收服务器发送的信息线程 \n", timebuf);}
#endif// 需要发送的信息while (1){printf("请输入发送的数据:\n");fgets(buffer, sizeof(buffer), stdin); // 标准输入获取数据buffer[strcspn(buffer, "\n")] = '\0'; // 替换换行符if (0 == strlen(buffer)){printf("********** 请输入有效信息 **********\n");}else if (!(tcp_v4_send(socketfd, buffer, strlen(buffer)))){time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");printf("[%s] 服务器已经终止连接\n", timebuf);close(socketfd);break;}else{bzero(buffer, sizeof(buffer)); // 清空缓存}}return 0;
}

测试结果

C语言TCP客户端测试

参考信息

  • TCP/IP 简介(第 4 部分)- 套接字和端口

衍生问题

  • 什么是Berkeley Sockets?

​ Berkeley 套接字是用于创建和使用套接字的行业标准应用程序编程接口 (API)。它最初被用作 Unix 操作系统的 API,后来被 TCP/IP 采用。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一键云部署:资源编排 ROS 轻松部署 LLM 流程编排服务 Flowise

Flowise是一个开源的低代码工具,供开发者构建定制化的 LLM 流程编排和 AI 代理。资源编排服务(Resource Orchestration Service, ROS)是阿里云提供基于基础设施即代码(Infrastructure as Code, IaC) 理念的自动化部署服务,我们可以通过定义一个 JSON/YAML/Terraform 模板…

CUDA程序优化-1.基础介绍

介绍CUDA的基本概念和架构,帮助读者建立对CUDA的初步认识,包括硬件架构/CUDA基础等内容简介 本合集主要介绍我在开发分布式异构训练框架时的CUDA编程实践和性能优化的相关内容。主要包含以下几个部分:介绍CUDA的基本概念和架构,帮助读者建立对CUDA的初步认识,包括硬件架构/…

第二次博客作业

前言 这次的作业是第一次博客作业的迭代和新的一个大作业的两次迭代三次大作业的组合。 1.第四次大作业 这次大作业输入信息分为5种:题目信息、试卷信息、答卷信息、学生信息、删除题目信息。信息可能会打乱且顺序混合输入。需要正则表达式判断是否满足格式,只有正确的格式才…

第二次blog总结

一、前言:第四次pta作业: 1.知识点:考察类的继承,抽象类的使用。 2.题量:3道简单题,1道迭代难题。 3.难度:这次最后一道题在上次的基础上添加了题目的种类( eg: 选择题、填空题 )等,难度增加的还行,不算太难也不算太容易。第五次pta作业: 1.知识点:考察类实现Comp…

检修日记点滴

5.30 周四 今天是第一天到检修班组学习,今天的主要任务比较简单.主要是熟悉班组情况,了解热工工作职责和任务。上午跟前辈咨询了检修工作的大体情况,相关工作纪律,然后队长分配了跟班师傅,刚好遇到的师傅在检修室工作,好巧不巧还是熟人,足球队一起踢球的,顿时亲切感十足.上午跟…

【VMware vSphere】安装配置Update Manager Download Service(UMDS)作为 vLCM 的下载存储库。

VMware vSphere Update Manager Download Service (UMDS) 是 vSphere Lifecycle Manager(vLCM) 的可选模块。我在之前文章中提到这个功能,当 vSphere 环境能够连接 Internet 时,我们可以使用 vLCM 的在线 Internet 下载源获取修补程序,当 vSphere 环境不能连接 Internet 时…

西方那么发达的营养学和医学,为啥长寿最多的人群在日本

哈喽,大家好,我是木头左!一、引言 当谈论长寿时,可能会首先想到的是日本。据统计,日本的人均寿命在全球名列前茅,远远超出许多西方国家。那么,为什么在营养学和医学技术发达的西方国家中,日本的寿命最长呢? 二、日本的长寿文化 日本的长寿与其独特的饮食文化和生活方式…

题目集4~6的总结性Blog

4~6次大作业总结 前言 在题目的开始设计好类和结构真的很重要,之前一直上来就开始写,后来就变得吃力,这样不能很好的一次性完成代码,发现应该先设计,在着手编写代码之前,可以先绘制类图,明确每个类的属性和方法,以及它们之间的关系。这有助于构建清晰的代码结构和逻辑,…

Unity Memory Profiler 内存分析

参考:Unity Memory Profiler从入门到精通-CSDN博客 Unity Memory Profiler 是一个用于分析和调试 Unity 游戏或应用程序内存使用情况的工具。它帮助开发者了解应用程序的内存分配和使用情况,从而识别和解决潜在的内存问题,如内存泄漏、过度的内存分配和不必要的内存消耗。 U…

密码工程-大素数

任务详情 在openEuler(推荐)或Ubuntu或Windows(不推荐)中完成下面任务 利用大整数库(GMP或者OpenSSL),参考《密码工程》p113伪代码实现 GenerateLargePrime 函数(10‘) 在测试代码中产生一个在范围l = 2^255至u = 2^256-1内的素数。(5‘) 用OpenSSL验证你产生的素数是不是…

西数SMR硬盘二级译码表结构(190模块)

西数(WD)的二级译码表对应的模块文件为190模块。一,190模块的作用: 用于记录LBA对应的物理位置(磁头,柱面,扇区),即实现对逻 辑地址与物理地址的转换。由于WD已经有31模块负责此项转换工作,因此190模块被称之为二级译码表,简称T2。 二,文件头部结构(地址:0): 0-3:文…

字体样式和文本样式

2.2字体样式 /* font-family:字体 font-weight:字体粗细 font-size:字体大小 color:字体颜色 */ <style> body{ font-family: 楷体; color: red; } .p1{ font-weight: bold; } h…