重点:socket共用方法中错误码的定义以及错误码的解析
底层辅助代码
//serhelp.h

#ifndef _vxser
#define _vxser #ifdef __cplusplus
extern "C"
{
#endif /**
* sersocket_init - socket初始化
* @listenfd:文件描述符
* 成功返回0,失败返回错误码
* */
int sersocket_init(int *listenfd); /**
* listen_socket - 绑定端口号,监听套接字
* @listenfd:文件描述符
* @port:绑定的端口号
* 成功返回0,失败返回错误码
* */
int listen_socket(int listenfd, int port); /**
* run_server - 运行服务器
* @listenfd:文件描述符
* */
void run_server(int listenfd); #ifdef __cplusplus
}
#endif
#endif
//sockhelp.c
//socket发送接收底层辅助方法
/*底层辅助方法不打印错误信息,由上层调用通过errno打印信息,并且不做参数验证,有调用函数验证*/ #include "sockhelp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/select.h>
#include <fcntl.h> /**
* readn - 读取指定大小的字节
* @fd:文件描述符
* @buf:接收字节缓冲区
* @count:指定的字节数
* 成功返回指定字节数,失败返回-1,对方连接已经关闭,返回已经读取字节数<count
* */
int readn(int fd, void *buf, int count)
{
//定义剩余字节数
int lread = count;
//定义每次读取的字节数
int nread = ;
//定义辅助指针变量
char *pbuf = (char *) buf;
//如果剩余字节数大于0,循环读取
while (lread > )
{
nread = read(fd, pbuf, lread);
if (nread == -)
{
//read()是可中断睡眠函数,需要屏蔽信号
if (errno == EINTR)
continue;
//read()出错,直接退出
return -;
} else if (nread == )
{
//对方关联连接
return count - lread;
}
//重置剩余字节数
lread -= nread;
//辅助指针变量后移
pbuf += nread;
}
return count;
} /**
* writen - 写入指定大小的字节
* @fd:文件描述符
* @buf:发送字节缓冲区
* @count:指定的字节数
* 成功返回指定字节数,失败返回-1
* */
int writen(int fd, void *buf, int count)
{
//剩余字节数
int lwrite = count;
//每次发送字节数
int nwrite = ;
//定义辅助指针变量
char *pbuf = (char *) buf;
while (lwrite > )
{
nwrite = write(fd, pbuf, lwrite);
if (nwrite == -)
{
//注意:由于有TCP/IP发送缓存区,所以即使对方关闭连接,发送也不一定会失败
//所以需要捕捉SIGPIPE信号
return -;
}
lwrite -= nwrite;
pbuf += nwrite;
}
return count;
} /**
* read_timeout - 读超时检测函数,不含读操作
* @fd:文件描述符
* @wait_seconds:等待超时秒数,如果为0表示不检测超时
* 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
* */
int read_timeout(int fd, unsigned int wait_seconds)
{
int ret = ;
if (wait_seconds > )
{
//定义文件描述符集合
fd_set readfds;
//清空文件描述符
FD_ZERO(&readfds);
//将当前文件描述符添加集合中
FD_SET(fd, &readfds);
//定义时间变量
struct timeval timeout;
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , &readfds, NULL, NULL, &timeout);
} while (ret == - && errno == EINTR);
//ret==-1时,返回的ret正好就是-1
if (ret == )
{
errno = ETIMEDOUT;
ret = -;
} else if (ret == )
{
ret = ;
}
}
return ret;
} /**
* write_timeout - 写超时检测函数,不含写操作
* @fd:文件描述符
* @wait_seconds:等待超时秒数,如果为0表示不检测超时
* 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
* */
int write_timeout(int fd, unsigned int wait_seconds)
{
int ret = ;
if (wait_seconds > )
{
//定义文件描述符集合
fd_set writefds;
//清空集合
FD_ZERO(&writefds);
//添加文件描述符
FD_SET(fd, &writefds);
//定义时间变量
struct timeval timeout;
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , NULL, &writefds, NULL, &timeout);
} while (ret == - && errno == EINTR);
if (ret == )
{
errno = ETIMEDOUT;
ret = -;
} else if (ret == )
{
ret = ;
}
}
return ret;
} /**
* accept_timeout - 带超时accept (方法中已执行accept)
* @fd:文件描述符
* @addr:地址结构体指针
* @wait_seconds:等待超时秒数,如果为0表示不检测超时
* 成功返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT
* */
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret = ;
if (wait_seconds > )
{
/*
* 说明:accept和connect都会阻塞进程,accept的本质是从listen的队列中读一个连接,是一个读事件
* 三次握手机制是由TCP/IP协议实现的,并不是由connect函数实现的,connect函数只是发起一个连接,
* connect并非读写事件,所以只能设置connect非阻塞,而用select监测写事件(读事件必须由对方先发送报文,时间太长了)
* 所以accept可以由select管理
* 强调:服务端套接字是被动套接字,实际上只有读事件
* */
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
struct timeval timeout;
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , &readfds, NULL, NULL, &timeout);
} while (ret == - && errno == EINTR);
if (ret == -)
{
return -;
} else if (ret == )
{
ret = -;
errno = ETIMEDOUT;
return ret;
}
//成功无需处理,直接往下执行
}
//一旦检测出select有事件发生,表示有三次握手成功的客户端连接到来了
//此时调用accept不会被阻塞
if (addr != NULL)
{
socklen_t len = sizeof(struct sockaddr_in);
ret = accept(fd, (struct sockaddr *) addr, &len);
} else
{
ret = accept(fd, NULL, NULL);
}
return ret;
} /**
* activate_nonblock - 设置套接字非阻塞
* @fd:文件描述符
* 成功返回0,失败返回-1
* */
int activate_nonblock(int fd)
{
int ret = ;
int flags = fcntl(fd, F_GETFL);
if (flags == -)
return -;
flags = flags | O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if (ret == -)
return -;
return ret;
} /**
* deactivate_nonblock - 设置套接字阻塞
* @fd:文件描述符
* 成功返回0,失败返回-1
* */
int deactivate_nonblock(int fd)
{
int ret = ;
int flags = fcntl(fd, F_GETFL);
if (flags == -)
return -;
flags = flags & (~O_NONBLOCK);
ret = fcntl(fd, F_SETFL, flags);
if (ret == -)
return -;
return ret;
} /**
* connect_timeout - 带超时的connect(方法中已执行connect)
* @fd:文件描述符
* @addr:地址结构体指针
* @wait_seconds:等待超时秒数,如果为0表示不检测超时
* 成功返回0.失败返回-1,超时返回-1并且errno = ETIMEDOUT
* */
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret = ;
//connect()函数是连接服务器,本来connect会阻塞,但是设置未阻塞之后,
//客户端仍然会三次握手机制,如果三次握手失败,那么客户端一定无法向文件描述符中写入数据
//如果连接成功,那么客户端就可以向文件描述符写入数据了,
//所以交给select监管的文件描述符如果可以写,说明连接成功,不可以写说明连接失败 //设置当前文件描述符未阻塞--设置非阻塞之后,
//connect在网络中非常耗时,所以需要设置成非阻塞,如果有读事件,说明可能连接成功
//这样有利于做超时限制
if (wait_seconds > )
{
if (activate_nonblock(fd) == -)
return -;
}
ret = connect(fd, (struct sockaddr *) addr, sizeof(struct sockaddr));
if (ret == - && errno == EINPROGRESS)
{
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(fd, &writefds);
struct timeval timeout;
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , NULL, &writefds, NULL, &timeout);
} while (ret == - && errno == EINTR);
//ret==-1 不需要处理,正好给ret赋值
//select()报错,但是此时不能退出当前connect_timeout()函数
//因为还需要取消文件描述符的非阻塞
if (ret == )
{
errno = ETIMEDOUT;
ret = -;
} else if (ret == )
{
//ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,
//此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。
int err = ;
socklen_t len = sizeof(err);
ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
if (ret == && err != )
{
errno = err;
ret = -;
}
//说明套接字没有发生错误,成功
}
}
if (wait_seconds > )
{
if (deactivate_nonblock(fd) == -)
return -;
}
return ret;
}
socket共用代码
//commsocket.h
#include "sockhelp.h" #ifndef _vx2016
#define _vx2016 /*
* 思考:select超时应该用在客户端,客户对时间有要求,
* 客户端不一定支持select,并且客户端IO也不多,所以管理IO使用多进程
* 服务器不需要使用select超时,但是需要select管理客户端连接和监听套接字
* */ //定义错误码
#define OK 0
#define Sck_BaseErr 3000
#define Sck_MacErr (Sck_BaseErr+1)
#define Sck_TimeoutErr (Sck_BaseErr+2)
#define Sck_ParamErr (Sck_BaseErr+3)
#define Sck_PipeClosed (Sck_BaseErr+4) #define MAXBUFSIZE 1020 //留出4个字节存放包体大小 //定义粘包结构
typedef struct _packet
{
int len; //报文长度
char buf[MAXBUFSIZE]; //包体
} Packet; //定义socket结构
typedef struct _mysock
{
int fd;
} Mysock; #ifdef __cplusplus
extern "C"
{
#endif
/**
* strsockerr - 错误码转成字符串
* @err:错误码
* 返回错误信息
* */
char * strsockerr(int err); /**
* socket_send - 报文发送
* @fd:文件描述符
* @buf:写入缓冲区
* @buflen:写入数据长度
* 成功返回0,失败返回-1
* */
int socket_send(int fd, void *buf, int buflen); /**
* socket_recv - 报文接收
* @fd:文件描述符
* @buf:接收缓冲区
* @buflen:接收数据长度
* 成功返回0,失败返回-1
* */
int socket_recv(int fd, void *buf, int *buflen); /**
* InstallSignal - 安装信号
* @signarr:信号数组
* @len:信号数组的长度
* 成功返回0,失败返回错误码
* */
int Install_Signal(int *signarr, int len,void (*handler)(int)); #ifdef __cplusplus
extern "C"
}
#endif #endif
//commsocket.c -- socket上层方法实现
#include "commsocket.h" #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h> /**
* strsockerr - 错误码转成字符串
* @err:错误码
* 返回错误信息
* */
char * strsockerr(int err)
{
switch (err)
{
case OK:
return "success!";
case Sck_BaseErr:
return "方法内部错误!";
case Sck_MacErr:
return "malloc内存错误!";
case Sck_TimeoutErr:
return "select 超时错误!";
case Sck_ParamErr:
return "方法参数列表错误!";
case Sck_PipeClosed:
return "对等方已经关闭连接!";
default:
return "未识别错误码!";
}
} /**
* socket_send - 报文发送
* @fd:文件描述符
* @buf:写入缓冲区
* @buflen:写入数据长度
* 成功返回0,失败返回错误码
* */
int socket_send(int fd, void *buf, int buflen)
{
int ret = ;
Packet pack;
memset(&pack, , sizeof(pack));
//本地字节序转化成网络字节序
pack.len = htonl(buflen);
strncpy(pack.buf, buf, MAXBUFSIZE);
ret = writen(fd, &pack, buflen + );
if (ret == -)
{
ret=Sck_BaseErr;
perror("writen() err");
return ret;
} else if (ret == buflen)
{
ret = ;
}
return ret;
} /**
* socket_recv - 报文接收
* @fd:文件描述符
* @buf:接收缓冲区
* @buflen:接收数据长度
* 成功返回0,失败返回错误码
* */
int socket_recv(int fd, void *buf, int *buflen)
{
int ret = ;
//定义包体长度
int len = *buflen;
int hostlen = ;
Packet pack;
memset(&pack, , sizeof(pack));
//获取报文字节数
ret = readn(fd, &pack.len, );
if (ret == -)
{
perror("readn() err");
return -;
} else if (ret < )
{
printf("peer is closed !\n");
return -;
}
//网络字节转化成本地字节序
hostlen = ntohl(pack.len);
if (len < hostlen)
{
printf("socket_recv() 接收缓冲区太小!\n");
return -;
}
ret = readn(fd, pack.buf, hostlen);
if (ret == -)
{
perror("readn() err");
return -;
} else if (ret < hostlen)
{
printf("peer is closed !\n");
return -;
}
*buflen = hostlen;
strncpy(buf, pack.buf, hostlen);
return ret;
} /**
* InstallSignal - 安装信号
* @signarr:信号数组
* @len:信号数组的长度
* 成功返回0,失败返回错误码
* */
int Install_Signal(int *signarr, int len,void (*handler)(int))
{
int ret = ;
if (signarr == NULL)
{
ret = -;
printf("Install_Signal() param not correct !\n");
return ret;
}
int i = ;
for (i = ; i < len; i++)
{
//安装信号
if (signal(signarr[i], handler) == SIG_ERR)
{
ret = -;
printf("signal() failed !\n");
break;
}
}
return ret;
}

