linux c connect 权限,Linux C语言 connect 的超时设置(含源码和例程)

0x01 关键步骤和相关函数

网络编程中默认情况下进入connect函数,会一直等待连接结束。超时等待设置关键在于

1、将socket置为非阻塞后

2、设定超时等待时间

3、时间结束后读取socket状态,进行判断

1、设置socket为非阻塞

记录下两种设置socket为非阻塞方式,分别是fcntl() 和 ioctl() 两个函数

fcntl()

#include #include /*********************************************************************

* Function : fcntl

* Description : 根据文件描述符操作文件特性

* Parameter :

* @fd 文件描述符

* @cmd 操作命令:

* F_DUPFD : 复制文件描述词。

* FD_CLOEXEC : 设置close-on-exec标志

* F_GETFD : 读取文件描述词标志

* F_SETFD : 设置文件描述词标志

* F_GETFL : 读取文件状态标志

* F_SETFL : 设置文件状态标志

* ...

* @arg 供命令使用的参数

*

* Return : int

*

* Usage :

* int fcntl(int fd, int cmd);

* int fcntl(int fd, int cmd, long arg);

* int fcntl(int fd, int cmd, struct flock *lock);

*

* 参考 man 或:

* https://blog.csdn.net/weixin_34362875/article/details/86340074

*

*********************************************************************/

int fcntl(int fd, int cmd, ... /* arg */ );

ioctl()

#include /*********************************************************

* Function : ioctl

* Description : 设备驱动程序中对设备的I/O通道进行管理的函数

* Parameter :

* @fd 文件描述符

* @request 操作命令:

* FIONBIN : 设置/ 清除非阻塞I/O 标志

* FIOASYNC : 设置/ 清除信号驱动异步I/O 标志

* FIONREAD : 获取接收缓存区中的字节数

* FIOSETOWN : 设置文件的进程ID 或进程组ID

* FIOGETOWN : 获取文件的进程ID 或进程组ID

* ...

* @arg 供命令使用的参数

*

* Return : int

*

* 参考 man 或:

* https://www.cnblogs.com/tdyizhen1314/p/4896689.html

* 上文详细介绍了该函数的用途

* 以及request对应参数所需要提供的arg类型

*********************************************************/

int ioctl(int fd, unsigned long request, .../* arg */);

2、超时等待

select() 多路复用

#include #include #include #include /*********************************************************

* Function : select

* Description : 同步I/O多路复用

* Parameter :

* @nfds 文件描述符+1

* @readfds 可读文件描述符词组

* @writefds 可写文件描述符词组

* @exceptfds 例外文件描述符词组

* @timeout 等待时间

*

* Return : int

*

* 参考 man 或:

* https://blog.csdn.net/lucykingljj/article/details/43198889

*

*********************************************************/

int select(int nfds, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds, struct timeval *timeout);

/**

* 相关函数

*/

void FD_ZERO(fd_set *set); // 清除描述符词组set的全部位

void FD_CLR(int fd, fd_set *set); // 清除描述符词组set中相关fd的位

void FD_SET(int fd, fd_set *set); // 设置描述符词组set中相关fd的位

int FD_ISSET(int fd, fd_set *set); // 测试描述符词组set中相关fd的位是否为真

3、获取socket状态

#include #include /*********************************************************

* Function : getsockopt

* Description : 返回指定socket的状态

* Parameter :

* @sockfd socket的文件描述符

* @level 操作的网络层, 一般设置为SOL_SOCKET

* @optname 操作的选项:

* SO_DEBUG 打开或关闭排错模式

* SO_REUSEADDR 允许在bind ()过程中本地地址可重复使用

* SO_TYPE 返回socket 形态.

* SO_ERROR 返回socket 已发生的错误原因

* SO_DONTROUTE 送出的数据包不要利用路由设备来传输.

* SO_BROADCAST 使用广播方式传送

* SO_SNDBUF 设置送出的暂存区大小

* SO_RCVBUF 设置接收的暂存区大小

* SO_KEEPALIVE 定期确定连线是否已终止.

* SO_OOBINLINE 当接收到OOB 数据时会马上送至标准输入设备

* SO_LINGER 确保数据安全且可靠的传送出去.

* @optval 保存结果的内存地址

* @optlen optval空间的大小

*

* Return : 成功则返回0, 若有错误则返回-1, 错误原因存于errno

* errno:

* EBADF 参数s 并非合法的socket 处理代码

* ENOTSOCK 参数s为一文件描述词, 非socket

* ENOPROTOOPT 参数optname指定的选项不正确

* EFAULT 参数optval指针指向无法存取的内存空间

*

* 参考 man 或:

* http://www.cnblogs.com/dpf-learn/p/6124170.html

*

*********************************************************/

int getsockopt(int sockfd, int level, int optname,

void *optval, socklen_t *optlen);

0x02 例程代码

本例程为一份检测本地与目标端口连接是否活跃的程序,只封装了建立一个普通TCP带有超时参数的客户端connect函数。

