標籤: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的聊天軟體