本篇文章简单介绍了TCP/IP 通信,服务端使用的是 Linux ,我使用的Linux为 Deepin 国产Linux系统。客户端使用的是 Windows 系统, 界面使用 Qt 完成的,基本的Socket编程使用的是操作系统提供的API。本系列文章所有例程均参考 尹圣雨 著《TCP/IP网络编程》 一书,客户端界面如下图所示:
我的Linux服务器的IP地址为 192.168.150.130 , Server的端口号为 9003
下面是TCP Server的完整代码:
#include <iostream>
#include <QDebug>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <QString>
int main(int argc, char *argv[])
{
char* portString = "9003";
int socketHnd = socket(PF_INET, SOCK_STREAM, 0);
if (socketHnd == -1)
qDebug() << "Create TCP Socket Error!";
//std::cout << "Create Socket Successed!";
qDebug() << "Create Socket Successed!";
// bind
sockaddr_in sockInfo;
memset(&sockInfo, 0, sizeof(sockInfo));
sockInfo.sin_family = AF_INET;
sockInfo.sin_addr.s_addr = htonl(INADDR_ANY);
sockInfo.sin_port = htons(atoi(portString));
if (bind(socketHnd, (sockaddr*)&sockInfo, sizeof(sockInfo)) == -1)
qDebug() << "bind Error!";
// listen
if (listen(socketHnd, 5) == -1)
qDebug() << "listion Error!";
while(1)
{
// accept
socklen_t clentAddrInfoSize = sizeof(sockInfo);
int clentSocketHnd = accept(socketHnd, (sockaddr*)&sockInfo, &clentAddrInfoSize);
if (clentSocketHnd == -1)
qDebug() << "accept error!";
char pData[50] = {0};
// Recv Data
while (recv(clentSocketHnd, pData, sizeof(pData), 0) > 0)
{
// Send Data
send(clentSocketHnd, pData, sizeof(pData), 0);
}
close(clentSocketHnd);
}
close(socketHnd);
return 0;
}
服务端的流程如下:
关于每个函数的具体使用,在后续的文章中介绍,下面是客户端部分的完整代码:
头文件
#pragma once
#include <QtWidgets/QWidget>
#include <WinSock2.h>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
#include <QSpinBox>
class TcpClient : public QWidget
{
Q_OBJECT
public:
TcpClient();
~TcpClient();
private slots:
void onClickedConnectButton(void);
void onClickedSendMessageButton(void);
private:
SOCKET m_Socket = INVALID_SOCKET;
private:
QTextEdit *m_TextEdit = nullptr;
QLineEdit *m_IPLineEdit = nullptr;
QSpinBox *m_PortEdit = nullptr;
QPushButton *m_ConnectButton = nullptr;
QLineEdit *m_SendMessageLineEdit = nullptr;
};
源文件
#include "TCPClient.h"
#include <QVBoxLayout>
#include <QDebug>
TcpClient::TcpClient()
{
QVBoxLayout *layout = new QVBoxLayout(this);
// IP Address Layout
QHBoxLayout *nTopLayout = new QHBoxLayout;
QLabel *ipTag = new QLabel("Server IP Address:");
ipTag->setFixedWidth(150);
ipTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
m_IPLineEdit = new QLineEdit;
nTopLayout->addWidget(ipTag);
nTopLayout->addWidget(m_IPLineEdit);
// Port
QHBoxLayout *nPortLayout = new QHBoxLayout;
QLabel *portTag = new QLabel("Server Port:");
portTag->setFixedWidth(150);
portTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
m_PortEdit = new QSpinBox;
m_PortEdit->setMinimum(1000);
m_PortEdit->setMaximum(10000);
m_PortEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
nPortLayout->addWidget(portTag);
nPortLayout->addWidget(m_PortEdit);
// Connect
m_ConnectButton = new QPushButton("Connect");
QObject::connect(m_ConnectButton, &QPushButton::clicked, this, &TcpClient::onClickedConnectButton);
// Send Message
QHBoxLayout *nSendMessageLayout = new QHBoxLayout;
QLabel *sendMessageTag = new QLabel("Content:");
sendMessageTag->setFixedWidth(150);
sendMessageTag->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
m_SendMessageLineEdit = new QLineEdit;
QPushButton *button = new QPushButton("Send");
nSendMessageLayout->addWidget(sendMessageTag);
nSendMessageLayout->addWidget(m_SendMessageLineEdit);
nSendMessageLayout->addWidget(button);
QObject::connect(button, &QPushButton::clicked, this, &TcpClient::onClickedSendMessageButton);
// TextEdit
m_TextEdit = new QTextEdit;
m_TextEdit->setReadOnly(true);
layout->addLayout(nTopLayout);
layout->addLayout(nPortLayout);
layout->addWidget(m_ConnectButton, 0, Qt::AlignRight | Qt::AlignVCenter);
layout->addLayout(nSendMessageLayout);
layout->addWidget(m_TextEdit);
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
m_TextEdit->append("Startup Error!");
}
TcpClient::~TcpClient()
{
if (INVALID_SOCKET != m_Socket)
closesocket(m_Socket);
WSACleanup();
}
void TcpClient::onClickedConnectButton(void)
{
// 断开连接
if (m_Socket != INVALID_SOCKET)
{
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
m_TextEdit->append("Disconnect Server Success!");
m_ConnectButton->setText("Connect");
return;
}
// 创建Socket
m_Socket = socket(PF_INET, SOCK_STREAM, 0);
if (m_Socket == INVALID_SOCKET)
{
m_TextEdit->append("Create Socket Fail!");
return;
}
// 连接Server
SOCKADDR_IN serverSockInfo;
memset(&serverSockInfo, 0, sizeof(serverSockInfo));
serverSockInfo.sin_family = AF_INET;
serverSockInfo.sin_addr.s_addr = inet_addr(m_IPLineEdit->text().toLocal8Bit().data());
serverSockInfo.sin_port = htons(m_PortEdit->value());
if (::connect(m_Socket, (SOCKADDR*)&serverSockInfo, sizeof(serverSockInfo)) == SOCKET_ERROR)
{
m_TextEdit->append("Connect Server Fail!");
// 关闭创建的Socket
closesocket(m_Socket);
m_Socket = INVALID_SOCKET;
return;
}
m_ConnectButton->setText("DisConnect");
m_TextEdit->append("Connect Server Success!");
}
void TcpClient::onClickedSendMessageButton(void)
{
if (m_Socket == INVALID_SOCKET)
return;
char* sendData = new char[30];
memset(sendData, 0, 30);
strcpy(sendData, m_SendMessageLineEdit->text().toLocal8Bit().data());
// 发送数据
int sendLen = send(m_Socket, sendData, 30, 0);
if (sendLen <= 0)
return;
QString sendMessa = "Send: %1";
sendMessa = sendMessa.arg(m_SendMessageLineEdit->text());
m_TextEdit->append(sendMessa);
// 接收数据
char *pRecvData = new char[50];
memset(pRecvData, 0, 50);
QString recvMessage = "Recv: ";
if (recv(m_Socket, pRecvData, 50, 0) > 0)
recvMessage += QString::fromLocal8Bit(pRecvData);
m_TextEdit->append(recvMessage);
delete[] pRecvData;
}
客户端部分的代码相对简单
首先需要在项目中添加库文件 ws2_32.lib
在使用Socket相关的函数的时候,需要添加头文件 WinSock2.h
windows中的函数跟Linux中的函数基本类似,Windows中的函数就是根据Unix的函数设计的。
函数 WSAStartup() 和 WSACleanup() 为注册Windows网络的函数
客户点中进行Socket通信如下步骤: