相关文章推荐

调用系统API时,经常会由于操作不当导致系统函数调用发生错误,而系统API也是比较友好的,会给你一些特殊的返回值,普遍返回-1,同时,会设置一些变量,表示错误类型。在Windows中,调用 GetLastError ,可以得到最近的调用失败的错误码;在Linux中,“全局变量” errno 记录了最近调用失败的错误码。

这里纠正一下, errno 其实并不是全局变量, errno 的作用是 thread local ,在线程中是全局的。

当然,这可以在代码中手动添加代码来得到这些错误码,从而推断出错误原因;

但是,我们不想去改代码,想在调试中就推断出系统API调用失败的原因怎么办,有办法的。

这里通过今天在Windows和Linux下创建原始套接字失败直接返回-1进行调试。

Windows

Windows一般用Visual Studio来写代码。而Visual Studio号称宇宙最强IDE,必然有一些方便我们的地方。

我们可以在调用API失败处打上一个断点,然后在监视器中填上 $err,hr ,然后向下执行这个系统调用,如果失败,那么监视器中就会显示错误代码以及 错误的原因描述 ,这个真的很方便。

int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

socketFd为-1,运用上面的方法,得到下面的图片。

看到这条信息,我就知道错误原因了,Windows下调用套接字函数有那么一套必须的代码流程:

#include <WinSock2.h>
#pragma comment(lib, "WS2_32")
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);

加上这段代码就能正常调用socket函数了。

Linux

Linux下,都是GNU那一套,用的是GDB调试,同样也是打断点,执行系统调用,然后在GDB中输入p errno,查看错误代码,注意,Linux下远没有Visual Studio那么方便,所以,得到了错误码,你要自己去查看错误码对应的错误原因;

这里有个办法,使用man errno及查看errno.h头文件。

man errno会得出一些宏观及其对应的错误原因,但是这些宏的值是多少?这个就要看头文件了。

不同的Linux版本,不同的G++版本,errno.h中的定义有点不一样,这个要特殊情况特殊分析,举个例子,Ubuntu 14.04 LTS中,想看到errno.h,这个头文件就要费点功夫了,得用find或者grep/usr/include中找,且,头文件可能还套头文件,其中,Ubuntu 14.04 LTS的错误码宏定义在/usr/include/asm-generic/errno-base.h以及/usr/include/asm-generic/errno.h中,这里和MinGW的头文件errno.h对比下,看看这些宏定义的值有哪些不一样。

// /usr/include/asm-generic/errno-base.h
#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */
// MinGW/include/error.h
#define EPERM		1	/* Operation not permitted */
#define	ENOFILE		2	/* No such file or directory */
#define	ENOENT		2
#define	ESRCH		3	/* No such process */
#define	EINTR		4	/* Interrupted function call */
#define	EIO		5	/* Input/output error */
#define	ENXIO		6	/* No such device or address */
#define	E2BIG		7	/* Arg list too long */
#define	ENOEXEC		8	/* Exec format error */
#define	EBADF		9	/* Bad file descriptor */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Resource temporarily unavailable */
#define	ENOMEM		12	/* Not enough space */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
/* 15 - Unknown Error */
#define	EBUSY		16	/* strerror reports "Resource device" */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Improper link (cross-device link?) */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* Too many open files in system */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Inappropriate I/O control operation */
/* 26 - Unknown Error */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Invalid seek (seek on a pipe?) */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Domain error (math functions) */
#define	ERANGE		34	/* Result too large (possibly too small) */
/* 35 - Unknown Error */
#define	EDEADLOCK	36	/* Resource deadlock avoided (non-Cyg) */
#define	EDEADLK		36
/* 37 - Unknown Error */
#define	ENAMETOOLONG	38	/* Filename too long (91 in Cyg?) */
#define	ENOLCK		39	/* No locks available (46 in Cyg?) */
#define	ENOSYS		40	/* Function not implemented (88 in Cyg?) */
#define	ENOTEMPTY	41	/* Directory not empty (90 in Cyg?) */
#define	EILSEQ		42	/* Illegal byte sequence */

可以看出,大部分通用的错误代码的宏定义还是一样的,不过也有一些区别,这很正常。而且,头文件中有注释,这些就是错误的原因。

int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

于是在Linux下,在GDB中也运用这个方法:

发现错误原因是Operation not permitted,这说明我们权限不够,然后查资料,得出,Linux下root用户才能创建原始套接字。

这些只是一些技巧,能解决问题的方法有很多,选择适合自己的就行。

搜索了个C++ 的tcp断线重连的案例(http://www.cnblogs.com/kingdom_0/articles/2571727.html),使用这个的原因还因其使用的是收发。在工控上经常用到tcp连接,比如串口服务器或某些支持modbustcp协议的仪表等,以前尽量使用串口服务器的虚拟串口功能,现在逐步使用上了tcpserver或tcpclient模式。本文主要参考原文链接:https://blog.csdn.net/gongzhu110/article/details/83147994。 来源:zxg623链接:http://blog.chinaunix.net/uid-11848011-id-96439.html一、概述TCP(传输控制协议)和UDP(用户数据报协议是网络体系结TCP/IP模型传输层一层的两个不同的通信协议。TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流,TCP套接口是字节流套接口(stream socket)的一种。UDP:用户... 在Linux实现抓取以太网络上的数据包主要有libpcap,raw socket以及从内核获取的方式。 我尝试选择使用raw socket来抓取以太网络上的数据包。 一、socket地址域(协议簇) 在Linux的manual,有两个篇包含socket相关的说明,分别是socket(2)和socket(7),这两篇socket定义不完全相同: socket(2): int socket(int domain, int type, int protocol); socket(7): mysocket 连接到远程主机(作为客户)    让我们尝试下用简单的能连接远程电脑的代码能解决什么。这样能让你更好的理解所有这些是怎么工作的以及防止出现混乱。 你需要找出你所要连接的远程计算机的信息,然后传递一个指针到这个神奇的函数结构,connect(),这个结构以及API见下面的列表,注意形式参数sin_zero是不需要的所以为空值   struct sockaddr_in   short 2 #include&lt;winsock2.h&gt; 3 #include&lt;stdio.h&gt; 4 #pragma comment(lib,"WS2_32.lib")//显示连接套接字库 5 int main() 7 WSADATA data;//定义WSADATA结...
 
推荐文章