头文件 - Network.h

/*************************************************************************

> File Name: Network.h

> Author: WangMinghang

> Mail: hackxiaowang@qq.com

> Blog: https://www.wangsansan.com

> Created Time: 2019年04月29日 星期一 10时32分21秒

************************************************************************/

#ifndef __USER_NETWORK_H__

#define __USER_NETWORK_H__

/************************************************************

* Function : create_connect

* Description : 创建一个TCP连接的客户端socket, 并设置超时时间

* Parameter :

* @host 主机域名或IP地址

* @port 目标端口

* @s 连接超时时间, 单位: 秒

* Return :

* 连接成功 - 返回socket描述符

* 连接失败 - 返回值小于0

* -1 : 创建socket失败

* -2 : 连接超时

*

************************************************************/

int create_connect(const char *host, int port, int s);

#endif

main 主程序 - Network_test.c

/*************************************************************************

> File Name: Network_test.c

> Author: WangMinghang

> Mail: hackxiaowang@qq.com

> Blog: https://www.wangsansan.com

> Created Time: 2019年04月29日 星期一 10时32分21秒

************************************************************************/

#include #include #include #include "Network.h"

#define TIMEOUT 3

#define SERVER_PORT 80

#define SERVER_HOST "www.wangsansan.com"

void delay_ms(int timeout);

int main(int argc, char **argv)

{

int sock_fd = -1;

while(1)

{

sock_fd = create_connect(SERVER_HOST, SERVER_PORT, TIMEOUT);

if(sock_fd <= 0){

printf("Connect failed - %d \n", sock_fd);

}else{

close(sock_fd);

printf("Connect success - %d \n", sock_fd);

}

/* 在网络通畅时, 如果不加延时会消耗资源 */

delay_ms(1000);

}

}

/* 毫秒定时器 */

void delay_ms(int timeout)

{

struct timeval timer;

timer.tv_sec = 0; // 0秒

timer.tv_usec = 1000*timeout; // 1000us = 1ms

select(0, NULL, NULL, NULL, &timer);

}

源代码 - Network.c

/*************************************************************************

> File Name: Network.c

> Author: WangMinghang

> Mail: hackxiaowang@qq.com

> Blog: https://www.wangsansan.com

> Created Time: 2019年04月29日 星期一 10时32分21秒

************************************************************************/

#include #include #include #include #include #include #include #include #include #include #include #include "Network.h"

int socket_create();

int socket_timeout(int sockfd, int s);

int socket_connect(int sockfd, const char* server, int port);

int create_connect(const char *host, int port, int s)

{

int re = -1;

int sock_fd = -1;

unsigned long ul;

// 创建 socket

sock_fd = socket_create();

if(sock_fd <= 0){

return -1;

}

// 设置非阻塞

ul = 1;

ioctl(sock_fd, FIONBIO, &ul);

// 连接 socket

re = socket_connect(sock_fd, host, port);

if(re == 1){

// 设置为阻塞

ul = 0;

ioctl(sock_fd, FIONBIO, &ul);

return sock_fd;

}

// 设置超时时间

re = socket_timeout(sock_fd, s);

if(re <= 0){

close(sock_fd);

return -2;

}

// 设置为阻塞

ul = 0;

ioctl(sock_fd, FIONBIO, &ul);

return sock_fd;

}

int socket_timeout(int sockfd, int s)

{

int re = 0;

fd_set set;

struct timeval tm;

int len;

int error = -1;

tm.tv_sec = s;

tm.tv_usec = 0;

FD_ZERO(&set);

FD_SET(sockfd, &set);

re = select(sockfd + 1, NULL, &set, NULL, &tm);

if(re > 0){

len = sizeof(int);

// 获取socket状态

getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);

if(error == 0){

re = 1;

}else{

re = -3;

}

}else{

re = -2;

}

return re;

}

int socket_connect(int sockfd, const char *server, int port)

{

int re = -1;

struct hostent *host;

struct sockaddr_in cliaddr;

// 域名解析

host = gethostbyname(server);

if(host == NULL){

printf("gethostbyname(%s) error:%s\n", server, strerror(errno));

re = -1;

return re;

}

// 填充socket的IP与端口

bzero(&cliaddr, sizeof(struct sockaddr));

cliaddr.sin_family = AF_INET;

cliaddr.sin_port = htons(port);

cliaddr.sin_addr = *((struct in_addr *)host->h_addr);

// 客户端连接

re = connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(struct sockaddr));

if(re >= 0){

return 1;

}

return re;

}

int socket_create()

{

int sockfd;

sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd < 0){

perror("create socket error");

return -1;

}

return sockfd;

}

关键位置都有注释说明,非阻塞设置方式使用的是ioctl()函数,在连接完成之后又将socket设置为阻塞,有兴趣的可以在后续代码中添加send和recv的疯转,对socket进行操作。

参考:

https://blog.csdn.net/wangyifei0822/article/details/2171314

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值