检查特定端口是否被防火墙允许通行
在开始建立连接之前需要查看服务端监听的端口是否被允许通行(以3666端口为例),如果输出结果为空,则表示该端口没有被允许通行。
firewall-cmd --list-ports | grep 3666
为特定端口添加防火墙规则
如果没有被允许通行,那么是无法成功建立连接的,需要设置端口通行。
在 CentOS 7 中,你可以使用 firewall-cmd
命令来配置 firewalld
服务,允许特定端口通过防火墙。以下是为特定端口添加防火墙规则的步骤:
1、添加临时规则(重启后失效)
假设开放3666端口,输入以下指令:
sudo firewall-cmd --zone=public --add-port=3666/tcp
2、添加永久规则(重启后仍然有效)
如果你想要永久开放端口,需要加上 --permanent
标志。例如,永久开放TCP端口3666,使用以下命令:
sudo firewall-cmd --zone=public --add-port=3666/tcp --permanent
完成添加规则后,为了让永久规则生效,需要重新加载防火墙的配置:
sudo firewall-cmd --reload
添加完成后可使用以下指令查看端口是否开放:
firewall-cmd --list-ports | grep 3666
出现以下这种情况代表成功开放
进行连接
完成以上操作后,就可以开始进行客户端与服务端的连接了,接下来我将用一个例子来演示:
服务端代码(在虚拟机Centos7上使用epoll实现):
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/epoll.h>
int main(int argc,char *argv[]){
int lfd,cfd;
lfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1){
perror("socket error");
exit(1);
}
int opt=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(3666);
addr.sin_addr.s_addr=INADDR_ANY;
int ret=bind(lfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret==-1){
perror("bind error");
exit(1);
}
ret=listen(lfd,128);
if(ret==-1){
perror("listen error");
exit(1);
}
struct epoll_event ep,eps[2000];
int epfd=epoll_create(1);
if(epfd==-1){
fprintf(stderr,"epoll_create error:%s\n",strerror(epfd));
exit(1);
}
ep.events=EPOLLIN;
ep.data.fd=lfd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ep);
if(ret==-1){
fprintf(stderr,"epoll_ctl error:%s\n",strerror(ret));
exit(1);
}
while(1){
int n=epoll_wait(epfd,eps,2000,-1);
if(n==-1){
fprintf(stderr,"epoll_wait error:%s\n",strerror(n));
exit(1);
}
for(int i=0;i<n;i++){
if(!(eps[i].events&EPOLLIN)){
continue;
}
if(eps[i].data.fd==lfd){
struct sockaddr_in caddr;
socklen_t len=sizeof(caddr);
cfd=accept(lfd,(struct sockaddr*)&caddr,&len);
if(cfd==-1){
perror("accept error");
exit(1);
}
char buf[16];
printf("客户端已连接 ip:%s port:%d\n",inet_ntop(AF_INET,&caddr.sin_addr,buf,16),ntohs(caddr.sin_port));
ep.events=EPOLLIN;
ep.data.fd=cfd;
int ret=epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ep);
if(ret==-1){
fprintf(stderr,"epoll_ctl error:%s\n",strerror(ret));
exit(1);
}
}
else{
char buf[1024];
int n=read(eps[i].data.fd,buf,1024);
if(n<0){
perror("read error");
exit(1);
}
else if(n==0){//客户端已关闭
int ret=epoll_ctl(epfd,EPOLL_CTL_DEL,eps[i].data.fd,NULL);
if(ret==-1){
fprintf(stderr,"epoll_ctl error:%s\n",strerror(ret));
exit(1);
}
close(eps[i].data.fd);
printf("客户端断开连接\n");
}
else{
printf("%s",buf);
for(int i=0;i<n;i++){
buf[i]=toupper(buf[i]);
}
write(STDOUT_FILENO,buf,n);
write(eps[i].data.fd,buf,n);
}
}
}
}
close(lfd);
close(epfd);
return 0;
}
客户端代码(在Windows上使用qt实现)
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QHostInfo>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
private:
QTcpSocket *tcpclient;
protected:
void closeEvent(QCloseEvent *event);
private slots:
void connectfunc();
void disconnectfunc();
void socketreaddata();
void on_pushButton_Connect_clicked();
void on_pushButton_Send_clicked();
void on_pushButton_Disconnect_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpclient=new QTcpSocket(this);
connect(tcpclient,SIGNAL(connected()),this,SLOT(connectfunc()));
connect(tcpclient,SIGNAL(disconnected()),this,SLOT(disconnectfunc()));
connect(tcpclient,SIGNAL(readyRead()),this,SLOT(socketreaddata()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_Connect_clicked()
{
QString addr=ui->comboBoxIp->currentText();
quint16 port=ui->spinBoxPort->value();
tcpclient->connectToHost(addr,port);
}
void MainWindow::on_pushButton_Send_clicked()
{
QString strmsg=ui->lineEdit_InputMsg->text();
ui->plainTextEdit_DispMsg->appendPlainText("[out]:"+strmsg);
ui->lineEdit_InputMsg->clear();
QByteArray str=strmsg.toUtf8();
str.append('\n');
tcpclient->write(str);
}
void MainWindow::on_pushButton_Disconnect_clicked()
{
if(tcpclient->state()==QAbstractSocket::ConnectedState)
tcpclient->disconnectFromHost();
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if(tcpclient->state()==QAbstractSocket::ConnectedState)
{
tcpclient->disconnectFromHost();
}
event->accept();
}
void MainWindow::connectfunc()
{
ui->plainTextEdit_DispMsg->appendPlainText("**********已经连接到服务器端**********");
ui->plainTextEdit_DispMsg->appendPlainText("**********peer address:"+
tcpclient->peerAddress().toString());
ui->plainTextEdit_DispMsg->appendPlainText("**********peer port:"+
QString::number(tcpclient->peerPort()));
ui->pushButton_Connect->setEnabled(false);
ui->pushButton_Disconnect->setEnabled(true);
}
void MainWindow::disconnectfunc()
{
ui->plainTextEdit_DispMsg->appendPlainText("**********已断开与服务器端的连接**********");
ui->pushButton_Connect->setEnabled(true);
ui->pushButton_Disconnect->setEnabled(false);
}
void MainWindow::socketreaddata()
{
while(tcpclient->canReadLine())
ui->plainTextEdit_DispMsg->appendPlainText("[in]:"+tcpclient->readLine());
}
客户端界面布局:
代码准备就绪后,运行Linux上的服务端,监听开放的端口,Windows客户端连接至该服务端,IP输入虚拟机Linux上的IP(可使用ifconfig查看)和开放的端口。
运行效果如下: