io复用文章:https://blog.csdn.net/qq_25490573/article/details/102457943
部分函数:getpeername 根据句柄得到ip地址
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
getpeername(fd,(struct sockaddr *)&addr,&len);
struct sockaddr_in *in = (struct sockaddr_in *)&addr;
inet_ntop(AF_INET,&IN->sin_addr,ipbuf,MAXBUFLEN);
strncmp(buffer, "end", 3)
循环读取只到完毕(解决粘包问题)
char * packetptr;
int isend nbytesneed bytesum;
while(nbytesneed>0)
{
isend = read(epollfd,packetptr,nbytesneed);
if(isend == -1||isend ==0)
break;
nbytesneed -=isend;
packetptr += isend;
bytesum += isend;
}
epoll端代码:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "sys/epoll.h"
#include "fcntl.h"
#include "stdlib.h"
#include
#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT "6666"
#define CLIENT_NAMBER 128
struct epoll_event ep_ev;
int listen_sock,epoll_fd,control_sock=-1;
struct epoll_event ready_ev[128];
int maxnum = 128;
int timeout = 1000;//设置超时时间 若为-1则永久等待
int ret = 0;
int done = 0;
char buf[1024];
char * databuf;
static int recvstat = 0;
int clientnum[128] = {0};
int creat_socket(char *ip,char *port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(2);
}
int opt = 1;
//设置socket 先断开时 避免进入time_wait状态,属性SO_REUSEADDR,是使其地址能够重用
if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0)
{
perror("setsockopt");
exit(3);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(port));
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(4);
}
if(listen(sock,5)<0){
perror("listen");
exit(5);
}
printf("listen and bind succeed\n");
return sock;
}
int set_noblock(int sock)
{
int fl = fcntl(sock,F_GETFL);
return fcntl(sock,F_SETFL,fl|O_NONBLOCK);
}
void Server_Init()
{
databuf = (char*)malloc(1024*128);
memset(databuf,'\0',sizeof(databuf));
listen_sock = creat_socket(SERVER_ADDR,SERVER_PORT);
epoll_fd = epoll_create(255);
if(epoll_fd <0)
{
perror("epoll_create");
exit(6);
}
ep_ev.events = EPOLLIN;
ep_ev.data.fd = listen_sock;
if (epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ep_ev)<0)
{
perror("epoll_ctl");
exit(7);
}
clientnum[0] = listen_sock;
memset(buf,'\0',sizeof(buf));
}
void creattoclient(int fd){
for (int i = 0; i < CLIENT_NAMBER; ++i)
{
if(clientnum[i]!=0){
}else
{
clientnum[i] = fd;
return;
}
}
}
void deletetoclient(int fd){
for (int i = 0; i < CLIENT_NAMBER; ++i)
{
if(clientnum[i]!=0){
if(clientnum[i] == fd)
clientnum[i] = 0;
}else
{
return;
}
}
}
void Epoll_Listen()
{
while(!done)
{
if (recvstat==1)
{
}else if (recvstat == 0)
{
memset(databuf,'\0',sizeof(databuf));
}
switch(ret = epoll_wait(epoll_fd,ready_ev,maxnum,timeout))
{
case -1:
perror("epoll_wait");
exit(8);
break;
case 0:
recvstat = -1;
// perror("time out ...\n");
break;
default:
// printf("a new sigle!\n");
for (int i = 0; i < ret; ++i)
{
//判断是否是监听套接字,是的话accept
int fd = ready_ev[i].data.fd;
if ((fd==listen_sock)&&(ready_ev[i].events&EPOLLIN))
{
struct sockaddr_in remote;
socklen_t len = sizeof(remote);
int accept_sock = accept(listen_sock,(struct sockaddr*)&remote,&len);
if (accept<0)
{
perror("accept");
continue;
}
//printf("accept a clinent..[ip]:%s,[port]:%d\n",inet_ntoa(remote.sin_addr),ntohs(remote.sin_port));
ep_ev.events = EPOLLIN|EPOLLET;
ep_ev.data.fd = accept_sock;
set_noblock(accept_sock);
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_sock,&ep_ev)<0)
{
perror("epoll_ctl");
close(accept_sock);
}
creattoclient(accept_sock);
}else{
if(ready_ev[i].events&EPOLLIN){
//申请空间同时存文件描述符合缓冲区地址
memset(buf,'\0',sizeof(buf));
fflush(stdout);
ssize_t _s = recv(fd,buf,sizeof(buf)-1,0);
if (control_sock!=0)
{
if(buf=="control:connect")
control_sock = fd;
}else if(control_sock == fd){
if (!strncasecmp(buf,"quit",4))
{
exit(0);
}
}
if (_s<0)
{
perror("recv");
continue;
}else if(_s==0){
deletetoclient(fd);
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
}else{
strcat(databuf,buf);
//printf("client#%s\n",buf);
recvstat = 1;
// ep_ev.data.fd = fd;
// ep_ev.events = EPOLLOUT|EPOLLET;
// epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&ep_ev);
}
}else if(ready_ev[i].events&EPOLLOUT)
{
//const char * msg = ".................";
if (databuf[0]!='\0')
{
send(fd,databuf,strlen(databuf),0);
}
// epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
// close(fd);
}
}
}
break;
}
if(recvstat == 1){
ep_ev.events = EPOLLOUT|EPOLLET;
for (int i = 0; i < CLIENT_NAMBER; ++i)
{
if(clientnum[i]==0){
continue;
}
ep_ev.data.fd = clientnum[i];
epoll_ctl(epoll_fd,EPOLL_CTL_MOD,clientnum[i],&ep_ev);
}
}else if(recvstat == 0){
ep_ev.events = EPOLLIN|EPOLLET;
for (int i = 0; i < CLIENT_NAMBER; ++i)
{
if(clientnum[i]==0){
continue;
}
ep_ev.data.fd = clientnum[i];
epoll_ctl(epoll_fd,EPOLL_CTL_MOD,clientnum[i],&ep_ev);
}
}
}
}
int main(int argc,char *argv[])
{
Server_Init();
Epoll_Listen();
}
qt工程:
主线程.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include "conturlserver.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void addevent();
signals:
void sendinfo(QString);
public slots:
void showrecvtext(QString);
private:
Ui::MainWindow *ui;
conturlserver * sockthread;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
sockthread = new conturlserver;
addevent();
}
void MainWindow::addevent(){
connect(ui->start,&QPushButton::clicked,sockthread,&conturlserver::timejishi);
connect(ui->stop,&QPushButton::clicked,[=](){
if(sockthread->isRunning()){
emit sendinfo("control:gameover");
}
});
connect(this,&MainWindow::sendinfo,sockthread,&conturlserver::sendinfo);
connect(ui->send,&QPushButton::clicked,[=](){
QString str = ui->sendtext->text();
emit sendinfo(str);
ui->sendtext->setText("");
});
connect(sockthread,&conturlserver::recviinfor,this,&MainWindow::showrecvtext);
}
void MainWindow::showrecvtext(QString info){
ui->recvtext->append(info);
}
MainWindow::~MainWindow()
{
delete ui;
}
子线程conturlserver.h
#ifndef CONTURLSERVER_H
#define CONTURLSERVER_H
#include "QThread"
#include "QDebug"
#include "QTcpServer"
#include "QHostAddress"
#include "QTcpSocket"
#include "QTimer"
class conturlserver : public QThread
{
Q_OBJECT
public:
conturlserver();
void run();
signals:
void recviinfor(QString info);
public slots:
void sendinfo(QString info);
void timejishi();
private:
QHostAddress myaddr;
QTcpServer * mytcpfd;
QTcpSocket * mysockfd;
quint16 port = 6666;
QTimer *timeout;
QTimer *writetime;
QByteArray recvdata;
QByteArray senddata;
bool sockstat;
};
#endif // CONTURLSERVER_H
子线程conturlserver.cpp
#include "conturlserver.h"
conturlserver::conturlserver()
{
myaddr = "192.168.10.127";
mysockfd = new QTcpSocket(this);
writetime = new QTimer(this);
connect(writetime,&QTimer::timeout,[=](){
});
}
void conturlserver::run(){
sockstat = false;
if(mysockfd->ConnectedState == QAbstractSocket::UnconnectedState)
{
emit recviinfor("sockect is busying!!!\n");
return;
}
while(1){
if(mysockfd->isReadable()){
recvdata = mysockfd->read(1024);
if(!recvdata.isEmpty())
emit recviinfor(recvdata.data());
}
sleep(1);
}
}
void conturlserver::timejishi(){
mysockfd->connectToHost(myaddr,port);
if(mysockfd->waitForConnected())
{
mysockfd->write(QByteArray("control:connect"));
this->start();
emit recviinfor("sockect is connect success!!!\n");
}else{
emit recviinfor("sockect connecting is failder...\n");
this->exit();
}
}
void conturlserver::sendinfo(QString info){
senddata = info.toLatin1();
if(mysockfd->isWritable()){
qint64 i = mysockfd->write(senddata);
if(i>0){
if(senddata=="control:gameover")
{
emit recviinfor("epoll_server quit !!!\n");
this->exit();
}
}
}else{
emit recviinfor("send faile...\n");
}
}