Linux socket多进程服务器框架一的更多相关文章

  1. Linux socket多进程服务器框架二

    客户端未解决Bug:子进程或者父进程退出的时候,我无法做到两个进程都调用clt_socket_Destory()方式释放socket句柄, 但是进程退出后,相应的资源也会释放,有一定影响,但是不大,以 ...

  2. Linux socket多进程服务器框架三

    在使用select管理服务器连接的时候: 注意1:select是可中断睡眠函数,需要屏蔽信号 注意2:必须获取select的返回值nread,每次处理完一个事件,nread需要-1 注意3:如果客户端 ...

  3. linux socket高性能服务器处理框架

    这个博客很多东西 http://blog.csdn.net/luozhonghua2014/article/details/37041765   思考一种高性能的服务器处理框架 1.首先需要一个内存池 ...

  4. workerman是一个高性能的PHP socket服务器框架

    workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架.被广泛的用于手机app.手游服务端.网络游戏服务器.聊天室服务器.硬件通讯服务器.智能家居. ...

  5. PHP socket 服务器框架集

    1.Swoole:重新定义PHP PHP语言的高性能网络通信框架,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,数据库连接池,AsyncTask,消息队列,毫秒定时 ...

  6. [C++]Linux之多进程运行代码框架

    声明:如需引用或者摘抄本博文源码或者其文章的,请在显著处注明,来源于本博文/作者,以示尊重劳动成果,助力开源精神.也欢迎大家一起探讨,交流,以共同进步- 0.0  多进程代码框架示例 /* @url: ...

  7. 【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)

    RT,使用消息队列,信号量和命名管道实现的多人群聊系统. 本学期Linux.unix网络编程的第三个作业. 先上实验要求: 实验三  多进程服务器 [实验目的] 1.熟练掌握进程的创建与终止方法: 2 ...

  8. 可扩展多线程异步Socket服务器框架EMTASS 2.0 续

    转载自Csdn:http://blog.csdn.net/hulihui/article/details/3158613 (原创文章,转载请注明来源:http://blog.csdn.net/huli ...

  9. 可扩展多线程异步Socket服务器框架EMTASS 2.0

    0 前言 >>[前言].[第1节].[第2节].[第3节].[第4节].[第5节].[第6节] 在程序设计与实际应用中,Socket数据包接收服务器够得上一个经典问题了:需要计算机与网络编 ...

随机推荐

  1. 通过dubbo暴露接口调用方法,及基于zookeeper的dubbo涉及配置文件

    现在很流行的Dubbo很多朋友都听说过吧,最近我也在看这方面的东西,分享先我的心得笔记. 先说说我们团队要做的项目框架,很简单重在实现基于zookeeper的dubbo注册. 框架:springmvc ...

  2. HtmlHelper用法大全

    HTML扩展类的所有方法都有2个参数: 以textbox为例子 public static string TextBox( this HtmlHelper htmlHelper, string nam ...

  3. 区别和详解:jQuery中的 $().each()和$.each()/jQuery.each()

    1.认识   $().each()遍历当前jQuery对象,并在每一个元素上执行回调函数.其方法内部是通过调用静态方法jQuery.each()来实现的.   jQuery.each()是一个通用的遍 ...

  4. 学C#你应该熟练使用ILDasm和Reflector【带视频教程】

    我们在学习C#的时候通常都会多多少少接触ILDasm和Reflector,这两样工具让我们对C#的理解不会只停留在编译器这个层面 上,而是让我们更深入的穿透编译器.这篇也是希望对IL和Reflecto ...

  5. lamp安装

    一.简介 什么是LAMPLAMP是一种Web网络应用和开发环境,是Linux, Apache, MySQL, Php/Perl的缩写,每一个字母代表了一个组件,每个组件就其本身而言都是在它所代表的方面 ...

  6. WIN7管理工具配置ODBC数据源-系统DSN中无Oracle,Sybase驱动的解决方法

    在C:\Windows\SysWOW64下找到: odbcad32.exe 这个文件,双击打开. 点击添加按钮,选择 对应的 驱动,然后就可用添加连接Oracle/Sybase的ODBC的数据源了.

  7. QT学习第1天

    QT学习第一天  坚持住!! 一 Qt概述 1.Qt发展历史 (1)1991年诞生(Haavard Nord/Eirik Chambe-Eng), (2)1994年创立Troll Tech(奇趣科技) ...

  8. android:context,getApplicationContext()生命周期

    getApplicationContext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁Activity.this的context 返回当前activity的上下文,属于activity ...

  9. Android 中PopupWindow使用 (转)

    参考学习后遇到问题: 要引用:有好几个,可以用错误提示解决: import android.widget.PopupWindow; import android.widget.Toast; Activ ...

  10. Can't load AMD 64-bit .dll on a IA 32-bit platform

    主要谈谈在win8.1(64bit)下搭建环境的经历. 安装win8.1(64bit)后,配置java环境是费了我一番心思的,所以想记录下来,成为经验.64位系统下比较理想的配置应该是 64位jdk ...