socket+epoll实现多客户端登陆注册传递位置信息,解决粘包拆包问题

s o c k e t socket socket入门:传送门
p r o t o b u f protobuf protobuf官方文档:传送门
e p o l l epoll epoll入门教程:传送门
客户端思想:注册,登录,登录成功可以听消息
服务器思想:
d b . p r o t o db.proto db.proto

syntax = "proto3";

package user;

message Person {
    
    
  string name = 1;
  string passwd = 2;
  string x = 3;
  string y =4;
}
message AddressBook {
    
    
  repeated Person people = 1;
}

命令行

protoc --cpp_out=./ db.proto

d b s e r v e r . c p p dbserver.cpp dbserver.cpp


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include<sys/types.h>
#include<sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <bits/stdc++.h>
#include <fstream>
#include <errno.h>
#include <fcntl.h>
#include "db.pb.h"
using namespace std;
using namespace user;
const int FD_SIZE = 1000;
const int EPOLL_EVENTS = 1000;
const int BUFFER_SIZE = 1505;
struct Fd_msg{
    
    
    int totalLength;
    char* totalBytes;
};
char bytes[BUFFER_SIZE];
AddressBook clients_book;
set<int> connected_fd;
unordered_map<int, Fd_msg> map_fd_msg;
Person peo;
char flag[1];
char* message;
int len;
auto intToChar4(int i)
{
    
    
    auto result = new char[4];
    result[0] = (i >> 24 & 0xFF);
    result[1] = (i >> 16 & 0xFF);
    result[2] = (i >> 8 & 0xFF);
    result[3] = (i & 0xFF);
    return result;
}
auto intToChar(int i)
{
    
    
    char result = (i & 0xFF);
    return result;
}
auto char4ToInt(char* bytes)
{
    
    
    int num = bytes[3] & 0xFF;
    num |= ((bytes[2] << 8) & 0xFF00);
    num |= ((bytes[1] << 16) & 0xFF0000);
    num |= ((bytes[0] << 24)  & 0xFF000000);
    return num;
}
void add_epoll_event(int epollfd, int fd, int state){
    
    
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}
void delete_epoll_event(int epollfd,int fd,int state){
    
    
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}

void modify_event(int epollfd,int fd,int state){
    
    
	struct epoll_event ev;
	ev.events = state;
	ev.data.fd = fd;
	epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}
