邮件Socket IO实现 - 参考libESMTP

关于libESMTP
            libESMTP, version 1.0
                  -- oOo --
            Brian Stafford  <brian@stafford.uklinux.net>

之前我自己也写过一个简单的Socket IO操作封装,主要目标是实现一个循环列表,把socket fd封装起来,通过这个循环列表建立它的输入缓冲。数据结构的定义为:
#define KAPOK_FILE_BUF_LEN  1024

typedef struct
{
    int kapok_file_magic;
    int el_flag;
    int fd;
    int read_head, read_tail;
    int fd_status;
    unsigned char read_buf[KAPOK_FILE_BUF_LEN];

} KapokFile;

enum
{
    KF_STATUS_OK,
    KF_STATUS_EOF,      //一般来说不应该有这个状态,因为socket没有eof的概念
    KF_STATUS_INVALID
};

而libESMTP提供的sio数据结构(及算法)就复杂很多了。查看siobuf.h可以看到它的所有外露接口:

#define SIO_BUFSIZE 2048 /* arbitrary, not too short, not too long */
#define SIO_READ    1
#define SIO_WRITE   2

typedef void (*recodecb_t) (char **dstbuf, int *dstlen,
                const char *srcbuf, int srclen, void *arg);
typedef void (*monitorcb_t) (const char *buffer, int length, int direction,
                 void *arg);

struct siobuf *sio_attach(int sdr, int sdw, int buffer_size);
void sio_detach(struct siobuf *sio);
void sio_set_monitorcb(struct siobuf *sio, monitorcb_t cb, void *arg);
void sio_set_timeout(struct siobuf *sio, int milliseconds);
void sio_set_securitycb(struct siobuf *sio, recodecb_t encode_cb,
                recodecb_t decode_cb, void *arg);
int sio_poll(struct siobuf *sio,int want_read, int want_write, int fast);
void sio_write(struct siobuf *sio, const void *bufp, int buflen);
void sio_flush(struct siobuf *sio);
void sio_mark(struct siobuf *sio);
int sio_fill(struct siobuf *sio);
int sio_read(struct siobuf *sio, void *bufp, int buflen);
char *sio_gets(struct siobuf *sio, char buf[], int buflen);
int sio_printf(struct siobuf *sio, const char *format, ...)
           __attribute__ ((format (printf, 2, 3))) ;
void *sio_set_userdata (struct siobuf *sio, void *user_data);
void *sio_get_userdata (struct siobuf *io);


#ifdef USE_TLS
int sio_set_tlsclient_ssl (struct siobuf *sio, SSL *ssl);
int sio_set_tlsserver_ssl (struct siobuf *sio, SSL *ssl);
#endif

1. attach/detach
用来关联和去关联socket套接字到SIO数据结构,这个过程中,套接字并没有被关闭。我设计的数据结构就只提供KapokFile的创建(关联)和关闭,在关闭时,套接字也会被关闭。从某个角度来说,我的设计参考了FILE数据结构的实现。

2. timeout设置
我的实现在timeout是在每次get/put中通过显式参数输入的。这里把timeout做到数据结构中。sio的timeout使用poll实现,而我原来的设计采用了select为实现,道理是一样的。

3. monitor call back
一个有利于调试的选项

4. security/recode call back
一个编码转译扩展接口,可以把邮件内容进行加密传送。

5. poll
对SIO数据结构的poll接口封装。查看源代码可以看到它支持SSL,而且利用SIO提供的读缓冲区,可以快速返回数据,提高处理效率。

6. write/flush/mark
写相关的函数,flush是真正的写,mark可以用来设置一个flush_mark的位置标置。如果flush_mark存在,则flush的目标不是整个有效缓冲区,而是flush以flush_mark指定的邮件。
write就是典型的套接字缓冲读写实现。配套实现有raw_write()函数

7. read/fill
fill和flush有点相似

8. gets/printf
高级点的读写操作

整体而言,SIO和我设计的KapokFile的区别有下面几点:

1.加入了monitor call back,支持联机monitor
2.加入recode call back,支持SSL等加密方式
3. 采用memmove则不是环循一维表的方式来处理内存
4. 提供flush_mark,而不是全面flush(觉得实用性不大)
5. 采用attach/detach来管理套接字关联,更加灵活。不过我的实现也可以接受。
6. 我没有把write_buf加入到数据结构中去。

还有一点就是sio采用双描述字,分别表示读和写。



