分类: 网络与安全
2011-04-18 20:28:41
基本函数定义
1.Static void proc_sessions(int tid,int p);//udp队列处理定时器函数
2.static void proc_session(soc_session_t *session);//处理会话函数
3.static int soc_mod_init_func(void)//udp模块入口函数()
4.static int soc_mod_fina_func(void)//udp模块出口函数()
5. int ms_bearer_open(int mode, void * cb)//开打设备函数
*cb为相关回调函数 定义 typedef void(*ms_bearer_cb_t)(int event)
6.void bearer_cb_action();//open函数的回调函数(在demo中实现.c中不出现)
7. int ms_bearer_close()//关闭设备函数
8. void * _vm_soc_session_malloc()//开辟会话session
9. void _ms_soc_session_free(int handle)//释放会话session
10. void * _ms_soc_session_get(int handle)//根据
11. int ms_soc_create_udp(int type, void * cb)//创建套接字 type为0是udp
底下调用的是
12. int ms_soc_close(int s)//关闭套接字
13. int ms_soc_sendto(int s,const void *buf,int len,ms_sockaddr *addr)//udp发送函数
session = _ms_soc_session_get(s)
底下调用nwrite = sendto(sessions[handle].socket, (char*)buf, len, 0,(struct sockaddr*)&remote_udp,sizeof(remote_udp));
14. int ms_soc_recvfrom(int s,void *buf,int len,ms_sockaddr *addr)//udp接收函数
底下调用的是
nread = recvfrom(sessions[handle].socket, (char*)buf, len, 0,(struct sockaddr*)&remote_udp,&serverlen);
15. int ms_inet_addr(char * ipchar, uint8 * ip)
将"xxx.xxx.xxx.xxx"格式的IP地址字符串转换为4字节整型
uint32 ipp = inet_addr(ipchar);
if (ip)
memcpy(ip, &ipp, 4);
16 void get_host_timer_cb(int tid, int p)
17. int ms_soc_gethostbyname(int s, const char * host, void * cb)
session = (soc_session_t*)_ms_soc_session_get(s);
大体思路如下:
Int ms_init(void)//demo中主函数入口
{
(一)Ms_soc_init()-à
1.(soc_mod_init_func()àret = WSAStartup(MAKEWORD(1, 1), &ws);// 初始化Windows Socket库。
2.ms_quick_timer(1000,0, proc_sessions,1);打开定时器每隔一秒调用proc_sessions函数,if (sessions[i].used)如果有会话再调用proc_session(sessions + i)函数)
(二)bearer_open_action()à
1. ret=int ms_bearer_open(int mode, void * cb) //正在打开网络设备接口
(void*)bearer_cb_action根据ret返回值执行,ret值多为-2所以调用
void bearer_cb_action(int event)
提示—》"按1键开始创建套接字连接");如果按1 进入按键事件函数
void key_event_up(int param0)
switch(param0) case MS_KEY_NUM1 :soc_create_action();开始创建套接字
}
一些基本内部变量声明
typedef struct _soc_session_t {
int used;
int handle;
SOCKET socket;
enum {UDP_CONNECTING, UDP_CONNECTED, UDP_WAIT_CLOSE} udp_state;
enum {BLOCK_WRITE, CAN_WRITE} write_pipe;
enum {BLOCK_READ, CAN_READ} read_pipe;
//void (*hook)(int handler, int state);
ms_sockaddr addr;
// typedef struct _ms_sockaddr
{uint16 port; /* 端口号 */
uint8 addr[14]; /* IP地址,通常为4字节;为兼容BSD标准,此处为14
} ms_sockaddr;
ms_get_host_cb_t get_host_cb;
//函数指针 typedef void (*ms_get_host_cb_t)(int s,int ip)
ms_soc_cb_t soc_cb;
//函数指针 typedef int(*ms_soc_cb_t)(int s,int e)
} soc_session_t;
//附:就是enum 把tcp_state改成udp_state
2. #define MS_SOCEVT_CONNECT (1)
/* 套接字可写 */
#define MS_SOCEVT_WRITE (2)
/* 套接字可读 */
#define MS_SOCEVT_READ (3)
/* 套接字被关闭 */
#define MS_SOCEVT_CLOSE (4)
/* 套接字连接失败 */
#define MS_SOCEVT_CONNECT_FAILED (5)
/////////////////////////////////////////////////////////////////////////
一些关键地方说明
(一) 创建注册说明
1.int ms_soc_create_udp(int type, void * cb)
soc_session_t* session;
*cb=soc_cb_action()
//进行相关的准备
session = _vm_soc_session_malloc()
unsigned long ul = 1;
unsigned long p = 0;
session->udp_state = UDP_CONNECTED;//
/////////////////////////////////////////////////////////////////////////////////////
重点说明:如果创建成功则一秒以后定时器刷函数 void proc_sessions(int tid, int p)根据soc_session_t* session结构体中
session->udp_state的值自动进入函数void proc_session(soc_session_t* session) 中case _CONNECTED:调用
session->soc_cb(session->handle, MS_SOCEVT_CONNECT_FAILED)
也就是void soc_cb_action(int s, int e)根据e的不同执行
case MS_SOCEVT_WRITE:
soc_send_action(s); //开始发送数据
case MS_SOCEVT_READ:
soc_recv_action(s); //开始接收数据
/////////////////////////////////////////////////////////////////////////////
session->used = TRUE;//
session->soc_cb = cb;//把地址接到session会话中
//定义函数指针 将来传递(void*)soc_cb_action执行sendto和recvfrom
if ((session->socket = socket(PF_INET, type, 0)) == INVALID_SOCKET)
_ms_soc_session_free(session->handle);
1. if(ioctlsocket(session->socket, FIONBIO, (unsigned long*)&ul) == SOCKET_ERROR)//控制套接口的模式
2. if (ioctlsocket(session->socket, FIONREAD, &p) != 0)
return session->handle
(二)客户端回调函数
//由int soc_create_actionàs = ms_soc_create(0, (void*)soc_cb_action)注册
因为定时器每过一秒就调用一次void proc_sessions(int tid,int p)àvoid proc_session(soc_session_t *session)通过传递过来的结构体判断会话中session->udp_state的值
case TCP_CONNECTING àcase TCP_CONNECTED àcase TCP_WAIT_CLOSE
Sessionàudp_state 进而调用函数void soc_cb_action(int s, int e)
Ret=select(1, NULL, &fdwrs, NULL, &timeout)à FD_ISSET(session->socket, &fdwrs)
根据select的返回值和isset判断sd句柄是否在相对应文件描述符中其中
(1).session->soc_cb(session->handle, MS_SOCEVT_CONNECT_FAILED);
(2). session->soc_cb(session->handle, MS_SOCEVT_CONNECT);
{
switch (e)
{
case MS_SOCEVT_CONNECT:
case MS_SOCEVT_WRITE:
soc_send_action(s); //开始发送数据
break;
case MS_SOCEVT_READ:
soc_recv_action(s); //开始接收数据
break;
case MS_SOCEVT_CLOSE:
case MS_SOCEVT_CONNECT_FAILED:
break; //错误处理或重连接
}
}
(三)select端回调函数
用法说明:
select()函数,这个函数允许我们执行I/O多路转接。其具体含义就是select()函数可以构造一个表,在这个表中包含了我们所有要用到的文件描述符。然后我们可以调用一个函数,这个函数可以检测这些文件描述符的状态,当某个(我们指定的)文件描述符准备好进行I/O操作时,此函数就返回,告知进程哪个文件描述符已经可以执行I/O操作了。这样就避免了长时间的阻塞。
因为这个函数下边几个函数中多次用到
void proc_session(soc_session_t* session)
int ms_soc_sendto(int s,const void *buf,int len,ms_sockaddr *addr)
int ms_soc_recvfrom(int s,void *buf,int len,ms_sockaddr *addr)
特别说明的是他的最后一个参数 timeout 参数timeout的设置有三种情况。
1.timeout==NULL时,这表示用户希望永远等待,直到我们指定的文件描述符中的一个已准备好,或者是捕捉到一个信号。如果是由于捕捉到信号而中断了这个无限期的等待过程的话,select()将返回"-1",同时设置errno的值为 EINTR。
2.timeout->tv_sec==0&&timeout->tv_usec==0,那么这表示完全不等待。 Select()测试了所有指定文件描述符后立即返回。这是得到多个描述符状态而不阻塞select()函数的轮询方法。
3..timeout->tv_sec!=0||timeout->tv_usec!=0,那么这两个参数的值即为我们希望函数等待的时间。其中tv_sec设置时间单位为秒,tv_usec设置时间单位为微秒。如果在超时的时候,在我们指定的所有文件描述符里面仍然没有任何一个准备好的话,则select()将返回"0"。
Int select(int n, fd_set *readfds, fd_set *writefds, fd_est *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
}
(四)一次读写数据整个过程中会话以及对应的函数调用变化情况sendto recvfrom(send /recv)
1.int ms_soc_recv(int s, void * buf, int len)
通过v=select(1, &fdwrs, NULL, NULL, &timeout)返回值v看下 读文件描述符中是否有 当v大于0的时候进行循环读取,如果等于-1进行出错误处理
如果没有v返回0 返回MS_SOC_WAIT;再次等待
一般情况下会不断刷函数void proc_session(soc_session_t* session)
case TCP_CONNECTED:因为v一直为0不满足条件if (v > 0 && FD_ISSET(session->socket, &fdwrs))所以总是break退出
直到再次有数据到来执行函数session->soc_cb(session->handle, MS_SOCEVT_READ);à