1 信号函数 sigaction()
1.1 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- 函数作用 :: 在接收到参数signum指定的信号后应该采取的行动。sigaction至少包括下面几个成员 :
struct sigaction { void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; }
说明
sigaction函数设置与信号signum关联。如果oldact不是指针,sigaction将会把原来的信号保存到oldact所指的位置。如果为空,不动作。
- sa_sigaction是一个函数指针,它指向接收到信号signum时将被调用的的信号处理函数。 可以特殊值SIG_ING和SIG_DFL
- sa_mask字段指定了一个信号集, 在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽中。它可以防止信号在它的处理函数还未运行结束时就被接收到情况,它可以消除这一竞态条件。
- sa_flags 可以重置调用前的信号,一般为0,也可是SA_RESETHAND,还可以是其他的,具体看说明。
- 一个简单程序
#include <signal.h> #include <stdio.h> #include <unistd.h> void ouch(int sig) { printf("OUCH! - I got signal %d/n", sig); } int main() { struct sigaction act; /*设置必要的参数*/ act.sa_handler = ouch; sigemptyset(&act.sa_mask); act.sa_flags = 0; if( sigaction(SIGINT, &act, 0) == -1 ) { printf("error!/n"); return -1; } while(1) { printf("Hello World!/n"); sleep(1); } }
按下Ctrl+C可以看到一条消息。按Ctrl+退出。
2 定时器
#include <sys/time.h>
int getitimer(int which, struct itimerval *value); int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
取值 | 含义 | 信号发送 |
---|---|---|
ITIMER_REAL | 定时真实时间,与alarm类型相同。 | SIGALRM |
ITIMER_VIRT | 定时进程在用户态下的实际执行时间。 | SIGVTALRM |
ITIMER_PROF | 定时进程在用户态和核心态下的实际执行时间 | SIGPROF |
setitimer设置的定时器则不同,它们不但可以计时到微妙(理论上), 还能自动循环定时。在一个Unix进程中,不能同时使用alarm和 ITIMER_REAL类定时器。 结构itimerval描述了定时器的组成: struct itimerval { struct tim. it_interval; /* 下次定时取值 */ struct tim. it_value; /* 本次定时设置值 */ } 结构tim.描述了一个精确到微妙的时间: struct tim. { long tv_sec; /* 秒(1000000微秒) */ long tv_usec; /* 微妙 */ } 函数setitimer设置一个定时器,参数value指向一个itimerval结构, 该结构决定了设置的定时器信息,结构成员it_value指定 首次定时的时间,结构成员it_interval指定下次定时的时间。定时器工作时, 先将it_value的时间值减到0,发送一个信号,再将 it_value赋值为it_interval的值,重新开始定时,如此反复。 如果it_value值被设置为0,则定时器停止定时;如果 it_value值不为0但it_interval值为0,则定时器在一次定时后终止。 函数setitimer调用成功时返回0,否则返回-1,参数ovalue如果不为空, 返回上次的定时器状态。 函数getitimer获取当前的定时器状态,整型参数which指定了读取的定时器类型, 参数value返回定时器状态。函数调用成功返回0,否则返回-1。 例1. 设置一个定时器,每2.5秒产生一个SIGALRM信号。 答:将itimerval结构的成员it_interval和成员it_value均赋值为2.5秒即可: struct itimerval value; value.it_value.tv_sec=2; value.it_value.tv_usec=500000; value.it_interval.tv_sec=2; value.it_interval.tv_usec=500000; setitimer(ITIMER_REAL, &value, NULL); 函数setitimer设置的定时器可以重复定时,无需多次调用。 例2. 设置一个定时器,进程在用户态下执行1秒钟后发出首次信号,以后进程每在 用户态下执行3秒钟,发送一个信号。 答:将itimerval结构的成员it_value均赋值为1秒,成员it_interval赋值为3秒即可: struct itimerval value; value.it_value.tv_sec=1; value.it_value.tv_usec=0; value.it_interval.tv_sec=3; value.it_interval.tv_usec=0; setitimer(ITIMER_VIRT, &value, NULL); 例3. 取消一个ITIMER_PROF类定时器。 答:将itimerval结构的成员it_value均赋值为0秒即可: struct itimerval value; value.it_value.tv_sec=1; value.it_value.tv_usec=0; setitimer(ITIMER_PROF, &value, NULL); 例4. 设置一个定时1.5秒的真实时间定时器,它仅发送一次信号就自动取消。 答:将itimerval结构的成员it_value均赋值为1.5秒,成员it_interval赋值为0秒即可: struct itimerval value; value.it_value.tv_sec=1; value.it_value.tv_usec=500000; value.it_interval.tv_sec=0; value.it_interval.tv_usec=0; setitimer(ITIMER_REAL, &value, NULL);
一个简单的函数:
/* 安装定时器 / static int InitAlarm(int nTimerSec) { struct itimerval struValue; if(nTimerSec < 0) { print("安装定时器时间太小错误 %d/n", nTimerSec); return -1; } struValue.it_value.tv_sec = nTimerSec; struValue.it_value.tv_usec = 0; struValue.it_interval = struValue.it_value; if(setitimer(ITIMER_REAL, &struValue, NULL) == -1) { print( "setitimer 安装定时器错误 /n/t错误代码 = [%d] 错误信息 =[%s]/n",/ errno,strerror(errno)); return EXCEPTION; } return NORMAL; }
3 客户端程序四个步骤
3.1 创建套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
3.2 命令套接字
要用到的几个函数:
struct in_addr { unsigned long int s_addr; } The hostent structure is defined in <netdb.h> as follows: struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] /* for backward compatibility */
函数名 | 原型 | 作用 |
---|---|---|
inet_aton | int inet_aton(const char *cp, struct in_addr *inp); | inet_aton() converts the Internet host address cp from the standard numbers-and-dots notation into binary data and stores it in the structure that inp points |
gethostbyname | struct hostent *gethostbyname(const char *name); | |
htonl | uint16_t htons(uint16_t hostshort); | converts the unsigned short integer netshort from network byte order to host byte order |
3.3 连接
用connect函数 #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
3.4 发送数据
用write函数 #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
3.5 接收数据
用read函数 #include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
4 一个完整的客户端程序
这个演示程序中,增加了定时功能, 头文件中有些没有用到,但也包含进去了,主要是不想改了。 /* * 公共包含的头文件 */ #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <time.h> #include <ctype.h> #include <math.h> #include <fcntl.h> #include <dlfcn.h> #include <stdarg.h> #include <dirent.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #define DEF_COM_TIMEOUT 60 /* * RESULT变量宏定义 */ #define NORMAL 0 #define EXCEPTION -1 #define INVALID -2 /* * BOOL变量宏定义 */ #define BOOLTRUE 1 #define BOOLFALSE 0 /* * 基本类型定义 */ #ifndef MFC typedef void VOID; typedef const void CVOID; #endif typedef void * PVOID; typedef const void * PCVOID; typedef char CHAR; typedef CHAR * PCHAR; typedef const CHAR CCHAR; typedef const CHAR * PCCHAR; typedef int INT; typedef INT * PINT; typedef const INT CINT; typedef const INT * PCINT; typedef short SHORT; typedef SHORT * PSHORT; typedef const SHORT CSHORT; typedef const SHORT * PCSHORT; typedef long LONG; typedef LONG * PLONG; typedef const LONG CLONG; typedef const LONG * PCLONG; typedef float FLOAT; typedef FLOAT * PFLOAT; typedef const FLOAT CFLOAT; typedef const FLOAT * PCFLOAT; typedef double DOUBLE; typedef DOUBLE * PDOUBLE; typedef const DOUBLE CDOUBLE; typedef const DOUBLE * PCDOUBLE; typedef unsigned char UCHAR; typedef UCHAR * PUCHAR; typedef const UCHAR CUCHAR; typedef const UCHAR * PCUCHAR; typedef unsigned int UINT; typedef UINT * PUINT; typedef const UINT CUINT; typedef const UINT * PCUINT; typedef unsigned short USHORT; typedef USHORT * PUSHORT; typedef const USHORT CUSHORT; typedef const USHORT * PCUSHORT; typedef unsigned long ULONG; typedef ULONG * PULONG; typedef const ULONG CULONG; typedef const ULONG * PCULONG; typedef int BOOL; typedef BOOL * PBOOL; typedef const BOOL CBOOL; typedef const BOOL * PCBOOL; typedef char STR; typedef STR * PSTR; typedef const STR CSTR; typedef const STR * PCSTR; typedef unsigned char BYTE; typedef BYTE * PBYTE; typedef const BYTE CBYTE; typedef const BYTE * PCBYTE; typedef unsigned short WORD; typedef WORD * PWORD; typedef const WORD CWORD; typedef const WORD * PCWORD; typedef int RESULT; typedef RESULT * PRESULT; typedef const RESULT CRESULT; typedef const RESULT * PCRESULT; typedef void * HANDLE; typedef HANDLE * PHANDLE; typedef const HANDLE CHANDLE; typedef const HANDLE * PCHANDLE; static BOOL nAlarmFlag = BOOLFALSE; /* 警报标志 */ static struct sigaction struOldAct; /* 信号处理器 */ /* * 处理SIGALRM 信号 */ static VOID CatchAlarm(int sig) { if(sig == SIGALRM) nAlarmFlag = BOOLTRUE; } /* * 安装SIGALRM信号处理函数 */ static RESULT InitCatchAlarm() { struct sigaction struAct; struAct.sa_handler = CatchAlarm; struAct.sa_flags = 0; sigemptyset(&struAct.sa_mask); if(sigaction(SIGALRM, &struAct, &struOldAct) == -1) { printf( "sigaction 安装信号处理函数错误 /n/t错误代码 = [%d] / 错误信息 = [%s]/n", errno, strerror(errno)); return EXCEPTION; } return NORMAL; } /* * 恢复SIGALRM 处理函数 */ static RESULT EndCatchAlarm() { if(sigaction(SIGALRM, &struOldAct, NULL) == -1) { printf("sigaction 恢复信号处理函数错误 /n/t错误代码 = [%d] / 错误信息 = [%s]/n", errno, strerror(errno)); return EXCEPTION; } return NORMAL; } /* * 安装定时器 */ static RESULT InitAlarm(INT nTimerSec) { struct itimerval struValue; if(nTimerSec < 0) { printf( "安装定时器时间太小错误 %d/n", nTimerSec); return INVALID; } struValue.it_value.tv_sec = nTimerSec; struValue.it_value.tv_usec = 0; struValue.it_interval = struValue.it_value; if(setitimer(ITIMER_REAL, &struValue, NULL) == -1) { printf( "setitimer 安装定时器错误 /n/t错误代码 = [%d] 错误信息 =[%s]/n", errno,strerror(errno)); return EXCEPTION; } return NORMAL; } /** * @函数说明:创建到服务器的连接 * @pszHostString: 要连接的主机名称 * @nPort: 要连接的主机端口 * @nTimeout: 超时的秒数.小于0使用超时时间为60 * @返回说明: 成功返回 连接好的套接字 失败返回 -1 */ INT CreateConnectSocket(PCSTR pszHostString, UINT nPort, UINT nTimeout) { INT nConnectFd; struct sockaddr_in struInAddr; struct hostent * pstruHost; INT nErrorNo; nAlarmFlag = BOOLFALSE; if(InitCatchAlarm() == EXCEPTION) { printf( "InitCatchAlarm 初始化信号操作错误/n"); return -1; } nTimeout = nTimeout > 0 ? nTimeout : DEF_COM_TIMEOUT; if(InitAlarm(nTimeout) == EXCEPTION) { printf( "InitAlarm 初始化定时器错误/n"); EndCatchAlarm(); InitAlarm(0); return -1; } memset(&struInAddr,0,sizeof(struInAddr)); if((inet_aton(pszHostString, (struct in_addr *)&struInAddr.sin_addr)) == 0) { if((pstruHost = gethostbyname(pszHostString)) == NULL) { printf( "获取服务器IP地址错误!/n/t错误代码=[%d] 错误信息=[%s]/n", h_errno,hstrerror(h_errno)); EndCatchAlarm(); InitAlarm(0); return -1; } struInAddr.sin_addr = *(struct in_addr *)pstruHost->h_addr_list[0]; } struInAddr.sin_family = AF_INET; struInAddr.sin_port = htons(nPort); nConnectFd = socket(AF_INET, SOCK_STREAM, 0); if(nConnectFd == -1) { nErrorNo = errno; printf( "socket 错误!/n/t错误代码=[%d] 错误信息=[%s]/n", errno,strerror(errno)); EndCatchAlarm(); InitAlarm(0); errno = nErrorNo; return -1; } if(connect(nConnectFd, (struct sockaddr *)&struInAddr, / sizeof(struct sockaddr)) == -1) { nErrorNo = errno; printf( "connect 错误! 错误代码=[%d] 错误信息=[%s]/n", errno, strerror(errno)); close(nConnectFd); EndCatchAlarm(); InitAlarm(0); errno = nErrorNo; return EXCEPTION; } EndCatchAlarm(); InitAlarm(0); return(nConnectFd); } /** * @函数说明:无同步字符从Socket接收数据 * @nConnectFd: 连接的文件描述符 * @pszRecvBuffer: 接收缓冲区 * @nRecvBufferLen: 要接收的长度 * @nTimeout: 超时时间(秒),如果为0则采用缺省的超时时间60 * @返回说明: 成功返回NORMAL 失败返回EXCEPTION */ RESULT RecvSocketNoSync(INT nConnectFd, PSTR pszRecvBuffer, / UINT nRecvBufferLen, UINT nTimeout) { INT nReadLenth; PSTR pszRecvBufferTemp; INT nErrorNo; nAlarmFlag = BOOLFALSE; if(InitCatchAlarm() == EXCEPTION) { printf( "InitCatchAlarm 初始化信号操作错误/n"); return EXCEPTION; } nTimeout = nTimeout > 0 ? nTimeout : DEF_COM_TIMEOUT; if(InitAlarm(nTimeout) == EXCEPTION) { printf( "InitAlarm 初始化定时器错误/n"); EndCatchAlarm(); InitAlarm(0); return EXCEPTION; } pszRecvBufferTemp = pszRecvBuffer; while(nRecvBufferLen > 0) { nReadLenth = read(nConnectFd, pszRecvBufferTemp, nRecvBufferLen); if((nReadLenth < 0) && (errno == EINTR)) { if(nAlarmFlag == BOOLTRUE) { printf( "RecvSocketNoSync 读取数据超时错误/n"); EndCatchAlarm(); InitAlarm(0); errno = ETIMEDOUT; return EXCEPTION; } nReadLenth = 0; } else if((nReadLenth <0) && (errno != EINTR)) { nErrorNo = errno; printf( "RecvSocketNoSync 读取数据错误/n/t / 错误代码 = [%d] 错误信息 = [%s]/n", errno,strerror(errno)); EndCatchAlarm(); InitAlarm(0); errno = nErrorNo; return EXCEPTION; } else if(nReadLenth == 0) { printf( "RecvSocketNoSync 读取数据错误 连接被关闭/n"); EndCatchAlarm(); InitAlarm(0); errno = EIO; return EXCEPTION; } nRecvBufferLen -= nReadLenth; pszRecvBufferTemp += nReadLenth; } EndCatchAlarm(); InitAlarm(0); return NORMAL; } /** * @函数说明:无同步字符从Socket发送报文 * @nConnectFd: 连接的文件描述符 * @pszSendBuffer: 发送缓冲区 * @nSendBufferLenth: 要发送的报文长度 * @nTimeout: 超时时间(秒),如果小于0则采用系统缺省的超时时间60秒 * @返回说明: 成功返回NORMAL 失败返回EXCEPTION */ RESULT SendSocketNoSync(INT nConnectFd, PSTR pszSendBuffer, / UINT nSendBufferLenth, UINT nTimeout) { INT nWritenLenth; PSTR pszSendBufferTemp; INT nErrorNo; if(InitCatchAlarm() == EXCEPTION) { printf( "InitCatchAlarm 初始化信号处理函数错误/n"); return EXCEPTION; } nTimeout = nTimeout > 0 ? nTimeout : DEF_COM_TIMEOUT; if(InitAlarm(nTimeout) == EXCEPTION) { printf( "InitAlarm 初始化定时器错误/n"); EndCatchAlarm(); InitAlarm(0); return EXCEPTION; } pszSendBufferTemp = pszSendBuffer; while(nSendBufferLenth > 0) { nWritenLenth = write(nConnectFd, pszSendBufferTemp, nSendBufferLenth); if((nWritenLenth < 0) && (errno == EINTR)) { if(nAlarmFlag == BOOLTRUE) { printf( "SendSocketNoSync 发送数据失败 超时/n"); EndCatchAlarm(); InitAlarm(0); errno = ETIMEDOUT; return EXCEPTION; } nWritenLenth = 0; } else if((nWritenLenth < 0) && (errno != EINTR)) { nErrorNo = errno; printf( "SendSocketNoSync 发送数据到失败 /n/t 错误代码=[%d] / 错误信息=[%s]/n", errno, strerror(errno)); EndCatchAlarm(); InitAlarm(0); errno = nErrorNo; return EXCEPTION; } nSendBufferLenth -= nWritenLenth; pszSendBufferTemp += nSendBufferLenth; } EndCatchAlarm(); InitAlarm(0); return NORMAL; } int main() { int port,head,timeout,fd; char IP[20], filename[20]; char buffer[4096],szTemp[5]; int len,rfd; strcpy(IP,"127.0.0.1"); port=9734; timeout=60; strcpy(filename,"./aa.txt"); /*判断要传送的文件是否存在*/ if (access(filename, F_OK) == -1) { fprintf(stderr," 打开文件不存在。/n" ); return(-1); } /*打开要传的文件*/ memset(buffer,0,sizeof(buffer)); if((rfd = open(filename,O_RDONLY)) == -1) { fprintf(stderr,"打开文件 %s 错误 [%s]/n",filename,strerror(errno)); return(-1); } if((len = read(rfd,buffer,sizeof(buffer))) < 1) { fprintf(stderr,"读取文件错误[%s]/n",strerror(errno)); return(-1); } if(buffer[len - 1] == '/n') buffer[len - 1] =0; close(rfd); /*创建套接字和连接*/ if((fd = CreateConnectSocket(IP,port,timeout)) == -1) { fprintf(stderr, "建立SOCKET 连接出错!"); return(-1); } /*发送数据*/ sprintf(szTemp, "%04d", len); if(SendSocketNoSync(fd,szTemp,4,timeout) == -1) return(-1); if(SendSocketNoSync(fd,buffer,len,timeout) == -1) return(-1); /*接收数据*/ memset(szTemp, 0, sizeof(szTemp)); memset(buffer, 0, sizeof(buffer)); if(RecvSocketNoSync(fd,szTemp,4,timeout) == -1) return(-1); if(RecvSocketNoSync(fd,buffer,atoi(szTemp),timeout) == -1) return(-1); }