本文最后更新于 231 天前,其中的信息可能已经有所发展或是发生改变。
今天花了很多的功夫在排错上面,主要是不明白问什么数据在哪里出错。因为这里已经将问题修改,贴一下修改过后的代码
#include <iostream>
#include "wrap.h"
int main() {
int i;
int sockfd;
int lfd = Socket(AF_INET, SOCK_STREAM, 0);
char clt_str[INET_ADDRSTRLEN];
int clt_port;
int cfd;
int maxfd = 0;
struct pollfd pfd[1024];
struct sockaddr_in serv_addr, clt_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&clt_addr, 0, sizeof(serv_addr));
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
char buf[BUFSIZ];
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
socklen_t clt_socklen;
Bind(lfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
Listen(lfd, BACK_LOG);
pfd[0].fd = lfd;
pfd[0].events = POLL_IN;
pfd[0].revents = 0;
int pfd_len = sizeof(pfd) / sizeof(pfd[0]);
for (i = 1; i < pfd_len; ++i) {
pfd[i].fd = -1;
}
maxfd = 0; // poll内部的有效元素的最大元素下标.
int nReady = 0;
while (true) {
nReady = poll(pfd, maxfd + 1, -1);
if (pfd[0].revents == POLL_IN) {
// 找到对应
clt_socklen = sizeof(clt_addr);
cfd = Accept(lfd, (struct sockaddr *) &clt_addr, &clt_socklen);
// 输出客户端信息
clt_port = ntohs(clt_addr.sin_port);
inet_ntop(AF_INET, &clt_addr.sin_addr, clt_str, INET_ADDRSTRLEN);
std::cout << "Client Port: " << clt_port << " Client IP: " << clt_str << std::endl;
for (i = 1; i < pfd_len; ++i) {
if (pfd[i].fd < 0) {
pfd[i].fd = cfd;
pfd[i].events = POLL_IN; //注意!!! 重点
//======================================================
//逻辑上应当在这里修改pfd,如果你写在外面,可能就会导致很大的问题.
break;
}
}
maxfd = std::max(maxfd, i);
if (--nReady <= 0) {
continue; // 代表仅有监听,那么继续监听,不处理客户端事件.
}
try {
if (i >= pfd_len) {
// 存满了
throw "Too many Clients";
}
} catch (const char *msg) {
std::cout << msg << std::endl;
exit(1);
}
}
//要注意 监听事件与客户端事件的分离,防止嵌套.
for (i = 1; i <= maxfd ; ++i) {
if((sockfd = pfd[i].fd) < 0) {
continue;
}
if (pfd[i].revents & POLL_IN) {
// 进行相关读写操作
ssize_t n = read(sockfd, buf, sizeof(buf));
if (n < 0) {
if (errno == ECONNRESET) {
// 连接被重置
std::cerr << "Client[" << i << "] close the connection" << std::endl;
close(sockfd);
} else {
std::cerr << "Read Error" << std::endl;
exit(1);
}
} else if (n == 0) {
std::cout << "Client close the connection" << std::endl;
close(sockfd);
pfd[i].fd = -1;
// i == maxfd 可能要进行相应处理.
if (i == maxfd) {
while (maxfd > 0 && pfd[maxfd].fd == -1) --maxfd;
}
break;
} else if (n > 0) {
for (int k = 0; k < n; ++k) {
buf[k] = toupper(buf[k]);
}
Write(STDOUT_FILENO, buf, n);
Write(sockfd, buf, n);
}
}
if (--nReady <= 0) {
break;
}
}
}
close(lfd);
return 0;
}
其中wrap.h封装了socket及read,write一系列函数,避免繁琐的错误处理
关键在于,写的时候的,
- 监听事件与客户端事件的分离
- pfd[i].events = POLL_IN;的处理位置
- 在这里我必须要承认,我对网上的教程正确度抱有期望确实太高了,有些时候我就看了半截就跑了,在后面代码反而会进行修改,其实应该是我的问题吧,应该看完。不过其实按自己的写法,写就好了,顶多后面参考一下代码逻辑。对。
- 感觉最恼火的就是,i的作用域范围,教程内部,直接定义i在初始位置,本身就不合理,因为要通过i进行控制多个循环,而我们只需要额外在定义一个变量例如k来控制循环就好了,让变量出去,也不好排错,这也是我们需要注意的。作用域导致的非编译问题,而是逻辑问题。