bool existt(Person peo)
{
    
    
    for(int i = 0; i < clients_book.people_size(); i++){
    
    
        const Person& person = clients_book.people(i);
        if (person.name() == peo.name() && person.passwd() == peo.passwd())
        {
    
    
            return true;
        }
    }
    return false;
}
void updatetheclient(Person* person, Person peo)
{
    
    
    for(int i = 0; i < clients_book.people_size(); i++){
    
    
        Person person = clients_book.people(i);
        if (person.name() == peo.name() && person.passwd() == peo.passwd())
        {
    
    
            person.set_x(peo.x());
            person.set_y(peo.y());
            return ;
        }
    }
}
void PromptForclient(Person* person, Person peo) {
    
    
  string name, passwd;
  person->set_name(peo.name());
  person->set_passwd(peo.passwd());
  person->set_x(peo.x());
  person->set_y(peo.y());
  cout << "has added " << peo.name() << " " << peo.passwd() << " " << peo.x() << " " << peo.y() <<endl;
}
void handle_accept(int epollfd, int serv_sock)
{
    
    
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    int flags = fcntl(clnt_sock,F_GETFL,0);
    fcntl(clnt_sock,F_SETFL,flags| O_NONBLOCK);
    add_epoll_event(epollfd, clnt_sock, EPOLLIN);
    Fd_msg* fd_msg = (Fd_msg*)malloc(sizeof(Fd_msg));
    fd_msg->totalLength = 0;
    fd_msg->totalBytes = new char(0);
    map_fd_msg[clnt_sock] = *fd_msg;
}
void pack()
{
    
    
    len = peo.ByteSizeLong();
    char* messagebody = new char[len];
    peo.SerializeToArray(messagebody, len);
    char* lengthBytes = new char(4);
    lengthBytes = intToChar4(len + 1);
    char messageid;
    messageid = intToChar(1);
    message = new char(len + 5);
    for (int i = 0; i < 4; i++) {
    
    
        message[i] = lengthBytes[i];
    }
    message[4] = messageid;
    for (int i = 5; i < 5 + len; i++) {
    
    
        message[i] = messagebody[i - 5];
    }
}
void boardcast(int epollfd, int now_fd)
{
    
    
    for (auto fd : connected_fd) {
    
    
        modify_event(epollfd, fd, EPOLLOUT);
    }
}
void handle_read(int epollfd, int fd)
{
    
    
    auto iter = map_fd_msg.find(fd);
    Fd_msg* fd_msg = &iter->second;
    while ((len = read(fd, bytes, 1505)) != -1) {
    
    
        int tmpLength = fd_msg->totalLength;
        fd_msg->totalLength += len;
        char* tmpBytes = new char[tmpLength];
        for (int i = 0; i < tmpLength; i++) {
    
    
            tmpBytes[i] = fd_msg->totalBytes[i];
        }
        fd_msg->totalBytes = new char[fd_msg->totalLength];
        for (int i = 0; i < tmpLength; i++) {
    
    
            fd_msg->totalBytes[i] = tmpBytes[i];
        }
        for (int i = tmpLength; i < fd_msg->totalLength; i++) {
    
    
            fd_msg->totalBytes[i] = bytes[i - tmpLength];
        }
        while (fd_msg->totalLength >= 5) {
    
    
            char *lengthBytes = new char[4];
            for (int i = 0; i < 4; i++) {
    
    
                lengthBytes[i] = fd_msg->totalBytes[i];
            }
            int contentLength = char4ToInt(lengthBytes) - 1;
            char messageid = fd_msg->totalBytes[4];
            int f = messageid & 0xFF;
            if (fd_msg->totalLength < contentLength + 5) {
    
    
                break;
            }
            char* contentBytes = new char[contentLength];
            for (int i = 0; i < contentLength; i++) {
    
    
                contentBytes[i] = fd_msg->totalBytes[i + 5];
            }
            peo.ParseFromArray(contentBytes, contentLength);
            if (f == 1) {
    
    
                PromptForclient(clients_book.add_people(), peo);
            } else if (f == 2) {
    
    
                flag[0] = '1';
                if (existt(peo) == false) {
    
    
                    flag[0] = '0';
                    write(fd, flag, 1);
                } else {
    
    
                    connected_fd.insert(fd);
                    write(fd, flag, 1);
                    boardcast(epollfd, fd);
                }
            } else if (f == 3) {
    
    
                updatetheclient(clients_book.add_people(), peo);
                boardcast(epollfd, fd);
            }
            int leftLength = fd_msg->totalLength - (5 + contentLength);
            char* leftBytes = new char[fd_msg->totalLength];
            for (int i = contentLength + 5; i < fd_msg->totalLength; i++) {
    
    
                leftBytes[i - 5 - contentLength] = fd_msg->totalBytes[i];
            }
            fd_msg->totalBytes = new char(leftLength);
            for (int i = 0; i < leftLength; i++) {
    
    
                fd_msg->totalBytes[i] = leftBytes[i];
            }
            fd_msg->totalLength = leftLength;
        }
    }
}
void handle_write(int epollfd, int fd)
{
    
    
    pack();
    write(fd, message, len + 5);
    modify_event(epollfd, fd, EPOLLIN);
}
void handle_epoll_events(int epollfd, struct epoll_event *events, int num, int serv_sock){
    
    
    for (int i = 0; i < num; i++) {
    
    
        int fd = events[i].data.fd;
        if ((fd == serv_sock) && (events[i].events & EPOLLIN)) {
    
    
            handle_accept(epollfd, serv_sock);
        } else if (events[i].events & EPOLLIN) {
    
    
            handle_read(epollfd, fd);
        } else if (events[i].events & EPOLLOUT) {
    
    
            handle_write(epollfd, fd);
        }
    }
}
int main(int argc, char* argv[]) {
    
    
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(1234);
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    listen(serv_sock, 20);
    int epollfd = epoll_create(FD_SIZE);
    add_epoll_event(epollfd, serv_sock, EPOLLIN);
    struct epoll_event events[EPOLL_EVENTS];
    while(1) {
    
    
        int ret = epoll_wait(epollfd,events,EPOLL_EVENTS,-1);
		handle_epoll_events(epollfd,events,ret,serv_sock);
    }
    close(epollfd);
    close(serv_sock);
    google::protobuf::ShutdownProtobufLibrary();
    return 0;
}


