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 : [email protected]
* 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 [email protected] 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 : [email protected]
* 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 : [email protected]
* 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 DEBUG
time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");
printf("[DEBUG][%s]成功连接服务器 \n", timebuf);
#endif
return true;
}
/****************************************************************************
*
* function name : tcp_v4_send
* function : 向IPv4 TCP服务器发送一条数据
* parameter :
* @socketfd: 已连接服务器的socket句柄
* @buf: 存储待发送数据的缓冲区指针。
* @bufsize: 数据的大小,单位字节。
*
* return value : 成功返回true,失败返回false
* note :
* 必须先使用tcp_v4_connect()连接服务器后再使用。
*
* author : [email protected]
* 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 DEBUG
time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");
printf("[DEBUG][%s] 发送 [%s] 失败!!!\n", timebuf, buf);
#endif
return false;
}
#ifdef DEBUG
time_format(timebuf, sizeof(timebuf), "%Y年%m月%d日 %H:%M:%S");
printf("[DEBUG][%s] 成功发送 [%s] \n", timebuf, buf);
#endif
return true;
}
/****************************************************************************
*
* function name : tcp_client_recv
* function : 线程任务,连接TCP IPv4服务器后,接收服务器发来的信息并输出。
* parameter :
* @arg: 已连接服务器的socket句柄指针
*
* return value : None
* note :
* 必须先使用tcp_v4_connect()连接服务器后再使用。
*
* author : [email protected]
* 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 DEBUG
else
{
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;
}
测试结果
参考信息
衍生问题
- 什么是Berkeley Sockets?
Berkeley 套接字是用于创建和使用套接字的行业标准应用程序编程接口 (API)。它最初被用作 Unix 操作系统的 API,后来被 TCP/IP 采用。
标签:socketfd,socket,buffer,timebuf,tcp,C语言,TCP,服务器 From: https://www.cnblogs.com/crazy3min/p/18235928