socket收取上传的文件流,errno=14怎么处理?

小鸟向前飞 2017-12-27 10:03:53
用socket写了一个简单的接收tcp数据流的代码如下,系统是ubuntu 14:


#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

using namespace std;

#define MAXLINE 4096
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000


void setnonblocking(int sock)
{
int opts;
opts=fcntl(sock,F_GETFL);
if(opts<0)
{
perror("fcntl(sock,GETFL)");
return;
}
opts = opts|O_NONBLOCK;
if(fcntl(sock,F_SETFL,opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
return;
}
}


void CloseAndDisable(int sockid, epoll_event ee)
{
close(sockid);
ee.data.fd = -1;
}


int main()
{
cout << "Hello World!" << endl;

int i = 0, maxi = 0, listenfd = 0, connfd = 0, sockfd = 0, epfd = 0, nfds = 0 , portnumber = 0;
char line[MAXLINE] = {0};
socklen_t clilen;

portnumber = 60001;

// 声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
struct epoll_event ev;
struct epoll_event events[20];

// 生成用于处理accept的epoll专用的文件描述符
epfd = epoll_create(256);

struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);

memset(&serveraddr, 0, sizeof(serveraddr));

serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(portnumber);

// bind and listen
bind(listenfd, (sockaddr *)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ);

//设置与要处理的事件相关的文件描述符
ev.data.fd = listenfd;
//设置要处理的事件类型
ev.events = EPOLLIN | EPOLLET;
//ev.events=EPOLLIN;

//注册epoll事件
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

maxi = 0;

int bOut = 0;

for (; ;)
{
if (bOut == 1)
{
break;
}

//等待epoll事件的发生
nfds = epoll_wait(epfd, events, 20, -1);

//处理所发生的所有事件
cout << "\nepoll_wait returns\n";

for (i = 0; i < nfds; ++i)
{
// 如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
if(events[i].data.fd == listenfd)
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
if(connfd < 0)
{
int no = errno;
perror("connfd < 0");
return (1);
}

char *str = inet_ntoa(clientaddr.sin_addr);
cout << "accapt a connection from " << str << endl;

// 设置用于读操作的文件描述符
setnonblocking(connfd);
ev.data.fd = connfd;

// 设置用于注测的读操作事件
ev.events = EPOLLIN | EPOLLET;
//注册ev
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
}
else if (events[i].events & EPOLLIN)
{ // 如果是已经连接的用户,并且收到数据,那么进行读入。
cout << "EPOLLIN" << endl;
if ( (sockfd = events[i].data.fd) < 0)
{
continue;
}

char * head = line;
int recvNum = 0;
int count = 0;
bool bReadOk = false;

while (true)
{
// 确保sockfd是nonblocking的
recvNum = recv(sockfd, head + count, MAXLINE, 0);
//recvNum = read(sockfd, head + count, MAXLINE);
if(recvNum < 0)
{
if(errno == EAGAIN)
{
// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
// 在这里就当作是该次事件已处理处.
bReadOk = true;
break;
}
else if (errno == ECONNRESET)
{
// 对方发送了RST
CloseAndDisable(sockfd, events[i]);
cout << "counterpart send out RST\n";
break;
}
else if (errno == EINTR)
{
// 被信号中断
continue;
}
else
{
//其他不可弥补的错误
int no = errno;
CloseAndDisable(sockfd, events[i]);
cout << "unrecovable error: " << no << endl;
break;
}
}
else if( recvNum == 0)
{
// 这里表示对端的socket已正常关闭.发送过FIN了。
CloseAndDisable(sockfd, events[i]);
cout << "counterpart has shut off\n";
break;
}

// recvNum > 0
count += recvNum;
if ( recvNum == MAXLINE)
{
continue; // 需要再次读取
}
else // 0 < recvNum < MAXLINE
{
// 安全读完
bReadOk = true;
break; // 退出while(1),表示已经全部读完数据
}
}

if (bReadOk == true)
{
// 安全读完了数据
line[count] = '\0';

cout << "we have read from the client : " << line;

//设置用于写操作的文件描述符
ev.data.fd=sockfd;

//设置用于注测的写操作事件
ev.events = EPOLLOUT | EPOLLET;

//修改sockfd上要处理的事件为EPOLLOUT
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
}
else if(events[i].events & EPOLLOUT) // 如果有数据发送
{
const char str[] = "hello from epoll : this is a long string which may be cut by the net\n";
memcpy(line, str, sizeof(str));
cout << "Write " << line << endl;
sockfd = events[i].data.fd;

bool bWritten = false;
int writenLen = 0;
int count = 0;
char * head = line;

while (true)
{
// 确保sockfd是非阻塞的
writenLen = send(sockfd, head + count, MAXLINE, 0);
//writenLen = write(sockfd, str, strlen(str));

if (writenLen == -1)
{
if (errno == EAGAIN)
{
// 对于nonblocking的socket而言,这里说明了已经全部发送成功了
bWritten = true;
break;
}
else if(errno == ECONNRESET)
{
// 对端重置,对方发送了RST
CloseAndDisable(sockfd, events[i]);
cout << "counterpart send out RST\n";
break;
}
else if (errno == EINTR)
{
// 被信号中断
continue;
}
else
{
// 其他错误
}
}

if (writenLen == 0)
{
// 这里表示对端的socket已正常关闭.
CloseAndDisable(sockfd, events[i]);
cout << "counterpart has shut off\n";
break;
}

// 以下的情况是writenLen > 0
count += writenLen;
if (writenLen == MAXLINE)
{
// 可能还没有写完
continue;
}
else // 0 < writenLen < MAXLINE
{
// 已经写完了
bWritten = true;
break; // 退出while(1)
}
}

if (bWritten == true)
{
//设置用于读操作的文件描述符
ev.data.fd = sockfd;

//设置用于注测的读操作事件
ev.events=EPOLLIN | EPOLLET;

epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
}
}
}

return 0;



使用postman上传文件进行测试,发现有时候能全部接收到数据,有时候会发生错误,recv或send返回-1,erno为14,查了一下14是bad address错误,怎么理解这个错误?另外遇到这个错误怎么处理?为什么上传文件时会有这个错误返回?

还有,recv和read这两套函数,在使用socket时用哪一套比较好?
...全文
905 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2017-12-27
  • 打赏
  • 举报
回复
不知道有多少前人掉在TCP Socket send(人多)send(病少)send(财富) recv(人多病)recv(少财富) 陷阱里面啊! http://bbs.csdn.net/topics/380167545

33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