0


Winsock套接字开发网络聊天室实例(C/S)模式

聊天室的基本要求

聊天器采用客户端/服务器(C/S)模式;
1,客户端利用UDP与服务器连接,客户端与客户端之间通过UDP互相通讯;
2,服务器端具有服务器端口设置,维护客户端个人信息,记录客户端状态,分配账号等功能
客户端具有服务器地址及端口设置,用户注册,用户登陆,添加好友和删除好友,查看好友信 息,给好友发送消息等功能;
3,服务器与客户端间、客户端之间的交互采用控制台方式或GUI窗口方式均可;


聊天器实例的实现情况

1,程序完成了基于客户端/服务器(C/S)模式的设计目标,模拟出类似于QQ聊天室的应用方式,并实现其相关的基本功能。
2,程序完成了基于UDP的设计目标,实现了客户端与服务器,客户端与客户端之间通过UDP互相通讯的设计模式。
3,服务器端实现了具有服务器端口设置,维护客户端个人信息,记录客户端状态等应用功能;客户端实现了具有服务器地址及端口设置,用户注册,用户登陆,给好友发送消息,实现多个用户之间群聊等应用功能。
4,不足之处在于服务器与客户端间、客户端之间采用控制台方式实现了相互交互,未实现基于GUI窗口开发的方式。


运行注意事项(看完再去运行蛤)

这个基于winsock的UDP网络聊天器是本人的期末大作业的内容,因为自己知道做一个课程设计对于很多同学来说难度都太大了,为了让大家能够在更短的时间内有一份可以完整提交的项目以及答辩顺利,所以我无偿的发布出我的大作业供大家学习使用。

项目基于的环境

  • windows操作系统
  • Visual studio 2022 编译器
  • X86(debug)运行架构

网上的项目繁多,所以需要找到适合自己的去学习使用,比如这门课对于苹果电脑的同学就不是很友好。

项目运行须知

同绝大多数的通信实例一样,网络编程对于网络环境的环境&介质要求很高,所以在运行项目之前,请确保运行项目的主机网络的畅通,关闭windows系统的防火墙,连上自己的手机热点做局域网测试效果为佳。

还有要清楚项目的运行逻辑,基于C/S模型的程序实例,一定要记得要先运行服务器,然后在运行客户端,客户端才可以正常访问服务器,并且要注意服务器和客户端的IP与端口信息,这个对于网络通信是非常重要的,很多bug往往不是代码本身的问题,而是出现在了网络配置的问题上,所以如果你是第一在电脑上运行项目,那么第一次就要做好程序在主机上的网络配置,找到代码里有关于网络配置的相关定义进行修改哦。

最后,在用编辑器调试程序运到报错运行不起来时,要学会复制所报出的问题,在百度上搜索问题的答案,这是基本功。


部分实例演示

服务器的运行界面

客户端的运行界面

用户上线提醒

用户注册提醒

私聊功能的实现

群聊功能的实现

聊天室的整体设计

该程序采用经典的c/s架构,即采用客户端/服务器架构。模型的设计包括三个主要部分,即文件存储部分,服务器部分,以及客户端部分。

如图3.1.1所示,其中,服务器的功能为接收发送器的消息请求,并根据消息类型进行不同的响应处理;服务器中通过文件存储用户的用户名和密码。

客户端的功能实现包括两个部分:发送器和接收器。其中发送器所实现的功能包括注册新账号,登录已有账号,发送群聊消息和私聊消息等;接收器主要实现的功能包括接收服务器转发的群聊消息和私聊消息,并将其显示在显示屏上。

服务器要处理的消息类型一共有五种,分别是登录请求、注册请求、群聊消息、私聊消息、退出命令。这五种消息类型,设计使用字符串的第一个字符来进行区分,比如’L‘是Login的首字母,用来作为登录请求的标志,’R‘是Rigister的首字母,用来作为注册请求的标志,’G‘是Group的首字母,用来作为群聊消息的标志,’P'是Personal的首字母,用来作为私聊消息的标志,最后字符串"exit"可以作为用户退出的命令。