因工作需要在Linux环境中用C++编写个发送邮件的程序,着实费了点周折,最终得以满意解决,现将历程与成果与大家分享! 一、刚开始网上一通逛搜,发现Linux环境下,发邮件使用较多的方法是libesmtp包,网上也有示例,按照相关章的指引,很容易就实现邮件的发送,但问题是不知道如何实现SSL。 二、发现libesmtp文件中有个smtp_starttls_set_ctx接口,似乎是可以解决ssl问题的,逛搜libesmtp解决SSL发送邮件的解决办法,几乎无任何信息,后来下载了个libesmtp的源代码包libesmtp-1.0.6.tar.bz2,内含examples示例目录,可以直接编译成功,但似乎是只支持tls邮件发送,而不支持ssl邮件的发送,百思不得其解。 三、接着寻找别的解决办法,在CSDN搜到一个csmtp说可以解决SSL邮件发送问题的资源,但下载需要50积分,说心里话能解决问题50积分也是值得的,但没有呀,提供资源者还比较仁义,告知资来源于https://www.codeproject.com,于是乎在codeproject找到了csmtp的资源,有两个版本,v2.4版本包CSmtp_v2_4_ssl.zip,v1.8版本分为window(CSmtp_v1_8a.zip)和linux(CSmtp_v1_8b.zip)两个包。 四、为了能省点精力,就直接用版较低的linux版吧,解压后发现有makefile文件,可直接编译通过,一般的邮件能发送成功,但可惜的是v1.8版本也不支持ssl协议。 五、其实从包的名字上就能看出来v2.4版本开始支持 ssl协议,但v2.4并不分windows版本和linux版本,是否能支持linux呢,查看源代码发现有对linux的支持,只是包内没有makefile文件,似乎没有在linux目录下编译过,于是编写了个makefile文件尝试编译,竞然编通过,而且发送文件成功,经过测试可以支持ssl邮件的发送,因暂无需求tls未做测试。 六、现将程序重新打包成csmtp_v2.4_linux.tar文件,与大家分享,文中所提到的相关资源包都一并打包到资源中了。 最后感谢原创christopher w. backen提供的代码资源!
PComm 库函数 PComm 库函数是台湾MOXA公司提供的,他为用户提供了基于win32 api的开发接口。 PComm库函数分为7大类 控制函数、数据输入函数、数据输出函数、状态函数、事件驱动函数、传输文件函数、特殊设定函数 上述函数均返回int型数据,若函数调用成功则返回0,否则返回一个负整数。 控制函数主要包括 sio_open(PortNum) 打开串口 sio_close(PortNum) 关闭串口 sio_ioctl(PortNum, baud, mode)设置串口的工作模式,包括 波特率,数据位元,停止位,效验等 sio_flush(portNum,func)清除发送、接收缓冲区 其中func 为0清空输入 ,为1清空输出,为2清空输入输出 sio_SetWriteTimeouts(int port,DWORD TotalTimeouts):设置串口写操作的超时。 sio_lctrl(Portnum,mode) 设置串口RTS/DTS。 待定 数据输入函数主要包括 sio_getch(PortNum) 从输入缓冲区读一个字符 sio_read(PortNum,buf,len) 从输入缓冲区读指定个数的字符 sio_iqueue(Portnum)读取输入缓冲区中的字符长度 数据输出函数主要包括 sio_putch(PortNum) 写一个字符到输出缓冲区 sio_write(PortNum,buf,len)写指定个数的字符到输出缓冲区 状态函数主要包括 sio_lstatus(portNum)获取串口的CTS,DSR,DCD,RI线的状态 sio_getbaud(portNum)获取串口的波特率 sio_getmode(portNum)获取串口的工作模式。对应sio_ioct函数 事件函数主要包括 sio_term_irq(portNum,func,code) 当接收到指定字符时响应事件,func为回调函数名,code为指定的字符 sio_cnt_irq(PortNum,func,count) 当接收到指定个数字元时响应事件,func为回调函数名,count接收的个数 传输文件函数主要包括 sio_FtASCIITx(portNum,fname,func,key)发送一个文本文件 sio_FtASCIIRx(portNum,fname,func,Key,sec)接收一个文本文件 int i,Ret,DataLen; FILE *fp1; uchar RetDataC[30],WriteData[24],PSC[5]; Ret = sio_open(port); if (Ret != SIO_OK ) return(-1);//打开串口错误 Ret = sio_ioctl (port, B9600, P_NONE | BIT_8 | STOP_1 ); if (Ret != SIO_OK) { sio_close(port); return(-2);//设置串口参数失败 } /*Ret = sio_lctrl (port, C_DTR | C_RTS ); if (Ret != SIO_OK) { sio_close(port); return(-2);//设置串口参数失败 }*/ /*Ret = sio_flush(port,2); if (Ret != SIO_OK){ sio_close(port); return(-3);//清空 输入和输出缓冲区的数据 }*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值