8.1 服务器开发 API 函数封装,select 优化服务器和客户端
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h> #include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #include "commsocket.h" typedef struct _SckHandle
{
int sockArray[];
int arrayNum;
int sockfd;
int contime;
int sendtime;
int revtime; }SckHandle; /**
* readn - 读取固定字节数
* @fd: 文件描述符
* @buf: 接收缓冲区
* @count: 要读取的字节数
* 成功返回count,失败返回-1,读到EOF返回<count
*/
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} /**
* writen - 发送固定字节数
* @fd: 文件描述符
* @buf: 发送缓冲区
* @count: 要读取的字节数
* 成功返回count,失败返回-1
*/
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} /**
* recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据
* @sockfd: 套接字
* @buf: 接收缓冲区
* @len: 长度
* 成功返回>=0,失败返回-1
*/
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while ()
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == - && errno == EINTR)
continue;
return ret;
}
} //函数声明
//客户端环境初始化
int sckCliet_init(void **handle, int contime, int sendtime, int revtime, int nConNum)
{
int ret = ;
if (handle == NULL ||contime< || sendtime< || revtime<)
{
ret = Sck_ErrParam;
printf("func sckCliet_init() err: %d, check (handle == NULL ||contime<0 || sendtime<0 || revtime<0)\n", ret);
return ret;
} SckHandle *tmp = (SckHandle *)malloc(sizeof(SckHandle));
if (tmp == NULL)
{
ret = Sck_ErrMalloc;
printf("func sckCliet_init() err: malloc %d\n", ret);
return ret;
} tmp->contime = contime;
tmp->sendtime = sendtime;
tmp->revtime = revtime;
tmp->arrayNum = nConNum; /*
int sockfd;
int i = 0;
for (i=0; i<1; i++)
{
//链表的顺序
sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
ret = errno;
printf("func socket() err: %d\n", ret);
return ret;
}
tmp->sockfd = sockfd;
}
*/ *handle = tmp;
return ret;
} /**
* activate_noblock - 设置I/O为非阻塞模式
* @fd: 文件描符符
*/
int activate_nonblock(int fd)
{
int ret = ;
int flags = fcntl(fd, F_GETFL);
if (flags == -)
{
ret = flags;
printf("func activate_nonblock() err:%d", ret);
return ret;
} flags |= O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if (ret == -)
{
printf("func activate_nonblock() err:%d", ret);
return ret;
}
return ret;
} /**
* deactivate_nonblock - 设置I/O为阻塞模式
* @fd: 文件描符符
*/
int deactivate_nonblock(int fd)
{
int ret = ;
int flags = fcntl(fd, F_GETFL);
if (flags == -)
{
ret = flags;
printf("func deactivate_nonblock() err:%d", ret);
return ret;
} flags &= ~O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if (ret == -)
{
printf("func deactivate_nonblock() err:%d", ret);
return ret;
}
return ret;
} /**
* connect_timeout - connect
* @fd: 套接字
* @addr: 要连接的对方地址
* @wait_seconds: 等待超时秒数,如果为0表示正常模式
* 成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
*/
static int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in); if (wait_seconds > )
activate_nonblock(fd); ret = connect(fd, (struct sockaddr*)addr, addrlen);
if (ret < && errno == EINPROGRESS)
{
//printf("11111111111111111111\n");
fd_set connect_fdset;
struct timeval timeout;
FD_ZERO(&connect_fdset);
FD_SET(fd, &connect_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
// 一但连接建立,则套接字就可写 所以connect_fdset放在了写集合中
ret = select(fd + , NULL, &connect_fdset, NULL, &timeout);
} while (ret < && errno == EINTR);
if (ret == )
{
ret = -;
errno = ETIMEDOUT;
}
else if (ret < )
return -;
else if (ret == )
{
//printf("22222222222222222\n");
/* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
/* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
int err;
socklen_t socklen = sizeof(err);
int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
if (sockoptret == -)
{
return -;
}
if (err == )
{
//printf("3333333333333\n");
ret = ;
}
else
{
//printf("4444444444444444:%d\n", err);
errno = err;
ret = -;
}
}
}
if (wait_seconds > )
{
deactivate_nonblock(fd);
}
return ret;
} //
int sckCliet_getconn(void *handle, char *ip, int port, int *connfd)
{ int ret = ;
SckHandle *tmp = NULL;
if (handle == NULL || ip==NULL || connfd==NULL || port< || port>)
{
ret = Sck_ErrParam;
printf("func sckCliet_getconn() err: %d, check (handle == NULL || ip==NULL || connfd==NULL || port<0 || port>65537) \n", ret);
return ret;
} //
int sockfd;
sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < )
{
ret = errno;
printf("func socket() err: %d\n", ret);
return ret;
} struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = inet_addr(ip); tmp = (SckHandle* )handle; /*
ret = connect(sockfd, (struct sockaddr*) (&servaddr), sizeof(servaddr));
if (ret < 0)
{
ret = errno;
printf("func connect() err: %d\n", ret);
return ret;
}
*/ ret = connect_timeout(sockfd, (struct sockaddr_in*) (&servaddr), (unsigned int )tmp->contime);
if (ret < )
{
if (ret==- && errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
return ret;
}
else
{
printf("func connect_timeout() err: %d\n", ret);
}
} *connfd = sockfd; 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 write_fdset;
struct timeval timeout; FD_ZERO(&write_fdset);
FD_SET(fd, &write_fdset); timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , NULL, &write_fdset, NULL, &timeout);
} while (ret < && errno == EINTR); if (ret == )
{
ret = -;
errno = ETIMEDOUT;
}
else if (ret == )
ret = ;
} return ret;
} //客户端发送报文
int sckClient_send(void *handle, int connfd, unsigned char *data, int datalen)
{
int ret = ; SckHandle *tmp = NULL;
tmp = (SckHandle *)handle;
ret = write_timeout(connfd, tmp->sendtime);
if (ret == )
{
int writed = ;
unsigned char *netdata = ( unsigned char *)malloc(datalen + );
if ( netdata == NULL)
{
ret = Sck_ErrMalloc;
printf("func sckClient_send() mlloc Err:%d\n ", ret);
return ret;
}
int netlen = htonl(datalen);
memcpy(netdata, &netlen, );
memcpy(netdata+, data, datalen); writed = writen(connfd, netdata, datalen + );
if (writed < (datalen + ) )
{
if (netdata != NULL)
{
free(netdata);
netdata = NULL;
}
return writed;
} } if (ret < )
{
//失败返回-1,超时返回-1并且errno = ETIMEDOUT
if (ret == - && errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
printf("func sckClient_send() mlloc Err:%d\n ", ret);
return ret;
}
return ret;
} return ret;
} /**
* 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 read_fdset;
struct timeval timeout; FD_ZERO(&read_fdset);
FD_SET(fd, &read_fdset); timeout.tv_sec = wait_seconds;
timeout.tv_usec = ; //select返回值三态
//1 若timeout时间到(超时),没有检测到读事件 ret返回=0
//2 若ret返回<0 && errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理)
//2-1 若返回-1,select出错
//3 若ret返回值>0 表示有read事件发生,返回事件发生的个数 do
{
ret = select(fd + , &read_fdset, NULL, NULL, &timeout);
} while (ret < && errno == EINTR); if (ret == )
{
ret = -;
errno = ETIMEDOUT;
}
else if (ret == )
ret = ;
} return ret;
} //客户端端接受报文
int sckClient_rev(void *handle, int connfd, unsigned char *out, int *outlen)
{ int ret = ;
SckHandle *tmpHandle = (SckHandle *)handle; if (handle==NULL || out==NULL)
{
ret = Sck_ErrParam;
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
} ret = read_timeout(connfd, tmpHandle->revtime ); //bugs modify bombing
if (ret != )
{
if (ret==- || errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
}
else
{
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
}
} int netdatalen = ;
ret = readn(connfd, &netdatalen, ); //读包头 4个字节
if (ret == -)
{
printf("func readn() err:%d \n", ret);
return ret;
}
else if (ret < )
{
ret = Sck_ErrPeerClosed;
printf("func readn() err peer closed:%d \n", ret);
return ret;
} int n;
n = ntohl(netdatalen);
ret = readn(connfd, out, n); //根据长度读数据
if (ret == -)
{
printf("func readn() err:%d \n", ret);
return ret;
}
else if (ret < n)
{
ret = Sck_ErrPeerClosed;
printf("func readn() err peer closed:%d \n", ret);
return ret;
} *outlen = n; return ;
} // 客户端环境释放
int sckClient_destroy(void *handle)
{
if (handle != NULL)
{
free(handle);
}
return ;
} int sckCliet_closeconn(int connfd)
{
if (connfd >= )
{
close(connfd);
}
return ;
} /////////////////////////////////////////////////////////////////////////////////////
//函数声明
//服务器端初始化
int sckServer_init(int port, int *listenfd)
{
int ret = ;
int mylistenfd;
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); mylistenfd = socket(PF_INET, SOCK_STREAM, );
if (mylistenfd < )
{
ret = errno ;
printf("func socket() err:%d \n", ret);
return ret;
} int on = ;
ret = setsockopt(mylistenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
if (ret < )
{
ret = errno ;
printf("func setsockopt() err:%d \n", ret);
return ret;
} ret = bind(mylistenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret < )
{
ret = errno ;
printf("func bind() err:%d \n", ret);
return ret;
} ret = listen(mylistenfd, SOMAXCONN);
if (ret < )
{
ret = errno ;
printf("func listen() err:%d \n", ret);
return ret;
} *listenfd = mylistenfd; return ;
} /**
* accept_timeout - 带超时的accept
* @fd: 套接字
* @addr: 输出参数,返回对方地址
* @wait_seconds: 等待超时秒数,如果为0表示正常模式
* 成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
*/
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in); if (wait_seconds > )
{
fd_set accept_fdset;
struct timeval timeout;
FD_ZERO(&accept_fdset);
FD_SET(fd, &accept_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = ;
do
{
ret = select(fd + , &accept_fdset, NULL, NULL, &timeout);
} while (ret < && errno == EINTR);
if (ret == -)
return -;
else if (ret == )
{
errno = ETIMEDOUT;
return -;
}
} //一但检测出 有select事件发生,表示对等方完成了三次握手,客户端有新连接建立
//此时再调用accept将不会堵塞
if (addr != NULL)
ret = accept(fd, (struct sockaddr*)addr, &addrlen); //返回已连接套接字
else
ret = accept(fd, NULL, NULL);
if (ret == -)
{
ret = errno;
printf("func accept() err:%d \n", ret);
return ret;
} return ret;
} int sckServer_accept(int listenfd, int *connfd, int timeout)
{
int ret = ; ret = accept_timeout(listenfd, NULL, (unsigned int) timeout);
if (ret < )
{
if (ret == - && errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
printf("func accept_timeout() timeout err:%d \n", ret);
return ret;
}
else
{
ret = errno;
printf("func accept_timeout() err:%d \n", ret);
return ret;
}
} *connfd = ret;
return ;
}
//服务器端发送报文
int sckServer_send(int connfd, unsigned char *data, int datalen, int timeout)
{
int ret = ; ret = write_timeout(connfd, timeout);
if (ret == )
{
int writed = ;
unsigned char *netdata = ( unsigned char *)malloc(datalen + );
if ( netdata == NULL)
{
ret = Sck_ErrMalloc;
printf("func sckServer_send() mlloc Err:%d\n ", ret);
return ret;
}
int netlen = htonl(datalen);
memcpy(netdata, &netlen, );
memcpy(netdata+, data, datalen); writed = writen(connfd, netdata, datalen + );
if (writed < (datalen + ) )
{
if (netdata != NULL)
{
free(netdata);
netdata = NULL;
}
return writed;
} } if (ret < )
{
//失败返回-1,超时返回-1并且errno = ETIMEDOUT
if (ret == - && errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
printf("func sckServer_send() mlloc Err:%d\n ", ret);
return ret;
}
return ret;
} return ret;
}
//服务器端端接受报文
int sckServer_rev(int connfd, unsigned char *out, int *outlen, int timeout)
{ int ret = ; if (out==NULL || outlen==NULL)
{
ret = Sck_ErrParam;
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
} ret = read_timeout(connfd, timeout); //bugs modify bombing
if (ret != )
{
if (ret==- || errno == ETIMEDOUT)
{
ret = Sck_ErrTimeOut;
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
}
else
{
printf("func sckClient_rev() timeout , err:%d \n", Sck_ErrTimeOut);
return ret;
}
} int netdatalen = ;
ret = readn(connfd, &netdatalen, ); //读包头 4个字节
if (ret == -)
{
printf("func readn() err:%d \n", ret);
return ret;
}
else if (ret < )
{
ret = Sck_ErrPeerClosed;
printf("func readn() err peer closed:%d \n", ret);
return ret;
} int n;
n = ntohl(netdatalen);
ret = readn(connfd, out, n); //根据长度读数据
if (ret == -)
{
printf("func readn() err:%d \n", ret);
return ret;
}
else if (ret < n)
{
ret = Sck_ErrPeerClosed;
printf("func readn() err peer closed:%d \n", ret);
return ret;
} *outlen = n; return ;
} //服务器端环境释放
int sckServer_destroy(void *handle)
{
return ;
}
select优化服务器:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while ()
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;
while ()
{
ret = recv_peek(sockfd, bufp, nleft);
if (ret < )
return ret;
else if (ret == )
return ret; nread = ret;
int i;
for (i=; i<nread; i++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+);
if (ret != i+)
exit(EXIT_FAILURE); return ret;
}
} if (nread > nleft)
exit(EXIT_FAILURE); nleft -= nread;
ret = readn(sockfd, bufp, nread);
if (ret != nread)
exit(EXIT_FAILURE); bufp += nread;
} return -;
} void echo_srv(int conn)
{
char recvbuf[];
while ()
{
memset(recvbuf, , sizeof(recvbuf));
int ret = readline(conn, recvbuf, );
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{
printf("client close\n");
break;
} fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
} void handle_sigchld(int sig)
{
/* wait(NULL);*/
while (waitpid(-, NULL, WNOHANG) > )
;
} void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n", sig);
} int main(void)
{
/*signal(SIGPIPE, SIG_IGN);*/
signal(SIGPIPE, handle_sigpipe);
/* signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen;
int conn; /*
pid_t pid;
while (1)
{
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept"); printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
echo_srv(conn);
exit(EXIT_SUCCESS);
}
else
close(conn);
}
*/ int i;
int client[FD_SETSIZE]; //支持的最大客户端连接数 数组
int maxi = ; //最大的不空闲的位置 printf("FD_SETSIZE:%d \n", FD_SETSIZE);
for (i=; i<FD_SETSIZE; i++)
client[i] = -; int nready;
int maxfd = listenfd;
fd_set rset;
fd_set allset; FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
printf("FD_SETSIZE:%d \n", FD_SETSIZE);
while ()
{
rset = allset;
//listenfd 可读,表示有客户端连接进来了,可以建立一个新的连接
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("select");
}
if (nready == )
continue; if (FD_ISSET(listenfd, &rset)) //若侦听套接口产生可读事件 说明3次握手已完成,有客户端已经连接建立
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); //处理accept
if (conn == -)
ERR_EXIT("accept"); for (i=; i<FD_SETSIZE; i++)
{
if (client[i] < )
{
client[i] = conn;
if (i > maxi)
maxi = i;
break;
}
} if (i == FD_SETSIZE)
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, &allset); //把新的连接 放入集合中
if (conn > maxfd) //同时按照条件,更新maxfd //维护最大描述符
maxfd = conn; if (--nready <= )
continue; } for (i=; i<=maxi; i++) //检测已连接的调节字conn是否可读
{
conn = client[i];
if (conn == -)
continue; if (FD_ISSET(conn, &rset)) //当前连接是否可读
{
char recvbuf[] = {};
int ret = readline(conn, recvbuf, );
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{
printf("client close\n");
FD_CLR(conn, &allset); //若对方已退出 从集合中清除
client[i] = -; //保存连接的数组 也置成-1 //也可进一步控制 若i是把maxfd,需要把maxifd变成第二大maxifd
close(conn);
} fputs(recvbuf, stdout);
//sleep(4);
writen(conn, recvbuf, strlen(recvbuf)); if (--nready <= )
break;
}
}
}
return ;
}
select优化客户端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while ()
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;
while ()
{
ret = recv_peek(sockfd, bufp, nleft);
if (ret < )
return ret;
else if (ret == )
return ret; nread = ret;
int i;
for (i=; i<nread; i++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+);
if (ret != i+)
exit(EXIT_FAILURE); return ret;
}
} if (nread > nleft)
exit(EXIT_FAILURE); nleft -= nread;
ret = readn(sockfd, bufp, nread);
if (ret != nread)
exit(EXIT_FAILURE); bufp += nread;
} return -;
} void echo_cli(int sock)
{
/*
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
writen(sock, sendbuf, strlen(sendbuf)); int ret = readline(sock, recvbuf, sizeof(recvbuf));
if (ret == -1)
ERR_EXIT("readline");
else if (ret == 0)
{
printf("client close\n");
break;
} fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
} close(sock);
*/
fd_set rset;
FD_ZERO(&rset); int nready;
int maxfd;
int fd_stdin = fileno(stdin); if (fd_stdin > sock)
maxfd = fd_stdin;
else
maxfd = sock; char sendbuf[] = {};
char recvbuf[] = {}; while ()
{
FD_SET(fd_stdin, &rset);
FD_SET(sock, &rset);
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if (nready == -)
ERR_EXIT("select"); if (nready == )
continue; if (FD_ISSET(sock, &rset))
{
int ret = readline(sock, recvbuf, sizeof(recvbuf));
if (ret == -)
ERR_EXIT("readline");
else if (ret == )
{
printf("server close\n");
break;
} fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
}
if (FD_ISSET(fd_stdin, &rset))
{
if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
break;
writen(sock, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
} close(sock);
} void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n", sig);
} int main(void)
{
/*
signal(SIGPIPE, handle_sigpipe);
*/
signal(SIGPIPE, SIG_IGN);
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("connect"); struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < )
ERR_EXIT("getsockname"); printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); echo_cli(sock); return ;
}
8.1 服务器开发 API 函数封装,select 优化服务器和客户端的更多相关文章
- 学习游戏服务器开发必看,C++游戏服务器开发常用工具介绍
C++游戏服务器开发常用工具介绍 在软件开发过程中需要使用的工具类型实属众多,从需求建模到软件测试,从代码编译到工程管理,这些工具都对项目有着不可替代的作用.庄子有云,"吾生也有涯,而知也无 ...
- Web服务器-服务器开发-返回固定页面的HTTP服务器(3.3.1)
@ 目录 1.注意 2.代码 关于作者 1.注意 浏览器解析的时候偶\r\n才算一个换行符 发送的str要编码,这里使用的是utf8 其他的都和上一篇没有什么区别 这里主要返回的是固定的网址 2.代码 ...
- erlang 游戏服务器开发
http://blog.csdn.net/slmeng2002/article/details/5532771 最近关注erlang游戏服务器开发 erlang大牛写的游戏服务器值得参考 介绍本文以 ...
- LwIP应用开发笔记之六:LwIP无操作系统TCP客户端
上一篇我们基于LwIP协议栈的RAW API实现了一个TCP服务器的简单应用,接下来一节我们来实现一个TCP客户端的简单应用. 1.TCP简述 TCP(Transmission Control Pro ...
- SQLite3 C/C++ 开发接口简介(API函数)
from : http://www.sqlite.com.cn/MySqlite/5/251.Html 1.0 总览 SQLite3是SQLite一个全新的版本,它虽然是在SQLite 2.8.13的 ...
- 免费API 接口罗列,再也不愁没有服务器开发不了APP了(下)【申明:来源于网络】
免费API 接口罗列,再也不愁没有服务器开发不了APP了(下)[申明:来源于网络] 地址:http://mp.weixin.qq.com/s/QzZTIG-LHlGOrzfdvCVR1g
- 微信开发 api 需要 https 服务器
微信开发 api 需要 https 服务器 先建一个环境,本地的 https 服务器. 以下这篇不错,很完整. https://zhuanlan.zhihu.com/p/23640321
- linux服务器开发三(网络编程)
网络基础 协议的概念 什么是协议 从应用的角度出发,协议可理解为"规则",是数据传输和数据的解释的规则. 假设,A.B双方欲传输文件.规定: 第一次,传输文件名,接收方接收到文件名 ...
- 手摸手教你让Laravel开发Api更得心应手
https://www.guaosi.com/2019/02/26/laravel-api-initialization-preparation/ 1. 起因 随着前后端完全分离,PHP也基本告别了v ...
随机推荐
- Ubuntu 16.04 kinetic 编译指定包
编译指定包 catkin_make -DCATKIN_WHITELIST_PACKAGES=baoming 使用上述命令后catkin_make会一直编译上面那个包,想要编译全部包,使用 catkin ...
- vmstat命令中System下in cs 何时为高?
https://superuser.com/ https://serverfault.com/ 良好状态指标 CPU利用率:User Time <= 70%,System Time <= ...
- 《剑指offer》第三十六题(二叉搜索树与双向链表)
// 面试题36:二叉搜索树与双向链表 // 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求 // 不能创建任何新的结点,只能调整树中结点指针的指向. #include < ...
- Flutter学习笔记(二)
*.assets 当引用图片的时候,需要在pubspec.yaml的文件中的flutter下添加assets,类似于下面的样子: image.png 这里需要注意的是文件里的assets只要一个缩进即 ...
- SpringBoot中的数据库连接池
内置的连接池 目前Spring Boot中默认支持的连接池有dbcp,dbcp2, tomcat, hikari三种连接池. 数据库连接可以使用DataSource池进行自动配置. 由于Tomcat数 ...
- [Java学习] Java虚拟机(JVM)以及跨平台原理
相信大家已经了解到Java具有跨平台的特性,可以“一次编译,到处运行”,在Windows下编写的程序,无需任何修改就可以在Linux下运行,这是C和C++很难做到的. 那么,跨平台是怎样实现的呢?这就 ...
- Necklace CodeForces - 613C (构造)
链接 大意: 给定n种珠子, 第i种有$a_i$个, 求将珠子穿成项链, 使得能使切开后的项链回文的切口尽量多 若有一种以上珠子为奇数, 显然不能为回文, 否则最大值一定是$gcd(a_1,a_2,. ...
- 操作系统错误 5:"5(拒绝访问。)
------------------------------ 无法打开物理文件 "G:/QGJX.mdf".操作系统错误 5:"5(拒绝访问.)". (Micr ...
- EBS开发附件上传和下载功能(转)
原文地址: EBS开发附件上传和下载功能 上传 Oracle ERP二次开发中使用的方式有两种,一是通过标准功能,在系统管理员中定义即可,不用写代码,就可以使几乎任何Form具有附件功能,具体参考系统 ...
- Oracle 声明常量 (转)
原文地址 Oracle 声明常量 常量在声明时赋予初值,并且在运行时不允许重新赋值.使用CONSTANT关键字声明常量. 声明常量 DECLARE pi CONSTANT number :=3.14; ...