服务器要处理的五种消息类型对应着聊天器所要实现的五种功能,即登录功能,注册功能,群聊功能,私聊功能,关闭程序。下文将针对聊天器所要实现的功能给出设计的方案与实现。

聊天室服务器完整代码

#include<WinSock2.h>
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

#define DEFAULT_PORT 5055
#define BUFFER_LENGTH 1024
class user
{
public:
    user(string username, string ip, int sender_port, int receiver_port)
    {
        this->username = username;
        this->ip = ip;
        this->sender_port = sender_port;
        this->receiver_port = receiver_port;

        //设置接收器的地址
        receiver.sin_family = AF_INET;
        receiver.sin_port = htons(receiver_port);
        char *addr = new char[ip.length() + 1];
        strcpy(addr, ip.c_str());
        receiver.sin_addr.s_addr = inet_addr(addr);
    }
    string username;              //用户名
    string ip;                    //客户端ip地址
    int sender_port;              //发送器端口
    int receiver_port;              //接收器端口
    struct sockaddr_in receiver;  //存储接收器的地址
};
class server
{
public:
    bool Startup();                                                   //检测是否满足服务器运行的环境
    bool SetServerSocket();                                           //设置服务器用来监听信息的socket套接字
    bool Checktxt();                                                  //检测存储文件是否存在,若不存在,创建一个
    void work();                                                      //服务器运行的主函数
    void SendMessage(string message, struct sockaddr_in x);           //发送信息的函数
    void Sendonlinelist();                                            //向客户端发送好友在线列表
    bool TestUsernameAndPassword(string username, string password, int &flag);   //测试用户名和密码是否正确
    bool TestDuplicateLogin(string username);                         //测试是否重复登录
    bool TestDuplicateRigister(string username);                      //测试是否重复注册
    string Getusername(string ip, int port);                           //根据ip和端口号获得用户名
    int  Getuserindex(string username);                               //根据用户名获得用户在在线用户表的索引号    
    void extractLoginuserinfor(string userinfor, string &username, string &password, string &receiverport); //提取登录请求中的用户名密码和显示器端口号
    void extractRegisteruserinfor(string userinfor, string&username, string&password);                       //提取注册请求中的用户名和密码
    void extactPersonalMessageReceivername(string &message, string &receivername);                           //提取私聊消息中的接收者的姓名
    
private:
    WSADATA wsaData;
    SOCKET sSocket;                            //用来接收消息的套接字
    struct sockaddr_in ser;                    //服务器地址
    struct sockaddr_in cli;                    //客户地址
    int cli_length = sizeof(cli);                //客户地址长度
    char recv_buf[BUFFER_LENGTH];              //接收数据的缓冲区
    vector<user> usertable;                    //在线用户表
    string sendmessage, printmessage;           //存储服务器转发、打印用的字符串
    int iSend, iRecv;                          //存储服务器发送和接收的字符串的长度
};