d b c l i e n t . c p p dbclient.cpp dbclient.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <bits/stdc++.h>
#include "db.pb.h"
using namespace std;
using namespace user;
const int HS = 4;
int sock, f;
Person peo;
string name, passwd, x, y;
char bytes[1505];
// int - > char4
auto intToChar4(int i)
{
    
    
    auto result = new char[4];
    result[0] = (i >> 24 & 0xFF);
    result[1] = (i >> 16 & 0xFF);
    result[2] = (i >> 8 & 0xFF);
    result[3] = (i & 0xFF);
    return result;
}
// int - > char
auto intToChar(int i)
{
    
    
    char result = (i & 0xFF);
    return result;
}
auto char4ToInt(char* bytes)
{
    
    
    int num = bytes[3] & 0xFF;
    num |= ((bytes[2] << 8) & 0xFF00);
    num |= ((bytes[1] << 16) & 0xFF0000);
    num |= ((bytes[0] << 24)  & 0xFF000000);
    return num;
}
void pack(int f, Person peo)
{
    
    
    int len = peo.ByteSizeLong();
    char* messagebody = new char[len];
    peo.SerializeToArray(messagebody, len);
    char* lengthBytes = new char[4];
    lengthBytes = intToChar4(len + 1);
    char messageid;
    messageid = intToChar(f);
    char* message = new char(len + 5);
    for (int i = 0; i < 4; i++) {
    
    
        message[i] = lengthBytes[i];
    }
    message[4] = messageid;
    for (int i = 5; i < 5 + len; i++) {
    
    
        message[i] = messagebody[i - 5];
    }
    write(sock, message, len + 5);
}
void input()
{
    
    
     cout << "press 1 to register and place your origin position" << endl;
     cout << "press 2 to login" << endl;
     cin >> f;
     if (f == 1) {
    
    
         cout << "please input your username" << endl;
         cin >> name;
         cout << "please input your passwd" << endl;
         cin >> passwd;
         cout << "please input your origin positon x, y" << endl;
         cin >> x >> y;
         peo.set_name(name);
         peo.set_passwd(passwd);
         peo.set_x(x);
         peo.set_y(y);
     } else if (f == 2) {
    
    
         cout << "please input your username" << endl;
         cin >> name;
         cout << "please input your passwd" << endl;
         cin >> passwd;
     } else if (f == 3) {
    
    
         cout << "please input your next positon x, y" << endl;
         cin >> x >> y;
     }
     pack(f, peo);
     if (f == 2) {
    
    
         char flag[1];
         read(sock, flag, sizeof(flag));
         cout << flag[0] << endl;
         if (flag[0] == '1') {
    
    
             cout << "you is logging." << endl;
             int totalLength = 0, len = 0;
             char* totalBytes = new char(0);
             while ((len = read(sock, bytes, 1505)) != -1) {
    
    
                 int tmpLength = totalLength;
                 totalLength += len;
                 char* tmpBytes = new char[tmpLength];
                 for (int i = 0; i < tmpLength; i++) {
    
    
                     tmpBytes[i] = totalBytes[i];
                 }
                 totalBytes = new char[totalLength];
                 for (int i = 0; i < tmpLength; i++) {
    
    
                     totalBytes[i] = tmpBytes[i];
                 }
                 for (int i = tmpLength; i < totalLength; i++) {
    
    
                     totalBytes[i] = bytes[i - tmpLength];
                 }
                 while (totalLength >= 5) {
    
    
                     char *lengthBytes = new char[4];
                     for (int i = 0; i < 4; i++) {
    
    
                         lengthBytes[i] = totalBytes[i];
                     }
                     int contentLength = char4ToInt(lengthBytes) - 1;
                     char messageid = totalBytes[4];
                     int f = messageid & 0xFF;
                     if (totalLength < contentLength + 5) {
    
    
                         break;
                     }
                     char* contentBytes = new char[contentLength];
                     for (int i = 0; i < contentLength; i++) {
    
    
                         contentBytes[i] = totalBytes[i + 5];
                     }
                     Person peo;
                     peo.ParseFromArray(contentBytes, contentLength);
                     cout << "now " << peo.name() << "'position is (" << peo.x() << ", " << peo.y() << ")" << endl;
                     cout << "press 3 to change your postion" << endl;
                     cout << "press other keys not to change your positon" << endl;
                     cin >> f;
                     if (f == 3) {
    
    
                         cout << "please input your next position" << endl;
                         cin >> x >> y;
                         peo.set_x(x);
                         peo.set_y(y);
                         pack(f, peo);
                     }
                     int leftLength = totalLength - (5 + contentLength);
                     char* leftBytes = new char[totalLength];
                     for (int i = contentLength + 5; i < totalLength; i++) {
    
    
                         leftBytes[i - 5 - contentLength] = totalBytes[i];
                     }
                     totalBytes = new char(leftLength);
                     for (int i = 0; i < leftLength; i++) {
    
    
                         totalBytes[i] = leftBytes[i];
                     }
                     totalLength = leftLength;
                }
            }
         } else {
    
    
             cout << "your username or passwd is incorrect!!!" << endl;
         }
     }
}
int main(){
    
    
    sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(1234);
    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    while (true)
    {
    
    
      input();
    }
    close(sock);
    return 0;
}

g++ dbserver.cpp db.pb.cc -o dbserver `pkg-config --cflags --libs protobuf` -std=c++17
g++ dbclient.cpp db.pb.cc -o dbclient `pkg-config --cflags --libs protobuf` -std=c++17

之后就无敌了。

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/115049615