网络聊天程序的设计与实现
二、实验目的
了解Socket通信的原理,在此基础上编写一个聊天程序。
总体设计
socket通信原理是一种“打开——读/写——关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方的内容,通讯结束时关闭文件。
Socket在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。
Socket保证了不同计算机之间的通信,对于网站,通信模型是服务器与客户端之间的通信。两端都建立一个socket对象,然后通过socket对象对数据进行传输。通常服务器处于一个无限循环,等待客户端的连接。
(1)服务器端编程的步骤
①加载套接字库,创建套接字WSAStartup();
在使用socket之前要进行版本的设定和初始化,应用程序只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。根据版本初始化windows socket,返回0表示成功。
②创建套接字,使用TCP协议;
有套接字的接口才能进行通信。
③绑定套接字到一个 IP 地址和一个端口上(bind());
用bind()函数确定socket各种属性。
④将套接字设置为监听模式等待连接请求(listen());
⑤循环等待请求到来,每接受一个连接请求,返回一个新的对应于此次连接的套接字(accept());
accept()是一个阻塞函数,如果没有客户端请求,连接会一直等待在这里。该函数会返回一个新的套接字,这个新的套接字是用来与客户端通信的套接字,之前那个套接字是用来监听的套接字。
⑥用返回的套接字和客户端进行通信(send()/recv());
⑦关闭套接字,关闭加载的套接字库(closesocket())。
(2)客户端编程的步骤
①加载套接字库,创建套接字WSAStartup();
要连接的服务器的ip,因为现在服务器端就是本机,所以写本机ip,127.0.0.1一个特殊的IP地址,表示是本机的IP地址。
②向服务器发出连接请求(connect());
如果没有成功连接到服务器,一直循环,直至连接上为止。
③和服务器端进行通信(send()/recv());
④关闭套接字,关闭加载的套接字库(closesocket())。
客户端
myClient.h
#ifndef MYCLIENT_H
#define MYCLIENT_H
#include
#include
#include "winsock2.h"
#include "stdlib.h"
#include "stdio.h"
#include "string"
#include
#pragma comment (lib, "ws2_32.lib")
QT_BEGIN_NAMESPACE
namespace Ui { class MyClient; }
QT_END_NAMESPACE
class MyClient : public QWidget
{
Q_OBJECT
public:
MyClient(QWidget *parent = nullptr);
~MyClient();
private slots:
void on_btcon_clicked();
void on_btsend_clicked();
void on_btclose_clicked();
private:
Ui::MyClient *ui;
};
class WorkThread : public QThread
{
public:
WorkThread();
protected:
void run();
};
#endif // MYCLIENT_H
main.cpp
主要是解决中文乱码问题,这里给出解决方案的一种
//添加库
#include
//设置编码GBK
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
myClient.cpp
#include "myclient.h"
#include "ui_myclient.h"
Ui::MyClient* mui;
SOCKET serverSocket;//服务器
MyClient::MyClient(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyClient)
{
ui->setupUi(this);
mui = ui;
}
void MyClient::on_btcon_clicked()
{
WorkThread* thread = new WorkThread();
thread->start();
}
void MyClient::on_btsend_clicked()
{
char* buff;
QString str = mui->tesend->toPlainText();
QByteArray ba = str.toLocal8Bit();
buff = ba.data();
int r = send(serverSocket, buff, strlen(buff), NULL);
//根据r的值查看是否发送成功
}
void recvAndShow()
{
int r, i = 0;
char buff[256];
while (1)
{
memset(buff, 0, 256);
r = recv(serverSocket, buff, 255, NULL);
if (r > 0)
{
mui->tbrecv->append(QString::fromLocal8Bit(buff,256));
i++;
}
}
}
void MyClient::on_btclose_clicked()
{
closesocket(serverSocket);
WSACleanup();
return;
}
MyClient::~MyClient()
{
delete ui;
}
WorkThread::WorkThread()
{
}
void WorkThread::run()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData) != 0;//成功==0
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)//请求版本失败
{
return ;
}
//创建socket
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)//创建socket失败!
{
return ;
}
//地址族
SOCKADDR_IN addr = { 0 };
//初始化地址
int port = mui->leport->text().toInt();
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);//端口号~65535尽量大于1W
int r = ::connect(serverSocket, (SOCKADDR*)&addr, sizeof addr);
struct sockaddr_in conn;
memset(&conn, 0, sizeof(struct sockaddr_in));
int len = sizeof(conn);
int ret = ::getsockname(serverSocket, (SOCKADDR*)&conn, &len);
mui->luser->setText(QString(QLatin1String(inet_ntoa(conn.sin_addr)))+QString::number(ntohs(conn.sin_port)));
if (r == -1)//连接服务器失败!
{
return ;
}
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)recvAndShow, NULL, NULL, NULL);
}
Ui设计
服务端
myServer.h
#ifndef MYSERVER_H
#define MYSERVER_H
#include
#include
#include "winsock2.h"
#include "stdlib.h"
#include "stdio.h"
#include "string"
#include
#include
#pragma comment (lib, "ws2_32.lib")
QT_BEGIN_NAMESPACE
namespace Ui { class MyServer; }
QT_END_NAMESPACE
class MyServer : public QWidget
{
Q_OBJECT
public:
MyServer(QWidget *parent = nullptr);
~MyServer();
private slots:
void on_btopen_clicked();
void on_btclose_clicked();
private:
Ui::MyServer *ui;
};
class WorkThread : public QThread
{
public:
WorkThread();
protected:
void run();
};
void communication(LPVOID n);
#endif // MYSERVER_H
main.cpp
与客户端相同
myServer.cpp
#include "myserver.h"
#include "ui_myserver.h"
Ui::MyServer* mui;
SOCKET clientSocket[1024];
SOCKET serverSocket;
SOCKADDR_IN addr = { 0 };
int k = 0;
MyServer::MyServer(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyServer)
{
ui->setupUi(this);
mui = ui;
}
MyServer::~MyServer()
{
delete ui;
}
void MyServer::on_btopen_clicked()
{
WorkThread* thread = new WorkThread();
thread->start();
}
void MyServer::on_btclose_clicked()
{
closesocket(serverSocket);
closesocket(*clientSocket);
WSACleanup();
}
void communication(LPVOID n)
{
char buff[256];
int r;
int i = (int)n;
while (1)
{
struct sockaddr_in sa;
int len = sizeof(sa);
if (!getpeername(clientSocket[i - 1], (struct sockaddr*)&sa, &len))
{
//printf("对方IP:%s ", inet_ntoa(sa.sin_addr));
//printf("对方PORT:%d ", ntohs(sa.sin_port));
}
memset(buff, 0, sizeof(buff));
r = recv(clientSocket[i - 1], buff, 255, NULL);
if (r > 0)
{
char* tem = buff;
QString s = QString(QLatin1String(inet_ntoa(sa.sin_addr))) + ":" + QString::number(ntohs(sa.sin_port)) + ":" + QString::fromLocal8Bit(buff, 256);
mui->tbinfor->append(s);
for (int j = 0; j < k; j++)
{
QByteArray ba = s.toLocal8Bit();
char* buff1 = ba.data();
int len = send(clientSocket[j], buff1, strlen(buff1), NULL);
if (len <= 0) {
return ;
}
}
}
}
}
WorkThread::WorkThread()
{
}
void WorkThread::run()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData) != 0;//成功==0
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
return;
}
//创建socket
//sockSer = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);//AF=Address family ,ipv4,TCP,0
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
return;
}
//初始化地址
int port = mui->leport->text().toInt();
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);//端口号~65535
int r = bind(serverSocket, (SOCKADDR*)&addr, sizeof(addr));
if (r == -1)//绑定失败
{
return;
}
r = listen(serverSocket, 10);
if (r == -1)//监听失败
{
return;
}
//连接
//地址族
SOCKADDR_IN cAddr = { 0 };
int len = sizeof(cAddr);
int i = 0;
while (i < 1024)
{
clientSocket[i++] = accept(serverSocket, (sockaddr*)&cAddr, &len);
k++;
if (clientSocket[i - 1] == SOCKET_ERROR)//错误的客户端!
{
closesocket(serverSocket);
WSACleanup();
return;
}
mui->tbuser->append(QString(QLatin1String(inet_ntoa(cAddr.sin_addr))) + ":" + QString::number(ntohs(cAddr.sin_port)) + QString::fromLocal8Bit(" 上线了!"));
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)communication, (LPVOID)i, NULL, NULL);
}
return;
}
ui设计