bool server::Startup()
{
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        cout << "Failed to load Winsock." << endl;
        return false;
    }
    return true;
}
bool server::SetServerSocket()
{
    //产生服务器端套接口
    sSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (sSocket == INVALID_SOCKET)
    {
        cout << "socket()Failed:" << WSAGetLastError() << endl;
        return false;
    }
    //建立服务器端地址
    ser.sin_family = AF_INET;
    ser.sin_port = htons(DEFAULT_PORT);               //htons()函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
    ser.sin_addr.s_addr = htonl(INADDR_ANY);          //htonl()函数把一个主机字节顺序的数转换为网络字节顺序的数   
    if (bind(sSocket, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR)
    {
        cout << "bind()Failed:" << WSAGetLastError() << endl;
        return false;
    }
    return true;
}
void server::SendMessage(string message, struct sockaddr_in x)
{
    char *send_buf = new char[message.length() + 1];
    strcpy(send_buf, message.c_str());
    SOCKET rSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (rSocket == INVALID_SOCKET)
    {
        cout << "socket()Failed:" << WSAGetLastError() << endl;
        return;
    }
    iSend = sendto(rSocket, send_buf, message.length() + 1, 0, (SOCKADDR*)&(x), sizeof(x));
    if (iSend == SOCKET_ERROR)
    {
        cout << "sendto failed:" << WSAGetLastError() << endl;
        closesocket(rSocket);
        return;
    }
    closesocket(rSocket);
}
void server::Sendonlinelist()
{
    string onlinelist;
    for (int i = 0; i < usertable.size(); i++)
        onlinelist = onlinelist + usertable[i].username + "#";
    onlinelist = onlinelist + "$";                //结束标志
    SendMessage(onlinelist, cli);
}
bool server::TestUsernameAndPassword(string username, string password, int &flag)
{
    if (!Checktxt())
    {
        cout << "无法找到存储文件." << endl << endl;
        flag = 0;                                 //未找到用户名的标志
        return false;
    }
    fstream in("C:\\userform\\userform.txt");
    string line;
    string username_txt, password_txt;
    while (getline(in, line))
    {
        for (int i = 0; i < line.size(); i++)
        {
            if (line[i] == '#')
            {
                username_txt = line.substr(0, i);
                password_txt = line.substr(i + 1);
                break;
            }
        }
        if (username_txt == username)         //该用户名存在
        {
            if (password == password_txt)     //且密码正确
            {
                in.close();
                return true;                  //返回验证成功
            }
            cout << "用户" << username << "登录密码错误" << endl << endl;       //返回密码错误的信息
            flag = 1;                        //密码错误的标志
            return false;
        }
    }
    in.close();
    cout << "未注册过的用户:" << username << endl << endl;
    flag = 0;                                 //未找到用户名的标志
    return false;
}
bool server::TestDuplicateLogin(string username)
{
    int i;
    for (i = 0; i < usertable.size(); i++)
        if (usertable[i].username == username) break;
    if (i == usertable.size())     //该用户还没有登录过
        return false;
    else
    {
        cout << "用户" << username << "重复登录" << endl;
        return true;
    }
}
bool server::TestDuplicateRigister(string username)
{
    if (!Checktxt())
    {
        cout << "无法找到存储文件." << endl << endl;
        return true;
    }
    fstream in("C:\\userform\\userform.txt");
    string line;
    while (getline(in, line))
    {
        string username_txt;
        for (int i = 0; i < line.size(); i++)
        {
            if (line[i] == '#')
            {
                username_txt = line.substr(0, i);              //提取用户名
                if (username_txt == username)                  //对比,相等则表明已经注册过
                {
                    in.close();
                    cout << "用户名" << username << "重复注册" << endl << endl;
                    return true;
                }
                break;                                         //否则继续对比下一个用户名
            }
        }
    }
    in.close();
    return false;                                               //代码执行到这说明该用户名还没有注册过
}
string server::Getusername(string ip, int port)
{
    for (int i = 0; i < usertable.size(); i++)
        if (usertable[i].ip == ip&&usertable[i].sender_port == port)
            return usertable[i].username;
    cout << "非法的用户连接上服务器" << endl;
    cout << "ip地址为:" << ip << endl << "端口号为:" << port << endl;
    return "";
}
int server::Getuserindex(string username)
{
    int i = 0;
    for (i = 0; i < usertable.size(); i++)
        if (usertable[i].username == username) break;
    return i;
}
void server::extractLoginuserinfor(string userinfor, string &username, string &password, string &receiverport)
{
    int i;
    for (i = 0; i < userinfor.length(); i++)           //提取用户名
    {
        if (userinfor[i] == '#')
        {
            username = userinfor.substr(0, i);
            break;
        }
    }
    for (int j = i + 1; j < userinfor.length(); j++)  //提取密码和显示器端口号
    {
        if (userinfor[j] == '#')
        {
            password = userinfor.substr(i + 1, j - i - 1);
            receiverport = userinfor.substr(j + 1);
            break;
        }
    }
}
void server::extractRegisteruserinfor(string userinfor, string&username, string&password)
{
    for (int i = 0; i < userinfor.size(); i++)
    {
        if (userinfor[i] == '#')
        {
            username = userinfor.substr(0, i);
            password = userinfor.substr(i + 1);
            break;
        }
    }
}
void server::extactPersonalMessageReceivername(string &message, string &receivername)
{
    for (int i = 0; i < message.size(); i++)
    {
        if (message[i] == '#')
        {
            receivername = message.substr(0, i);
            message = message.substr(i + 1);
            break;
        }
    }
}
bool server::Checktxt()
{
    FILE *fp = fopen("C:\\userform\\userform.txt", "r");
    if (fp == NULL)
    {
        system("md C:\\userform");
        ofstream out("C:\\userform\\userform.txt");
        if (!out)
            return false;
        out.close();
        return true;
    }
    return true;
}
void server::work()
{
    cout << "-----------------" << endl;
    cout << "Server running" << endl;
    cout << "-----------------" << endl;
    while (true)                                       //进入一个无限循环,进行数据接收和发送
    {
        memset(recv_buf, 0, sizeof(recv_buf));         //初始化接收缓冲区
        iRecv = recvfrom(sSocket, recv_buf, BUFFER_LENGTH, 0, (struct sockaddr*)&cli, &cli_length);
        if (iRecv == SOCKET_ERROR)
        {
            cout << "recvfrom()Failed:" << WSAGetLastError() << endl;
            continue;
        }

        //获取发送方的地址(ip和端口)
        char *x = inet_ntoa(cli.sin_addr); string address(x);         //获取客户端ip
        int userport = ntohs(cli.sin_port);                           //获取客户端端口

        string infortype = string(recv_buf);                          //根据infortype[0]来判断消息的类型       
        if (infortype[0] == 'L')                                      //登录请求
        {
            string userinfor = infortype.substr(1);                   //除去消息类型
            string username, password, receiver_port;
            extractLoginuserinfor(userinfor, username, password, receiver_port);  //提取用户名和密码
                                                                                  //向不合法用户发送登录失败的回应
            int flag = 0;
            if (!TestUsernameAndPassword(username, password, flag))
            {
                if (flag == 0)
                    SendMessage("0", cli);
                if (flag == 1)
                    SendMessage("1", cli);
                continue;
            }
            //查询该用户是否重复登录
            if (TestDuplicateLogin(username))
            {
                SendMessage("2", cli);
                continue;
            }
            //将合法的未登录的用户加入列表
            int receiver_port_int = atoi(receiver_port.c_str());
            user newuser(username, address, userport, receiver_port_int);
            usertable.push_back(newuser);

            printmessage = "(上线消息)" + newuser.username + "已上线";               //设置要打印的消息
            sendmessage = printmessage;                                           //设置要转发的消息    
            SendMessage("Y", cli);                                                //向客户端发送登录成功的回应
        }
        else if (infortype[0] == 'R')                 //注册信息
        {
            string userinfor = infortype.substr(1);                  //除去消息类型        
            string username, password;
            extractRegisteruserinfor(userinfor, username, password); //提取用户名和密码

                                                                     //检测用户名是否已经注册过
            if (TestDuplicateRigister(username))
            {
                SendMessage("N", cli);
                continue;
            }
            //向文件写入新注册的用户名和密码
            if (!Checktxt())
            {
                SendMessage("N", cli);
                continue;
            }
            fstream out("C:\\userform\\userform.txt", ios::app);
            out << userinfor << endl;
            out.close();
            //发送注册成功的回应
            SendMessage("Y", cli);
            cout << "注册成功" << endl << "新用户名为:" << username << endl << endl;
            continue;
        }
        else if (infortype[0] == 'G')                                          //群聊消息
        {
            string message = infortype.substr(1);
            string sendername = Getusername(address, userport);                  //获取发送者姓名
            if (sendername == "")   continue;
            printmessage = "(群消息)" + sendername + ":" + message;             //设置要打印的消息
            sendmessage = printmessage;
            //sendmessage = "G#"+sendername + ":" + message;                       //设置要转发的消息
        }
        else if (infortype[0] == 'P')                     //私聊消息
        {
            if (infortype[1] == 'L')                      //获取在线好友列表的请求
            {
                Sendonlinelist();
                continue;
            }
            if (infortype[1] == 'M')                      //私聊消息
            {
                string message = infortype.substr(2);
                string sendername = Getusername(address, userport);  //提取发送者姓名
                if (sendername == "")  continue;
                //提取接收者姓名
                string receivername;
                extactPersonalMessageReceivername(message, receivername);
                //检查接收者是否离线
                int i = Getuserindex(receivername);
                if (i == usertable.size())                              //接收者已经离线
                {
                    SendMessage("N", cli);
                    continue;
                }
                SendMessage("Y", cli);                                  //向发送方发送成功的响应
                printmessage = "(私消息)" + sendername + "->" + receivername + ":" + message;               //设置要打印的消息
                cout << printmessage << endl;
                cout << "用户ip:" << address << endl;
                cout << "用户端口:" << userport << endl;
                cout << "当前在线人数:" << usertable.size() << endl << endl;
                sendmessage = printmessage;                                                                 //设置要发送的消息
                SendMessage(sendmessage, usertable[i].receiver);
                if (sendername != receivername)
                {
                    int j = Getuserindex(sendername);
                    SendMessage(sendmessage, usertable[j].receiver);
                }
                continue;
            }
        }
        else if (infortype == "exit")
        {
            string sendername = Getusername(address, userport);
            if (sendername == "") continue;
            int i = Getuserindex(sendername);
            if (i >= usertable.size() || i < 0) continue;
            SendMessage("exit", usertable[i].receiver);                                 //向该用户显示器发送退出命令
            usertable.erase(usertable.begin() + i);
            printmessage = "(下线消息)" + sendername + "已下线";                         //设置要打印的消息
            sendmessage = printmessage;                                                 //设置要转发的消息

        }
        //在服务器上打印消息    
        cout << printmessage << endl;
        cout << "用户ip:" << address << endl;
        cout << "用户端口:" << userport << endl;
        cout << "当前在线人数:" << usertable.size() << endl << endl;
        //向客户端发送消息
        for (int i = 0; i < usertable.size(); i++)
            SendMessage(sendmessage, usertable[i].receiver);
    }
    

}

int main()
{
    server x;
    if (x.Startup() == false)
        return 0;
    if (x.SetServerSocket() == false)
        return 0;
    x.work();
    return 0;
}

客户端发送器的完整代码

#include<Winsock2.h>
#include<iostream>
#include<string>
#include<ctime>
#include<tchar.h>
#include<Windows.h>
#include<fstream>
#include<vector>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

#define DEFAULT_PORT 5055
#define DATA_BUFFER 1024

class client
{
public:
    bool Startup();
    void SetServerAddress();
    int GeneratePort();               //随机生成显示器端口号
    bool Getonlinelist();             //获得在线的用户名
    void work();                      //发送器的主函数
private:
    WSADATA wsaData;
    SOCKET sClient;                       //发送信息和接收信息时使用的套接字
    struct sockaddr_in ser;               //保存服务器的地址
    int ser_length = sizeof(ser);
    struct sockaddr_in communication;
    int communication_length = sizeof(communication);
    char recv_buf[DATA_BUFFER];           //接收信息的缓冲区
    int receiver_port;                    //显示器的端口号
    vector<string> onlinelist;            //保存在线用户的用户名
    int iSend, iRecv;
};

bool client::Startup()
{
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        cout << "Failed to load Winsock." << endl;
        return false;
    }
    sClient = socket(AF_INET, SOCK_DGRAM, 0);
    if (sClient == INVALID_SOCKET)
    {
        cout << "socket()Failed:" << WSAGetLastError() << endl;
        return false;
    }
    return true;
}
void client::SetServerAddress()
{
    cout << "请输入ip地址:";
    string iptemp;
    cin >> iptemp;
    char *ip = new char[iptemp.length() + 1];
    strcpy(ip, iptemp.c_str());
    //建立服务器端地址
    ser.sin_family = AF_INET;
    ser.sin_port = htons(DEFAULT_PORT);
    ser.sin_addr.s_addr = inet_addr(ip);
}
int client::GeneratePort()
{
    srand((unsigned)time(NULL));
    int x = 1024 + rand() % (5000 - 1024);
    return x;
}
bool client::Getonlinelist()            //向服务器请求获取好友在线列表
{
    if (onlinelist.size() > 0)
        onlinelist.clear();
    char getonlinelist[3] = "PL";
    iSend = sendto(sClient, getonlinelist, 3, 0, (struct sockaddr*)&ser, ser_length);
    if (iSend == SOCKET_ERROR)
    {
        cout << "sendto()Failed:" << WSAGetLastError() << endl;
        return false;
    }
    memset(recv_buf, 0, sizeof(recv_buf));
    iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&communication, &communication_length);   ///
    if (iRecv == SOCKET_ERROR)
    {
        cout << "recvfrom() Failed" << WSAGetLastError() << endl;
        return false;
    }
    string list(recv_buf);
    string friendname;
    for (int i = 0; i < list.length(); i++)
    {
        if (list[i] == '$')  break;
        else if (list[i] == '#')
        {
            onlinelist.push_back(friendname);
            friendname = "";
        }
        else
            friendname = friendname + list[i];
    }
    cout << "----------------------------" << endl;
    cout << "在线好友列表" << endl;
    for (int i = 0; i < onlinelist.size(); i++)
        cout << i << ":  " << onlinelist[i] << endl;
    cout << "----------------------------" << endl;
    return true;
}

void client::work()
{
    while (true)
    {
        memset(recv_buf, 0, sizeof(recv_buf));
        system("cls");
        cout << "****************************************" << endl;
        cout << "*                                      *" << endl;
        cout << "*       1.登录  2.注册  3.退出         *" << endl;
        cout << "*                                      *" << endl;
        cout << "****************************************" << endl;

        string choice;
        getline(cin, choice);
        if (choice == "1")
        {
            system("cls");
            cout << "请输入用户名:";
            string username;
            getline(cin, username);
            cout << "请输入密码:";
            string password;
            getline(cin, password);
            //产生显示器端口
            receiver_port = GeneratePort();
            //将端口号写入文件供显示器程序读取
            ofstream out("port.txt");
            out << receiver_port << "\n" << username;
            out.close();

            string init_infortemp = "L" + username + "#" + password + "#" + to_string(receiver_port);
            char *init_infor = new char[init_infortemp.length() + 1];
            strcpy(init_infor, init_infortemp.c_str());
            //向服务器验证用户信息
            iSend = sendto(sClient, init_infor, init_infortemp.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
            //接收服务器回应的消息
            iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (SOCKADDR*)&communication, &communication_length);
            if (iRecv == SOCKET_ERROR)
            {
                cout << "recvfrom() Failed:" << GetLastError() << endl;
                cout << "未收到服务器的响应,登录失败,请输入Y返回首页:";
                string ret;
                while (getline(cin, ret))
                {
                    if (ret == "Y")break;
                    cout << "未收到服务器的响应,登录失败,请输入Y返回首页:";
                }
                continue;
            }
            if (recv_buf[0] == 'Y')   //登录成功
            {
                system("cls");
                ShellExecute(NULL, _T("open"), _T("receiver.exe"), NULL, NULL, SW_SHOW);     //运行显示器程序
            }
            else if (recv_buf[0] == '0')
            {
                cout << "未注册用户名,登录失败,请输入Y返回首页:";
                string ret;
                while (getline(cin, ret))
                {
                    if (ret == "Y")break;
                    cout << "未注册用户名,登录失败,请输入Y返回首页:";
                }
                continue;
            }
            else if (recv_buf[0] == '1')
            {
                cout << "密码错误,登录失败,请输入Y返回首页:" << endl;
                string ret;
                while (getline(cin, ret))
                {
                    if (ret == "Y")break;
                    cout << "密码错误,登录失败,请输入Y返回首页:";
                }
                continue;
            }
            else if (recv_buf[0] == '2')
            {
                cout << "重复登录,登录失败,请输入Y返回首页:" << endl;
                string ret;
                while (getline(cin, ret))
                {
                    if (ret == "Y")break;
                    cout << "重复登录,登录失败,请输入Y返回首页:";
                }
                continue;
            }

            //选择聊天方式
            while (true)
            {
                system("cls");
                cout << "---------------------------------------------------" << endl;
                cout << "                 用户名:" << username << endl << endl;;
                cout << "            1.私聊  2.群聊  3.退出登录             " << endl << endl;
                cout << "---------------------------------------------------" << endl;
                string mode;
                getline(cin, mode);
                if (mode == "1")    //私聊
                {
                    system("cls");
                    cout << "私聊模式中,输入return返回上一级" << endl << endl;
                    if (!Getonlinelist())    continue;                           //获取好友在线列表失败
                    cout << "请选择私聊对象的序号" << endl;
                    string choose;
                    getline(cin, choose);
                    if (choose == "return") continue;
                    int i = 0;
                    for (i = 0; i < choose.size(); i++)
                        if (choose[i] > '9' || choose[i] < '0')break;
                    if (i < choose.size()) continue;    
                    stringstream stream(choose);
                    int index = 0;
                    stream >> index;
                    if (index<0 || index>=onlinelist.size()) continue;
                    while (true)                     //向该用户循环发送消息,直到输入return退出
                    {
                        system("cls");
                        cout << "正在和" << onlinelist[index] << "私聊中" << ",输入return返回上一级" << endl << endl;
                        string message;
                        getline(cin, message);
                        if (message == "return")
                        {
                            system("cls");
                            break;
                        }
                        message = "PM" + onlinelist[index] + "#" + message;
                        char *buf = new char[message.length() + 1];
                        strcpy(buf, message.c_str());
                        iSend = sendto(sClient, buf, message.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
                        if (iSend == SOCKET_ERROR)
                        {
                            cout << "sendto()Failed:" << WSAGetLastError() << endl;
                            break;
                        }
                        delete[]buf;
                        iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (SOCKADDR*)&communication, &communication_length);
                        if (recv_buf[0] == 'Y') continue;
                        else
                        {
                            cout << onlinelist[index] << "已下线" << "输入Y返回主菜单";
                            string ret;
                            while (getline(cin, ret))
                            {
                                if (ret == "Y") break;
                                cout << onlinelist[index] << "已下线" << "输入Y返回主菜单";
                            }
                            break;
                        }
                    }
                }
                else if (mode == "2")    //群聊
                {
                    system("cls");
                    while (true)
                    {
                        system("cls");
                        cout << "群聊模式,输入return返回上一级" << endl << endl;
                        string message;
                        getline(cin, message);
                        if (message == "return")
                        {
                            system("cls");
                            break;
                        }

                        message = "G" + message;
                        char *buf = new char[message.length() + 1];
                        strcpy(buf, message.c_str());
                        iSend = sendto(sClient, buf, message.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
                        delete[]buf;
                        if (iSend == SOCKET_ERROR)
                        {
                            cout << "sendto()Failed:" << WSAGetLastError() << endl;
                            break;
                        }
                    }
                    continue;
                }
                else if (mode == "3")                             //退出登录
                {
                    char buf[] = "exit";
                    iSend = sendto(sClient, buf, sizeof(buf), 0, (struct sockaddr*)&ser, ser_length);
                    break;
                }
                else
                    continue;
            }
        }
        else if (choice == "2")
        {
            system("cls");
            cout << "请设置用户名:";
            string username;
            getline(cin, username);
            cout << "请设置登录密码:";
            string password;
            getline(cin, password);
            string init_infortemp = "R" + username + "#" + password;
            char *init_infor = new char[init_infortemp.length() + 1];
            strcpy(init_infor, init_infortemp.c_str());
            //向服务器发送注册用户信息
            iSend = sendto(sClient, init_infor, init_infortemp.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
            //接收服务器回应的消息
            iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&communication, &communication_length);
            if (recv_buf[0] == 'Y')
            {
                cout << "注册成功" << endl;
                continue;
            }
            else
            {
                cout << "用户名已存在,注册失败,请输入Y返回首页:" << endl;
                string ret;
                while (getline(cin, ret))
                {
                    if (ret == "Y")break;
                    cout << "用户名已存在,注册失败,请输入Y返回首页:";
                }
                continue;
            }
        }
        else if (choice == "3")
        {
            closesocket(sClient);
            WSACleanup;
            return;
        }
        else
            continue;
    }
}

int main()
{
    client x;
    if (x.Startup() == false)
        return 0;
    x.SetServerAddress();
    x.work();
}

客户端接收器的完整代码

#include<WinSock2.h>
#include<iostream>
#include<fstream>
#include<string>
#include<ctime>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define DEFAULT_SPORT 5055
#define DEFAULT_CPORT 5056
#define BUFFER_LENGTH 1024

void main()
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        cout << "Failed to load Winsock." << endl;
        return;
    }
    //建立显示器端地址
    struct sockaddr_in receiver;
    //读取分配好的端口
    ifstream in("port.txt");
    string receiver_port;
    string username;
    getline(in, receiver_port);
    getline(in, username);
    in.close();
    remove("port.txt");
    int receiver_port_int = atoi(receiver_port.c_str());

    receiver.sin_family = AF_INET;
    receiver.sin_port = htons(receiver_port_int);    //htons()函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
    receiver.sin_addr.s_addr = htonl(INADDR_ANY);    //htonl()函数把一个主机字节顺序的数转换为网络字节顺序的数   
    SOCKET rSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (rSocket == INVALID_SOCKET)
    {
        cout << "socket()Failed:" << WSAGetLastError() << endl;
        return;
    }
    if (bind(rSocket, (LPSOCKADDR)&receiver, sizeof(receiver)) == SOCKET_ERROR)
    {
        cout << "bind()Failed:" << WSAGetLastError() << endl;
        return;
    }

    char recv_buf[BUFFER_LENGTH];                  //接收数据的缓冲区
    memset(recv_buf, 0, sizeof(recv_buf));         //初始化接收缓冲区

    struct sockaddr_in ser;                        //客户端地址
    int ser_length = sizeof(ser);                  //客户端地址长度

    cout << "----------------------------------------" << endl << endl;
    cout << "           显示器---" << username << endl << endl << endl;
    cout << "----------------------------------------" << endl << endl;

    while (true)                                   //进入一个无限循环,进行数据接收和发送
    {
        int iRecv = recvfrom(rSocket, recv_buf, BUFFER_LENGTH, 0, (SOCKADDR*)&ser, &ser_length);
        string transmessage(recv_buf);
        if (iRecv == SOCKET_ERROR)
        {
            cout << "recvfrom()Failed:" << WSAGetLastError() << endl;
            break;
        }
        else if (transmessage == "exit") break;
        else
            cout << transmessage << endl;
    }
    closesocket(rSocket);
    WSACleanup();
}

标签: 网络 服务器 udp

本文转载自: https://blog.csdn.net/weixin_51989356/article/details/128491837
版权归原作者 陈丹宇jmu 所有, 如有侵权,请联系我们删除。

“Winsock套接字开发网络聊天室实例(C/S)模式”的评论:

还没有评论