linux下基于socket的聊天软件
来源:互联网 发布:淘宝摄影师兼职招聘 编辑:程序博客网 时间:2024/04/29 06:03
最近学习linux socket编程,看看unp那本书,顺便写了个类似最简单聊天功能的软件,界面是用qt写的,写下来总结总结吧,如果有问题,欢迎大家和我交流。
模式是C/S模式,服务器端等待请求,客户端发送后建立请求。连接用的是tcp不是udp,其实udp实现更为简单。
一. 环境搭建
我用docker搭了几个虚拟机,具体搭建方式可以参考网上的或我之前写docker的总结:点击打开链接
docker还是很轻量级的,比一般虚拟机起的快多了。
搭建好docker以后就是基本的先看网络是否联通,如果不连通看看链路,防火墙设置一下。
二.没有界面的代码实现:
这部分代码主要参考unp上的简单例子,其中肯定会有bug,因为没有考虑复杂的tcp环境,只是单纯的先实现再说:
服务器端:
#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 */ }}
客户端代码:
#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个,先来看看界面吧:
服务器端和客户端的聊天界面是一样的,服务器端我做了点小处理,拒绝多个客户的连接,当一个客户聊天时,监听的socket就被关闭了。
刚学qt就开始搞界面,真是太恶心了。
主要代码:
客户端登录:
#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;}
客户端聊天:
#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 ;}
我把全部代码上传到csdn了,链接:点击打开链接
安装时需要配置qt5,视频和传文件功能暂未实现
四.总结和未来展望
这个软件也就写着玩,肯定有Bug。其实之前的计划是作NAT转发,使得两台局域网之间的主机可以直接聊天,后来发现行不通,没有权限修改入网的路由NAT规则。
以后的计划如下:
1.是不用qt界面,直接命令行。
2.实现文件传输。
3.抛弃C/S架构,搞一台有公网ip的服务器,两台聊天的主机同时连到这个服务,服务器进行转发
如果大家有什么问题欢迎交流,希望能有小伙伴和我一起来搞~~。
代码虽不难,但也是自己倒腾的,转载请注明地址:http://blog.csdn.net/u011353822/article/details/41246277
- linux下基于socket的聊天软件
- 第一篇博客-自己的小聊天软件-linux下socket基于TCP协议聊天软件
- 基于Linux和Qt的聊天软件
- C# WPF 基于Socket的企业聊天软件IM(源码)
- 基于Java Socket编程的一对一聊天软件
- 基于socket实现的简单聊天android软件
- linux环境下基于udp socket简单聊天通信
- 基于linux下的局域网聊天
- Linux Linux函数 Linux聊天程序 基于socket的TCP(有连接的)聊天程序
- Linux Linux函数 Linux聊天程序 基于socket的TCP(有连接的)聊天程序
- Linux下基于TCP的Socket编程
- Linux下基于C的简单终端聊天程序
- 基于linux下的聊天程序设计与实现
- 一个在Linux下可以使用的聊天软件.
- 基于Socket简单的聊天程序
- Android 基于Socket的聊天应用(二)
- Java:基于socket的聊天实现+文件传输
- Java基于Tcp的socket聊天程序
- 电梯调度算法——编程之美
- UICollectionViewLayout的部分属性
- ant入门指南(6)
- [数据结构] 邻接矩阵实现Dijkstra算法
- 照片采集
- linux下基于socket的聊天软件
- Hibernate初体验
- IndentationError: unindent does not match any outer indentation level的问题
- Android PendingIntent.getBroadcast intent数据不更新问题
- 解决“SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT 'OpenRowset/OpenDatasource' 的访问
- 创建ftp服务器
- [通用源] 史上最全抓源工具大合集(新手、高手必看)
- Unresolved Import Issues with PyDev and Eclipse
- C++类中成员变量的初始化总结