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)

posted on 2016-10-31 12:42  寒魔影  阅读(7408)  评论(2编辑  收藏  举报

导航