QT 网络编程三(TCP版)
QT客户端
//widget.h #ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTcpSocket> #include <QPushButton> #include <QLineEdit> #include <QLabel> #include <QCloseEvent> #include <QTextBrowser> class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private: QTcpSocket *tcpsocket; QPushButton *btn1,*btn2; QLineEdit *edit1,*edit2,*edit3; QLabel *label1,*label2,*label3; QTextBrowser *textb; void closeEvent(QCloseEvent *event); private slots: void btn1_click(); void btn2_click(); void myrecvdata(); }; #endif // WIDGET_H
//widget.cpp #include "widget.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QMessageBox> #include <QHostAddress> Widget::Widget(QWidget *parent) : QWidget(parent) { tcpsocket=new QTcpSocket(this); btn1=new QPushButton(tr("连接")); btn2=new QPushButton(tr("发送")); connect(btn1,SIGNAL(clicked()),this,SLOT(btn1_click())); connect(btn2,SIGNAL(clicked()),this,SLOT(btn2_click())); edit1=new QLineEdit(); edit2=new QLineEdit(); edit3=new QLineEdit(); label1=new QLabel(tr("IP地址:")); label2=new QLabel(tr("端口号:")); label3=new QLabel(tr("消息内容:")); textb=new QTextBrowser(); QHBoxLayout *lay1=new QHBoxLayout(); lay1->addWidget(label1); lay1->addWidget(edit1); lay1->addWidget(label2); lay1->addWidget(edit2); lay1->addWidget(btn1); QHBoxLayout *lay2=new QHBoxLayout(); lay2->addWidget(label3); lay2->addWidget(edit3); lay2->addWidget(btn2); QHBoxLayout *lay4=new QHBoxLayout(); lay4->addWidget(textb); QVBoxLayout * lay3=new QVBoxLayout(this); lay3->addLayout(lay1); lay3->addLayout(lay2); lay3->addLayout(lay4); /*接收数据*/ connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(myrecvdata())); } //连接 void Widget::btn1_click() { /*禁用按钮,值是false*/ btn1->setEnabled(false); //获取IP地址 QString ipaddr=edit1->text(); edit1->setEnabled(false); //获取端口号 QString port=edit2->text(); edit2->setEnabled(false); QHostAddress * serverip=new QHostAddress(); serverip->setAddress(ipaddr); tcpsocket->connectToHost(*serverip,port.toInt()); //释放socket连接 delete serverip; } //发送 void Widget::btn2_click() { QString strtext=edit3->text(); if(!strtext.isEmpty()) tcpsocket->write(strtext.toStdString().data()); /*清空输入框*/ edit3->clear(); /*设置输入框重新获得焦点*/ edit3->setFocus(); } //接收消息 void Widget::myrecvdata() { char buf[1024]={0}; /*bytesAvailable()表示有效数据*/ while(tcpsocket->bytesAvailable()>0) { memset(buf,0,sizeof(buf)); tcpsocket->read(buf,sizeof(buf)); textb->append(buf); //QMessageBox::information(this,"消息",buf); } } //关闭 void Widget::closeEvent(QCloseEvent *event) { if(QMessageBox::question(this,"提示","你确定要退出程序吗?",QMessageBox::Yes|QMessageBox::No,QMessageBox::No)==QMessageBox::Yes) { event->accept(); }else { event->ignore(); } } Widget::~Widget() { }
Linux服务器端
//pub.h #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #define MAXPondNum 100 #ifdef __cplusplus extern "C" { #endif //create socket int create_socket(int port); //set noblock int setnonblock(int st); //select int open_select(int st); #ifdef __cplusplus extern "C" } #endif
//pub.c #include "pub.h" //create socket int create_socket(int port) { if (port <= 0) { printf("create_socket() param not correct!\n"); return -1; } int st = socket(AF_INET, SOCK_STREAM, 0); if (st < 0) { printf("socket() failed ! error message:%s\n", strerror(errno)); return -1; } //reuse address int on = 1; if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { printf("socket() failed ! error message:%s\n", strerror(errno)); close(st); return -1; } //bind struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) < 0) { printf("bind() failed ! error message:%s\n", strerror(errno)); close(st); return -1; } //listen if (listen(st, 100) < 0) { printf("listen() failed ! error message:%s\n", strerror(errno)); close(st); return -1; } return st; } //set noblock int setnonblock(int st) { if (st < 0) { printf("setnonblock() param not correct!\n"); return -1; } int opts = fcntl(st, F_GETFL); if (opts < 0) { printf("fcntl() failed ! error message:%s\n", strerror(errno)); return -1; } opts = opts | O_NONBLOCK; if (fcntl(st, F_SETFL, opts)) { printf("fcntl() failed ! error message:%s\n", strerror(errno)); return -1; } return opts; } //net address to int sockaddrtoa(const struct sockaddr_in *addr, char *ipaddr) { if (addr == NULL || ipaddr == NULL) { printf("sockaddrtoa() param not correct!\n"); return -1; } unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr); sprintf(ipaddr, "%u:%u:%u:%u", p[0], p[1], p[2], p[3]); return 0; } //accept clinet int accept_client(int st) { if (st < 0) { printf("accept_client() param not correct!\n"); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); socklen_t len = sizeof(addr); int client_st = accept(st, (struct sockaddr *) &addr, &len); if (client_st < 0) { printf("accept() failed ! error message:%s\n", strerror(errno)); return -1; } char ipstr[100] = { 0 }; sockaddrtoa(&addr, ipstr); printf("accept by %s\n", ipstr); return client_st; } //recv message int recv_message(int st) { if (st < 0) { printf("accept_client() param not correct!\n"); return -1; } char buf[1024]={0}; int rc=recv(st,buf,sizeof(buf),0); if(rc<0) { printf("recv() failed ! error message:%s\n",strerror(errno)); return -1; }else if(rc==0) { /*这里需要判断客户端是不是关闭连接了*/ printf("client is closed!\n"); return -1; } printf("%s\n",buf); //发送接收信息 memset(buf,0,sizeof(buf)); strcpy(buf,"server have recved !\n"); if(send(st,buf,sizeof(buf),0)<0) { printf("send() failed ! error message:%s\n",strerror(errno)); return -1; } return 0; } //select int open_select(int st) { if (st < 0) { printf("open_select() param not correct!\n"); return -1; } int i = 0; /*池子中只存放客户端socket*/ int socketpond[MAXPondNum] = { 0 }; /* * 注意int socketpond[MAXPondNum] = { -1 }; * 改写法并不能将所有元素初始化为-1,只有0这种唯一特例 * */ for(i=0;i<MAXPondNum;i++) { socketpond[i]=-1; } /*定义事件数组中socket最大的值*/ int maxfd = 0; /*准备监听事件数组结构*/ fd_set allsocket; while (1) { /*清空有消息的事件数组*/ FD_ZERO(&allsocket); /*将服务器socket直接放入事件数组中*/ FD_SET(st, &allsocket); /*假设服务器socket是事件数组中最大的*/ maxfd = st; /*遍历现有socket池,找出最大的socket,将所有的socket添加到事件数组中*/ for (i = 0; i < MAXPondNum; i++) { if (socketpond[i] != -1) { FD_SET(socketpond[i], &allsocket); if (socketpond[i] > maxfd) { maxfd = socketpond[i]; } } } int events = select(maxfd + 1, &allsocket, NULL, NULL, NULL); if (events < 0) { printf("select() failed ! error message:%s\n", strerror(errno)); break; } /* * 猜想:当两个客户端同时连接到服务器时,select是如何处理服务器socket事件的呢 * 我认为一个服务器端socket只能处理一个客户端连接请求 * 事件数组中只有一个服务器socket,这个服务器socket只会处理一个客户端连接请求, * 而此时另外一个客户端请求会在下次的select事件数组中被处理 * */ /*先处理服务器socket特殊处理*/ if (FD_ISSET(st, &allsocket)) { printf("accept client!\n"); //accept int client_st = accept_client(st); if (client_st < 0) { break; } //set nonblock if (setnonblock(client_st) < 0) { break; } for (i = 0; i < MAXPondNum; i++) { if (socketpond[i] == -1) { socketpond[i] = client_st; break; } } if (i == MAXPondNum) { printf("服务端连接池已满!\n"); close(client_st); } } for (i = 0; i < MAXPondNum; i++) { if (socketpond[i] < 0) { continue; } if (FD_ISSET(socketpond[i], &allsocket)) { //接收客户端发送消息 if(recv_message(socketpond[i])<0) { //接收客户端消息出错 //关闭客户端 close(socketpond[i]); //从服务器端socket池子中将该socket清除 socketpond[i]=-1; } /*需要处理的事件数减一*/ events--; } if(events<0) { break; } } } return 0; }
//mserver.c //服务器端 #include "pub.h" int main(int arg,char *args[]) { if(arg<2) { printf("please print one param!\n"); return -1; } int port=atoi(args[1]); int listen_st=create_socket(port); if(listen_st<0) return -1; if(setnonblock(listen_st)<0) goto END; open_select(listen_st); END:close(listen_st); return 0; }
.SUFFIXES:.c .o CC=gcc SRCS=mserver.c\ pub.c OBJS=$(SRCS:.c=.o) EXEC=mser start:$(OBJS) $(CC) -o $(EXEC) $(OBJS) @echo "-------OK--------" .c.o: $(CC) -g -Wall -o $@ -c $< clean: rm -f $(OBJS) rm -f $(EXEC)