1. 说明:
此篇博客主要记录一种客户端实现方式,和两种使用poll或者epoll分别创建echo服务器的方式,具体可看代码注释:
2. 相关代码:
2.1 echoClient.cpp
cpp
#include <iostream>
#include <cstdio>
#include <cstring>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define ERR_EXIT(m) \
do {\
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main() {
int sock;
struct sockaddr_in svrAddr, localAddr;
socklen_t addrlen = sizeof(sockaddr);
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0)
ERR_EXIT("socket");
memset(&svrAddr, 0, sizeof(svrAddr));
svrAddr.sin_family = AF_INET;
svrAddr.sin_port = htons(8888);
svrAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sock, (sockaddr *)&svrAddr, sizeof(svrAddr)) < 0)
ERR_EXIT("connect");
if (getsockname(sock, (sockaddr *)&localAddr, &addrlen) < 0)
ERR_EXIT("getsockname");
std::cout << "ip = " << inet_ntoa(localAddr.sin_addr)
<< " port = " << ntohs(localAddr.sin_port) << std::endl;
char sendBuf[1024] = {0};
char recvBuf[1024] = {0};
while (fgets(sendBuf, sizeof(sendBuf), stdin) != NULL)
{
write(sock, sendBuf, strlen(sendBuf));
read(sock, recvBuf, sizeof(recvBuf));
fputs(recvBuf, stdout);
memset(sendBuf, 0, sizeof(sendBuf));
memset(recvBuf, 0, sizeof(recvBuf));
}
close(sock);
return 0;
}
2.2 echoServer_poll.cpp
cpp
#include <iostream>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>
#include <sys/socket.h>
#define ERR_EXIT(m) \
do {\
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
using PollfdList = std::vector<pollfd>;
int main()
{
//忽略系统提示的一些错误信号
/*
signal(para1,para2)
para1:信号类型
para2:信号处理函数(可以自定义)
讲解参考:https://blog.csdn.net/u013271656/article/details/114537411
*/
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
//创建一个监听套接字(非阻塞套接字)
int listenfd;
listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if(listenfd < 0){
ERR_EXIT("socket");
}
//设置地址
struct sockaddr_in srvAddr;
memset(&srvAddr,0,sizeof(srvAddr));//初始化
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(8888);
srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//设置地址的重复利用
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
ERR_EXIT("setsockopt");
}
//绑定
if(bind(listenfd, (sockaddr *)&srvAddr, sizeof(srvAddr)) < 0){
ERR_EXIT("bind");
}
//监听
if(listen(listenfd, SOMAXCONN) < 0){
ERR_EXIT("listen");
}
//使用poll并关注pollin事件
struct pollfd pfd;
pfd.fd = listenfd;
pfd.events = POLLIN;
//储存poll的描述符
PollfdList pollfds;
pollfds.push_back(pfd);
int nready;
struct sockaddr_in peerAddr;
socklen_t peerlen;
int connfd;
int idlefd;//空闲描述符
//循环处理
while (true) {
//取事件
nready = poll(pollfds.data(), pollfds.size(), -1);
if(nready == -1){
if(errno == EINTR){
continue;
}
ERR_EXIT("poll");
}
else if(nready == 0){
continue;
}
//如果有POLLIN事件
if(pollfds[0].revents & POLLIN){
//接受
peerlen = sizeof(peerAddr);
connfd = ::accept4(listenfd, (sockaddr *)&peerAddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
//剔除空闲连接
if (connfd == -1) {
if (errno == EMFILE) {
close(idlefd);
idlefd = accept(listenfd, NULL, NULL);
close(idlefd);
idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
continue;
}
else
ERR_EXIT("accept4");
}
//加入到监听
pfd.fd = connfd;
pfd.events = POLL_IN;
pfd.revents = 0;
pollfds.push_back(pfd);
--nready;
//连接成功
std::cout << "ip = " << inet_ntoa(peerAddr.sin_addr)
<< " port = " << ntohs(peerAddr.sin_port) << std::endl;
if (nready == 0)
continue;
}
//std::cout << "pollfds size: " << pollfds.size() << std::endl;
//std::cout << "nready fds: " << nready << std::endl;
//遍历判断哪些套接字产生了事件
for (auto it = pollfds.begin() + 1; it != pollfds.end() && nready > 0; ++it) {
//如果是可读事件
if (it->revents & POLL_IN) {
--nready;
connfd = it->fd;
char buf[1024] = {0};
//读取数据
int ret = read(connfd, buf, 1024);
if (ret == -1)
ERR_EXIT("read");
if (ret == 0) {
std::cout << "client closed" << std::endl;
it = pollfds.erase(it);
--it;
close(connfd);
continue;
}
std::cout << buf << std::endl;
//将接收的消息返回给客户端
write(connfd, buf, strlen(buf));
}
}
}
return 0;
}
2.3 echoServer_epoll.cpp
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#define ERR_EXIT(m) \
do {\
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
using EventList = std::vector<epoll_event>;
int main()
{
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
//创建一个监听套接字(非阻塞套接字)
int listenfd;
listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if(listenfd < 0){
ERR_EXIT("socket");
}
//设置地址
struct sockaddr_in srvAddr;
memset(&srvAddr,0,sizeof(srvAddr));//初始化
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(8888);
srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//设置地址的重复利用
int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
ERR_EXIT("setsockopt");
}
//绑定
if(bind(listenfd, (sockaddr *)&srvAddr, sizeof(srvAddr)) < 0){
ERR_EXIT("bind");
}
//监听
if(listen(listenfd, SOMAXCONN) < 0){
ERR_EXIT("listen");
}
std::vector<int> clients;
int epollfd = epoll_create(EPOLL_CLOEXEC);
//使用EPOLLIN并关注EPOLLIN事件
struct epoll_event event;
event.data.fd = listenfd;
event.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
//储存epoll的描述符
EventList events(16);
struct sockaddr_in peerAddr;
socklen_t peerlen;
int connfd;
int idlefd;//空闲描述符
int nready;
//循环处理
while (true) {
//取事件
nready = epoll_wait(epollfd, events.data(), static_cast<int>(events.size()), -1);
if(nready == -1){
if(errno == EINTR){
continue;
}
ERR_EXIT("epoll_wait");
}
else if(nready == 0){
continue;
}
if (static_cast<size_t>(nready) == events.size()) {
events.resize(events.size() * 2);
}
for (auto e : events) {
if (e.data.fd == listenfd) {
peerlen = sizeof(peerAddr);
connfd = ::accept4(listenfd, (sockaddr *)&peerAddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (connfd == -1) {
if (errno == EMFILE) {
close(idlefd);
idlefd = accept(listenfd, NULL, NULL);
close(idlefd);
idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
continue;
}
else
ERR_EXIT("accept4");
}
clients.push_back(connfd);
event.data.fd = connfd;
event.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
std::cout << " connection from ip = " << inet_ntoa(peerAddr.sin_addr)
<< " port = " << ntohs(peerAddr.sin_port) << std::endl;
}
else if (e.events & EPOLLIN) {
connfd = e.data.fd;
char buf[1024] = {0};
int ret = read(connfd, buf, 1024);
if (ret == -1)
ERR_EXIT("read");
if (ret == 0) {
std::cout << "client closed" << std::endl;
event = e;
epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
close(connfd);
continue;
}
std::cout << "msg: " << buf << std::endl;
write(connfd, buf, strlen(buf));
}
}
}
return 0;
}