客户端服务端共享的:
在进行套接字创建的时候采用int sock = ::socket(PF_INET, SOCK_STREAM, 0);
设置套接口发送及接收缓冲大小:
socklen_t window_size = 128*1204
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &window_size, sizeof(window_size));
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &window_size, sizeof(window_size));
下面的可重用设置只有服务端才用的
int reuse = 1;
int retcode = ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));//设置套接口为可重用状态
接着来就是要connect了(客户端采用的)
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str()); //服务端只改为htonl(INADDR_ANY);即可
addr.sin_port = htons(port);
服务端必须要有的绑定(客户端的在connect操作会自动绑定的,所以可以没有)
int retcode = ::bind(sock, (struct sockaddr*) &addr, sizeof(addr));
服务端必须要有的监听操作(客户端是没有的)
retcode = ::listen(sock, MAX_WAITQUEUE);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = NULL;
assert(0 == epoll_ctl(kdpfd, EPOLL_CTL_ADD, sock, &ev));//kdpfd是由epoll_create产生的
retcode = TEMP_FAILURE_RETRY(::connect(sock, (struct sockaddr *)&addr, sizeof(addr));
现在如果conect success,那么就进入进行对消息的收发的了
对于服务器还要进行::accept操作
int accept(struct sockaddr_in *addr)
{
socklen_t len = sizeof(struct sockaddr_in);
bzero(addr, sizeof(struct sockaddr_in));
struct epoll_event ev;
int rc = epoll_wait(kdfd, &ev, 1, timeout);
if(1 == rc && (ev.events & EPOLLIN))
return TEMP_FAILURE_RETRY(::accept(sock, (struct sockaddr *)addr, &len));
return -1;
}
retcode = TEMP_FAILURE_RETRY(::recv(sock, _rcv_queue.wr_buf(), _rcv_queue.wr_size(), MSG_NOSIGNAL));
如果在多线程的环境下,就会有多个线程同时对套接口进行读数据,这个时候就需要一种策略来进行检测套接口是否有数据可以读取(function:waitForWrite)
strcut pollfd pfd;
pfd.fd = sock;
pfd.events = POLLIN | POLLOUT | POLLPRI;
pfd.revents = 0;
retcode = TEMP_FAILURE_RETRY(::poll(&pfd, 1, timeout));
if(retcode > 0 && 0 == (pfd.revents & POLLIN))//如果是检测写的(pfd.revnets & POLLOUT)
retcode = -1;
对于发送数据需要进行数据量的控制(也就是流量的控制)
sendRawDataMI(const void *pBuffer, const int size)
{
if(NULL == pBuffer || size <= 0 ) return false;
int offset = 0;
do
{
int retcode = sendRawData(&((char *)pBuffer)[offset], size-offset);
if(-1 == retcode) return false;
offset += retcode;
}while(offset < size)
return (offset == size);
}
int sendRawData(const void *pBuffer, const int size)
{
if(isset_flag(INCOMPLETE_WRITE))
{
clear_flag(INCOMPLETE_WRITE);
goto do_select;
}
int retcode = TEMP_FAILURE_RETRY(::send(sock, pBuffer, size, MSG_NOSIGNAL));
if(retcode = -1 && (errno == EAGAIN) || errno == EWOULDBLOCK)
{
do_select:
retcode = waitForWrite();
if(1 == retcode)
retcode = TEMP_FAILURE_RETRY(::send(sock, pBuffer, size, MSG_NOSIGNAL));
else
return retcode;
}
if(retcode > 0 && retcode < size)
set_flag(INCOMPLETE_WRITE);
return retcode;
}
备注1:要对所创建的套接字进行合法性的检测。
备注2:要对setstockopt函数返回值进行合法性的检测
对于此函数的一点说明:
原型:
int setsockopt ( int sockfd, int level, int optname, const void * optval, socklen_t *opteln )
客户端的必须要在::connect之前设置
服务端的必须要在::accept之前设置
sockfd(套接字): 指向一个打开的套接口描述字
level:(级别): 指定选项代码的类型。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname(选项名): 选项名称
optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型
optlen(选项长度) :optval 的大小
返回值:标志打开或关闭某个特征的二进制选项
SO_RCVBUF和SO_SNDBUF(选项)
每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由应用进程来读。 TCP:TCP通告另一端的窗口大小。 TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制,如果对方无视窗口大小而发出了超过宙口大小的数据,则接 收方TCP将丢弃它。 UDP:当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP是没有 流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。
备注3:
MSG_NOSIGNAL is a flag used by send()/recv() in some implementations of the Berkeley sockets
API.This flag requests that the implementation does not to send a SIGPIPE signal on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned as normal.
备注4:在进行数据的收发的时候采用了建立缓冲队列&缓冲栈.
备注5:在上面的说明中kdpfd的定义:int kdpfd = epoll_create(1);