linux下基於socket的聊天軟體

來源:互聯網
上載者:User

標籤:oppo   start   ios   art   put   close   關閉   object   null   

近期學習linux socket編程。看看unp那本書。順便寫了個類似最簡單聊天功能的軟體。介面是用qt寫的。寫下來總結總結吧,假設有問題。歡迎大家和我交流。

模式是C/S模式,server端等待請求。client發送後建立請求。串連用的是tcp不是udp,事實上udp實現更為簡單。


一. 環境搭建

我用docker搭了幾個虛擬機器,詳細搭建方式能夠參考網上的或我之前寫docker的總結:點擊開啟連結

docker還是非常輕量級的,比一般虛擬機器起的快多了。

搭建好docker以後就是主要的先看網路是否聯通,假設不連通看看鏈路。防火牆設定一下。


二.沒有介面的代碼實現:

這部分代碼主要參考unp上的簡單範例,當中肯定會有bug,由於沒有考慮複雜的tcp環境,僅僅是單純的先實現再說:

server端:

#include "unp.h"#include <stdio.h>#include <unistd.h>void Answer(FILE *fop, FILE *fip, int sockfd) {    int         maxfdp1, stdineof;    fd_set      rset;    char        buf[MAXLINE];    int     n;    stdineof = 0;    FD_ZERO(&rset);    for ( ; ; ) {        if (stdineof == 0)            FD_SET(fileno(fip), &rset);        FD_SET(sockfd, &rset);        maxfdp1 = max(fileno(fip), sockfd) + 1;        Select(maxfdp1, &rset, NULL, NULL, NULL);        if (FD_ISSET(sockfd, &rset)) {  /* socket is readable */            if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {                if (stdineof == 1)                    return;     /* normal termination */                else                    err_quit("client quit!");            }            Write(fileno(fop), buf, n);        }        if (FD_ISSET(fileno(fip), &rset)) {  /* input is readable */            if ( (n = Read(fileno(fip), buf, MAXLINE)) == 0) {                stdineof = 1;                Shutdown(sockfd, SHUT_WR);  /* send FIN */                FD_CLR(fileno(fip), &rset);                continue;            }            Writen(sockfd, buf, n);        }    }    }int main(int argc, char **argv){    int                 listenfd, connfd;    pid_t               childpid;    socklen_t           clilen;    struct sockaddr_in  cliaddr, servaddr;    listenfd = Socket(AF_INET, SOCK_STREAM, 0);    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family      = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    //servaddr.sin_port        = htons(SERV_PORT);    servaddr.sin_port        = htons(50001);    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));    Listen(listenfd, LISTENQ);    for ( ; ; ) {        clilen = sizeof(cliaddr);        if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {            if (errno == EINTR)                continue;       /* back to for() */            else                err_sys("accept error");        }        if ( (childpid = Fork()) == 0) {    /* child process */            Close(listenfd);    /* close listening socket */            Answer(stdout, stdin, connfd);            printf("end of one connect!\n");            exit(0);        }        Close(connfd);          /* parent closes connected socket */    }}


client代碼:

#include"unp.h"const int SIZE = 1024;void Input(FILE *fp, int sockfd) {    intmaxfdp1, stdineof;fd_setrset;charbuf[MAXLINE];intn;stdineof = 0;FD_ZERO(&rset);for ( ; ; ) {if (stdineof == 0)FD_SET(fileno(fp), &rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);if (FD_ISSET(sockfd, &rset)) {/* socket is readable */if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {if (stdineof == 1)return;/* normal termination */elseerr_quit("str_cli: server terminated prematurely");}Write(fileno(stdout), buf, n);}if (FD_ISSET(fileno(fp), &rset)) {  /* input is readable */if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {stdineof = 1;Shutdown(sockfd, SHUT_WR);/* send FIN */FD_CLR(fileno(fp), &rset);continue;}Writen(sockfd, buf, n);}}}int main(int argc, char **argv){intsockfd;struct sockaddr_inservaddr;if (argc != 3)err_quit("usage: tcpcli <IPaddress>::<Port>");sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));struct sockaddr_in ss;    socklen_t len = sizeof(ss);    if(getsockname(sockfd, (SA *)&ss, &len) < 0) return 0;    int clientPort = ntohs(ss.sin_port);  //pay attention the translate n<->pstr_cli(stdin, sockfd);/* do it all */        printf("%d\n", clientPort);    Input(stdin, sockfd);exit(0);}



vim一粘貼,這代碼格式我也是無奈了……

要想跑通這個代碼還得配置unp的環境。

好吧。我以下有介面的代碼能夠不用配置unp環境


三.加介面

我抽象了幾個基類。主要介面就4個,先來看看介面吧:




server端和client的聊天介面是一樣的。server端我做了點小處理,拒絕多個客戶的串連。當一個客戶聊天時,監聽的socket就被關閉了。

剛學qt就開始搞介面,真是太噁心了。

主要代碼:

client登入:

#include "clientstart.h"#include "chatclient.h"#include <iostream>#include <fstream>#include <sstream>using namespace std;ClientStart::ClientStart(QWidget *parent) :    StartBase(parent){    this->className = "ClientStart";    /*belong to first layout*/    QLabel *labelOppositeIP = new QLabel("請輸入伺服器IP:");    oppositeIP = new QLineEdit;    QLabel *labelOppositePort = new QLabel("請輸入伺服器連接埠:");    oppositePort = new QLineEdit;    firstLayout->addWidget(labelOppositeIP, 2, 0, 1, 1);    firstLayout->addWidget(oppositeIP, 2, 1, 1, 4);    firstLayout->addWidget(labelOppositePort, 3, 0, 1, 1);    firstLayout->addWidget(oppositePort, 3, 1, 1, 4);    /*second layout*/    secondLayout = new QHBoxLayout;    secondLayout->setSpacing(60);    secondLayout->addWidget(buttonSure);    secondLayout->addWidget(buttonCancel);    topLayout->addLayout(firstLayout);    topLayout->addLayout(secondLayout);    QWidget* widget = new QWidget(this);    widget->setLayout(topLayout);      this->setCentralWidget(widget); //add the topLayout in current dialog}ClientStart::~ClientStart(){    //delete this;}void ClientStart::doOpen() {    //QMessageBox::information(this, "success", QObject::tr("xxx"), QMessageBox::Ok);    serverIP = (oppositeIP->text()).toStdString();    serverPort = (oppositePort->text()).toStdString();    /*start connect now*/    bool isConnect = tryConnect();    if(!isConnect) {        errorCall(this, "串連失敗");        return ;    }    ChatClient cc(clientIP, clientPort, serverIP, serverPort, servaddr, clientSocketFd);    cc.exec();    //cc.show();    this->close();}void ClientStart::doCancel() {    QMessageBox::information(this, "failed", QObject::tr("請又一次設定"), QMessageBox::Ok);    oppositeIP->setText("");    oppositePort->setText("");}bool ClientStart::tryConnect() {    clientSocketFd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(serverPort.c_str()));inet_pton(AF_INET, serverIP.c_str(), &servaddr.sin_addr);if(::connect(clientSocketFd, (SA *) &servaddr, sizeof(servaddr)) < 0) return 0;    /*get client port, ip has already got*/    struct sockaddr_in ss;    socklen_t len = sizeof(ss);    if(getsockname(clientSocketFd, (SA *)&ss, &len) < 0) return 0;    stringstream stmp; //change int to string    stmp << ntohs(ss.sin_port);  //pay attention the translate n<->p    clientPort = stmp.str();    return 1;}

client聊天:

#include "chatclient.h"#include <iostream>#include <fstream>using namespace std;ChatClient::ChatClient(std::string clientIP, std::string clientPort, std::string serverIP, std::string serverPort, struct sockaddr_in servaddr, int clientSocketFd)     : ChatBase(NULL) {    this->clientIP = clientIP;    this->clientPort = clientPort;    this->serverIP = serverIP;    this->serverPort = serverPort;    this->servaddr = servaddr;    this->clientSocketFd = clientSocketFd;    this->lineLocalIP->setText(QString(clientIP.c_str()));    this->lineLocalPort->setText(QString(clientPort.c_str()));    this->lineOppositeIP->setText(QString(serverIP.c_str()));    this->lineOppositePort->setText(QString(serverPort.c_str()));         this->secondLayout->setReadOnly(1);    this->secondLayout->setText("start chatting now!\n");    this->secondLayout->append(showTime());    this->secondLayout->show();    /*connect the socket and function*/    stdineof = 0;    FD_ZERO(&rset);    revSN = new QSocketNotifier(clientSocketFd, QSocketNotifier::Read);     QObject::connect(revSN, SIGNAL(activated(int)), this, SLOT(doDataReceived()));}ChatClient::~ChatClient() {    stdineof = 1;    shutdown(clientSocketFd, SHUT_WR);/* send FIN */    FD_ZERO(&rset);        this->close();}void ChatClient::doSendMsg() {    std::string sent = (forthLayout->toPlainText()).toStdString();    sent += ‘\n‘;    if(write(clientSocketFd, sent.c_str(), sent.size()) < 0) {        errorCall(this, "send message failed!");        return ;    }    appendSecondLayout(sent);    forthLayout->clear();}void ChatClient::doCancel() {    forthLayout->clear();}void ChatClient::doSendFile() {}void ChatClient::doVedio() {}void ChatClient::doDataReceived() {    int revLen;    if ( (revLen = read(clientSocketFd, buf, MAXLINE)) == 0) {if (stdineof == 1)return;/* normal termination */else {            errorCall(this, "server terminated prematurely");            this->close();        }    }       buf[revLen] = ‘\0‘;    appendSecondLayout(string(buf));    return ;}


server端代碼就不放了。基本都是類似的。

我把所有代碼上傳到csdn了,連結:點擊開啟連結
安裝時須要配置qt5,視頻和傳檔案功能暫未實現


四.總結和未來展望

這個軟體也就寫著玩。肯定有Bug。

事實上之前的計劃是作NAT轉寄,使得兩台區域網路之間的主機能夠直接聊天,後來發現行不通,沒有許可權改動入網的路由NAT規則。

以後的計劃例如以下:

1.是不用qt介面。直接命令列。

2.實現檔案傳輸。

3.拋棄C/S架構,搞一台有公網ip的server,兩台聊天的主機同一時候連到這個服務,server進行轉寄


假設大家有什麼問題歡迎交流,希望能有小夥伴和我一起來搞~~。

代碼雖不難,但也是自己倒騰的。轉載請註明地址:http://blog.csdn.net/u011353822/article/details/41246277

linux下基於socket的聊天軟體

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.