提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、Windows环境下实现TCP通信
- 1.服务器
- 2.客户端
- 3.运行
- 二、Linux环境下实现TCP通信
- 1.服务端
- 2.客户端
- 三、Qt实现TCP通信
- 1.服务端
- 1.客户端
- 总结
前言
大多数项目都在Linux系列的操作系统下开发服务器端,而多数客户端是在Windows平台下开发的。不仅如此,有时应用程序还需要在两个平台之间相互切换。因此,学习套接字编程的过程中,有必要兼顾Windows和Linux两大平台。另外,这两大平台下的套接字编程非常类似,如果把其中相似的部分放在一起讲解,将大大提高学习效率。只要理解好其中一个平台下的网络编程方法,就很容易通过分析差异掌握另一平台。因为不论什么方式实现,底层的逻辑都是完全一致的。
一、Windows环境下实现TCP通信
为了在Winsock基础上开发网络程序,需要做如下准备,以下两个文件都是系统自带的,包含时注意目标平台版本与库的版本一致。
导入头文件winsock2.h。
链接ws2_32.lib库(库名称和链接目录)。
1.服务器
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
void ErrorHandling(char *message)
{
fputs(message, stderr);
fputs("\n", stderr);
exit(1);
}
int main(int argc, char * argv[])
{
WSADATA wsaData;
SOCKET hServSock, hClntSock;
SOCKADDR_IN servAddr, clntAddr;
int szClntAddr;
char message[] = "Hello World!";
if (argc != 2)
{
printf("Usage : %s <port>", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("WSAStartup() error!");
hServSock = socket(PF_INET, SOCK_STREAM, 0);
if (hServSock == INVALID_SOCKET)
ErrorHandling("socket() error!");
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(argv[1]));
if (bind(hServSock, (SOCKADDR *)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
ErrorHandling("bind() error!");
if (listen(hServSock, 5) == SOCKET_ERROR)
ErrorHandling("listen() error!");
szClntAddr = sizeof(clntAddr);
hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr);
if (hClntSock == INVALID_SOCKET)
ErrorHandling("accept() error!");
fputs("sending message...", stderr);
fputs("", stderr);
send(hClntSock, message, sizeof(message), 0);
closesocket(hClntSock);
closesocket(hServSock);
WSACleanup();
return 0;
}
2.客户端
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
void ErrorHandling(char* message);
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET hSocket;
SOCKADDR_IN servAddr;
char message[30];
int strlen;
if (argc != 3)
{
printf("Usage: %s <IP> <port>", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("WSAStartup() error!");
hSocket = socket(PF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET)
ErrorHandling("socket() error!");
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(argv[1]);
servAddr.sin_port = htons(atoi(argv[2]));
if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
ErrorHandling("connect() error!");
strlen = recv(hSocket, message, sizeof(message) - 1, 0);
if (strlen == -1)
ErrorHandling("read() error!");
printf("Message from server:%s", message);
closesocket(hSocket);
WSACleanup();
return 0;
}
void ErrorHandling(char * message) {
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
3.运行
运行时直接进入到可执行程序文件,使用命令行运行
C:\tcpip>tcp_win_server 9190
C:\tcpip>tcp_win_client 127.0.0.1 9190
二、Linux环境下实现TCP通信
1.服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc,char *argv[])
{
int serv sock;
int clnt sock;
struct sockaddr in serv addr;
struct sockaddr in clnt addr;
socklen t clnt addr size;
char message[]="Hello World!";
if(argc!=2)
{
printf("Usage :%s<port>\n",argv[0]);
exit(1);
}
serv sock=Socket(PF INET,SOCK STREAM,0);
if(serv sock ==-1)
error_handling("socket() error");
memset(&serv_addr,0,sizeof(serv addr));
serv_addr.sin_family=AF INET;
serv_addr.sin_addr.s addr=htonl(INADDR ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv sock,5)==-1)
error_handling("listen() error");
clnt addr size=sizeof(clnt addr);
cInt sock=accept(sery sock,(struct sockaddr*)&cInt addr,&cInt addr size);
if(clnt sock==-1)
error_handling("accept() error");
write(clnt_sock,message,sizeof(message));
close(clnt sock);
close(serv sock);return 0;
}
void error handling(char *message)
{
fputs(message, stderr);
fputc('\n',stderr);
exit(1);
}
2.客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if(argc!-3)
{
printf("Usage :%s<IP><port>\n",argv[0]);
exit(1);
}
sock=socket(PF INET,SOCK STREAM, 0);
if(sock == -1)
error_handling("socket() error")
memset(&serv addr,0,sizeof(serv addr));
serv addr.sin family=AF INET;
serv addr.sin addr.s addr-inet addr(argv[1]);
serv_addr.sin port=htons(atoi(argv[2]));
if(connect(sock,(struct sockaddr*)&serv addr,sizeof(serv_addr))==-1)
error_handling("connect() error!");
str len=read(sock,message,sizeof(message)-1);
if(str len=--1)
error_handling("read()error!");
printf("Message from server :Xs n",message);
close(sock);
return 0;
}
void error handling(char*message)
{
fputs(message, stderr)fputc('\n',stderr);
exit(1);
}
三、Qt实现TCP通信
相对于前边两种方法,QT进行了一定封装,使用起来更加简洁。
1.服务端
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtNetwork>
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
QString MainWindow::getLocalIP()
{
//获取本机IPv4地址
//! 本地主机名, QHostInfo 的静态函数 localHostName()可获取本机的主机名
QString hostName = QHostInfo::localHostName();
//! 返回指定的主机名的IP地址
QHostInfo hostInfo = QHostInfo::fromName(hostName);
QString localIP = "";
//! 返回与 hostName()主机关联的IP地址列表
QList<QHostAddress> addList = hostInfo.addresses();//
for (int i = 0; i < addList.count(); i++)
{
QHostAddress aHost = addList.at(i);
if (QAbstractSocket::IPv4Protocol == aHost.protocol())
{
//192.168.23.114
localIP=aHost.toString();
break;
}
}
return localIP;
}
void MainWindow::closeEvent(QCloseEvent *event)
{//关闭窗口时停止监听
if (tcpServer->isListening())
tcpServer->close();;//停止网络监听
event->accept();
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
LabListen=new QLabel("监听状态:");
LabListen->setMinimumWidth(150);
ui->statusBar->addWidget(LabListen);
LabSocketState=new QLabel("Socket状态:");//
LabSocketState->setMinimumWidth(200);
ui->statusBar->addWidget(LabSocketState);
//本机IP
QString localIP = getLocalIP();
this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);
ui->comboIP->addItem(localIP);
//! tcp服务端
tcpServer = new QTcpServer(this);
//! 如果有新的连接
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}
MainWindow::~MainWindow()
{
delete ui;
}
//! 服务端有新的连接时
void MainWindow::onNewConnection()
{
// ui->plainTextEdit->appendPlainText("有新连接");
//! 创建socket,返回下一个等待接入的连接
tcpSocket = tcpServer->nextPendingConnection();
//! connectToHost()成功连接到服务器后发射此信号,连接成功后显示
connect(tcpSocket, SIGNAL(connected()), this, SLOT(onClientConnected()));
//! 客户端接入时
onClientConnected();
//! 当socket断开连接后发射此信号
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
//! 当socket的状态变化时发射此信号,参数socketState表示了socket当前的状态
connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)), this,
SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(tcpSocket->state());
//! 当缓冲区有新数据需要读取时发射此信号,在此信号的槽函数里读取缓冲区的数据
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}
//socket状态变化时
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
switch(socketState)
{
case QAbstractSocket::UnconnectedState:
LabSocketState->setText("scoket状态:UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
LabSocketState->setText("scoket状态:HostLookupState");
break;
case QAbstractSocket::ConnectingState:
LabSocketState->setText("scoket状态:ConnectingState");
break;
case QAbstractSocket::ConnectedState:
LabSocketState->setText("scoket状态:ConnectedState");
break;
case QAbstractSocket::BoundState:
LabSocketState->setText("scoket状态:BoundState");
break;
case QAbstractSocket::ClosingState:
LabSocketState->setText("scoket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
LabSocketState->setText("scoket状态:ListeningState");
}
}
//客户端接入时
void MainWindow::onClientConnected()
{
ui->plainTextEdit->appendPlainText("**client socket connected");
ui->plainTextEdit->appendPlainText("**peer name:" + tcpSocket->peerName());
//! 在已连接状态下,返回对方socket的地址
ui->plainTextEdit->appendPlainText("**peer address:" + tcpSocket->peerAddress().toString());
ui->plainTextEdit->appendPlainText("**peer port:" + QString::number(tcpSocket->peerPort()));
}
void MainWindow::onClientDisconnected()
{//客户端断开连接时
ui->plainTextEdit->appendPlainText("**client socket disconnected");
tcpSocket->deleteLater();
// deleteLater();//QObject::deleteLater();
}
void MainWindow::onSocketReadyRead()
{//读取缓冲区行文本
// QStringList lines;
while(tcpSocket->canReadLine())
ui->plainTextEdit->appendPlainText("[in] " + tcpSocket->readLine());
// lines.append(clientConnection->readLine());
}
//开始监听
void MainWindow::on_actStart_triggered()
{
//! IP地址
QString IP = ui->comboIP->currentText();
//! 端口
quint16 port = ui->spinPort->value();
QHostAddress addr(IP);
tcpServer->listen(addr, port);//
// tcpServer->listen(QHostAddress::LocalHost,port);// Equivalent to QHostAddress("127.0.0.1").
ui->plainTextEdit->appendPlainText("**开始监听...");
ui->plainTextEdit->appendPlainText("**服务器地址:" + tcpServer->serverAddress().toString());
ui->plainTextEdit->appendPlainText("**服务器端口:" + QString::number(tcpServer->serverPort()));
ui->actStart->setEnabled(false);
ui->actStop->setEnabled(true);
LabListen->setText("监听状态:正在监听");
}
void MainWindow::on_actStop_triggered()
{//停止监听
if (tcpServer->isListening()) //tcpServer正在监听
{
tcpServer->close();//停止监听
ui->actStart->setEnabled(true);
ui->actStop->setEnabled(false);
LabListen->setText("监听状态:已停止监听");
}
}
void MainWindow::on_actClear_triggered()
{
ui->plainTextEdit->clear();
}
void MainWindow::on_btnSend_clicked()
{//发送一行字符串,以换行符结束
QString msg=ui->editMsg->text();
ui->plainTextEdit->appendPlainText("[out] "+msg);
ui->editMsg->clear();
ui->editMsg->setFocus();
QByteArray str=msg.toUtf8();
str.append('\n');//添加一个换行符
tcpSocket->write(str);
}
void MainWindow::on_actHostInfo_triggered()
{//获取本机地址
QString hostName=QHostInfo::localHostName();//本地主机名
ui->plainTextEdit->appendPlainText("本机主机名:"+hostName+"\n");
QHostInfo hostInfo=QHostInfo::fromName(hostName);
QList<QHostAddress> addList=hostInfo.addresses();//
if (!addList.isEmpty())
for (int i=0;i<addList.count();i++)
{
QHostAddress aHost=addList.at(i);
if (QAbstractSocket::IPv4Protocol==aHost.protocol())
{
QString IP=aHost.toString();
ui->plainTextEdit->appendPlainText("本机IP地址:"+aHost.toString());
if (ui->comboIP->findText(IP)<0)
ui->comboIP->addItem(IP);
}
}
}
mainwindow.h
```cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QLabel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QLabel *LabListen;//状态栏标签
QLabel *LabSocketState;//状态栏标签
QTcpServer *tcpServer; //TCP服务器
QTcpSocket *tcpSocket;//TCP通讯的Socket
QString getLocalIP();//获取本机IP地址
protected:
void closeEvent(QCloseEvent *event);
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
//自定义槽函数
void onNewConnection();//QTcpServer的newConnection()信号
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onClientConnected(); //Client Socket connected
void onClientDisconnected();//Client Socket disconnected
void onSocketReadyRead();//读取socket传入的数据
//UI生成的
void on_actStart_triggered();
void on_actStop_triggered();
void on_actClear_triggered();
void on_btnSend_clicked();
void on_actHostInfo_triggered();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
1.客户端
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostAddress>
#include <QHostInfo>
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
QString MainWindow::getLocalIP()
{
QString hostName = QHostInfo::localHostName();//本地主机名
QHostInfo hostInfo = QHostInfo::fromName(hostName);
QString localIP = "";
QList<QHostAddress> addList = hostInfo.addresses();//
for (int i = 0; i < addList.count(); i++)
{
QHostAddress aHost = addList.at(i);
if (QAbstractSocket::IPv4Protocol == aHost.protocol())
{
localIP=aHost.toString();
break;
}
}
return localIP;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (tcpClient->state()==QAbstractSocket::ConnectedState)
tcpClient->disconnectFromHost();
event->accept();
}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpClient = new QTcpSocket(this); //创建socket变量
LabSocketState=new QLabel("Socket状态:");//状态栏标签
LabSocketState->setMinimumWidth(250);
ui->statusBar->addWidget(LabSocketState);
//本机IP
QString localIP = getLocalIP();
this->setWindowTitle(this->windowTitle() + "----本机IP:" + localIP);
ui->comboServer->addItem(localIP);
connect(tcpClient,SIGNAL(connected()),this,SLOT(onConnected()));
connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,
SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
connect(tcpClient,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onConnected()
{ //connected()信号槽函数
ui->plainTextEdit->appendPlainText("**已连接到服务器");
ui->plainTextEdit->appendPlainText("**peer address:" + tcpClient->peerAddress().toString());
ui->plainTextEdit->appendPlainText("**peer port:" + QString::number(tcpClient->peerPort()));
ui->actConnect->setEnabled(false);
ui->actDisconnect->setEnabled(true);
}
void MainWindow::onDisconnected()
{//disConnected()信号槽函数
ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");
ui->actConnect->setEnabled(true);
ui->actDisconnect->setEnabled(false);
}
void MainWindow::onSocketReadyRead()
{//readyRead()信号槽函数
while(tcpClient->canReadLine())
ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{//stateChange()信号槽函数
switch(socketState)
{
case QAbstractSocket::UnconnectedState:
LabSocketState->setText("scoket状态:UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
LabSocketState->setText("scoket状态:HostLookupState");
break;
case QAbstractSocket::ConnectingState:
LabSocketState->setText("scoket状态:ConnectingState");
break;
case QAbstractSocket::ConnectedState:
LabSocketState->setText("scoket状态:ConnectedState");
break;
case QAbstractSocket::BoundState:
LabSocketState->setText("scoket状态:BoundState");
break;
case QAbstractSocket::ClosingState:
LabSocketState->setText("scoket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
LabSocketState->setText("scoket状态:ListeningState");
}
}
void MainWindow::on_actConnect_triggered()
{//连接到服务器
QString addr=ui->comboServer->currentText();
quint16 port=ui->spinPort->value();
tcpClient->connectToHost(addr,port);
// tcpClient->connectToHost(QHostAddress::LocalHost,port);
}
void MainWindow::on_actDisconnect_triggered()
{//断开与服务器的连接
if (tcpClient->state()==QAbstractSocket::ConnectedState)
tcpClient->disconnectFromHost();
}
void MainWindow::on_actClear_triggered()
{
ui->plainTextEdit->clear();
}
void MainWindow::on_btnSend_clicked()
{//发送数据
QString msg=ui->editMsg->text();
ui->plainTextEdit->appendPlainText("[out] "+msg);
ui->editMsg->clear();
ui->editMsg->setFocus();
QByteArray str = msg.toUtf8();
str.append('\n');
tcpClient->write(str);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QTcpSocket *tcpClient; //socket
QLabel *LabSocketState; //状态栏显示标签
QString getLocalIP();//获取本机IP地址
protected:
void closeEvent(QCloseEvent *event);
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
//自定义槽函数
void onConnected();
void onDisconnected();
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();//读取socket传入的数据
//
void on_actConnect_triggered();
void on_actDisconnect_triggered();
void on_actClear_triggered();
void on_btnSend_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
总结
以上就是今天要讲的内容,本文整理了三种实现TCP通信的方法,从上层到偏底层,对于新手理解通信原理有极大帮助。