socket笔记2

2.10 网络报文的数据格式定义及使用

网络报文包含两个部分,头和体。
服务端


//  Created by Surser on 2020/1/17.
//  Copyright © 2020 Surser. All rights reserved.
//
#ifdef _WIN32
    #include<windows.h>
    #include<WinSock2.h>
#else
    #include <unistd.h>
    #include<arpa/inet.h>
    #include<string.h>

    #define SOCKET int
    #define INVALID_SOCKET (SOCKET) (~0)
    #define SOCKET_ERROR (-1)
#endif

#include <stdio.h>
#include<thread>
#include<iostream>
using namespace std;

enum CMD
{
    CMD_LOGIN,
    CMD_LOGOUT,
    CMD_ERROR
};

struct DataHeader
{
    short dataLength;
    short cmd;
};
//DataPacket
struct Login
{
    char userName[32];
    char PassWord[32];
};
struct LoginResult
{
    int result;
};
struct Logout
{
    char userName[32];
};
struct LogoutResult{
    int result;
};

int main(int argc, const char * argv[]) {
//
    //1. 建立一个socket 三个参数变量,1 ip编码方式 ipv4;2 流式,面向字节流的;3 TCP;
    SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    //2. bind 绑定用于接受客户端连接的端口
    sockaddr_in _sin = {};
    _sin.sin_family = AF_INET;
    _sin.sin_port = htons(4567); //网络和本机字节序编码不同,需要转换,一个是大端编码,一个是小端编码
    _sin.sin_addr.s_addr = INADDR_ANY; // 本机ip地址
    if(SOCKET_ERROR == ::bind(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in))){
        cout<<"ERROR!绑定用于接受客户端连接的端口失败!\n";
    }
    else{
        cout<<"绑定用于接受客户端连接的端口成功!\n";
    }
    
    //3. listen 监听网络端口
    if(SOCKET_ERROR == listen(_sock, 5)){
        cout<<"ERROR!监听网络端口失败!\n";
    }
    else{
        cout<<"监听网络端口成功!\n";
    }
    //4. accept 等待接受客户端连接
    sockaddr_in clientAddr = {};
    socklen_t nAddrLen = sizeof(sockaddr_in);
    SOCKET _cSock = INVALID_SOCKET;
    
    _cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);
    if(INVALID_SOCKET == _cSock){
        cout<<"ERROR!接收到无效客户端SOCKET!\n";
    }
    cout<<"新客户端加入:socket = "<<(int)_cSock<<"IP="<< inet_ntoa(clientAddr.sin_addr)<<"\n";
    
    while(true)
    {
        DataHeader header = {};
        //5. 接受客户端请求
        int nLen = recv(_cSock, (char *)&header, sizeof(header), 0);
        if(nLen<=0){
            cout<<"客户端已退出,任务结束。\n";
            break;
        }
        cout<<"收到命令:"<<header.cmd<<"数据长度:"<<header.dataLength<<"\n";
        switch (header.cmd) {
            case CMD_LOGIN:
            {
                Login login = {};
                recv(_cSock, (char *)&login, sizeof(login), 0);
                //忽略判断用户密码是否正确的过程
                LoginResult ret = {1};
                send(_cSock, (char*)&header, sizeof(header), 0);
                send(_cSock, (char*)&ret, sizeof(ret), 0);
            }
            break;
            case CMD_LOGOUT:
            {
                Logout logout = {};
                 recv(_cSock, (char *)&logout, sizeof(logout), 0);
                //忽略判断用户密码是否正确的过程
                LogoutResult ret = {1};
                send(_cSock, (char*)&header, sizeof(header), 0);
                send(_cSock, (char*)&ret, sizeof(ret), 0);
            }
            break;
            default:
                header.cmd = CMD_ERROR;
                header.dataLength = 0;
                send(_cSock, (char*)&header, sizeof(header), 0);
            break;
        }
    }
    //8. 关闭套接字closesocket
    close(_sock);
//
    cout<<"服务器已退出。\n";
    getchar();
    return 0;
}
``

客户端:

```cpp
//
//  main.cpp
//  hellocpp
//
//  Created by Surser on 2020/1/17.
//  Copyright © 2020 Surser. All rights reserved.
//
#ifdef _WIN32
    #include<windows.h>
    #include<WinSock2.h>
#else
    #include <unistd.h>
    #include<arpa/inet.h>
    #include<string.h>

    #define SOCKET int
    #define INVALID_SOCKET (SOCKET) (~0)
    #define SOCKET_ERROR (-1)
#endif

#include <stdio.h>
#include<thread>
#include<iostream>
using namespace std;

enum CMD
{
    CMD_LOGIN,
    CMD_LOGOUT,
    CMD_ERROR
};

struct DataHeader
{
    short dataLength;
    short cmd;
};
//DataPacket
struct Login
{
    char userName[32];
    char PassWord[32];
};
struct LoginResult
{
    int result;
};
struct Logout
{
    char userName[32];
};
struct LogoutResult{
    int result;
};

int main(int argc, const char * argv[]) {
//    1.建立一个socket
    SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
    if(INVALID_SOCKET == _sock){
        cout<<"错误!建立套接字失败\n";
    }
    else{
        cout<<"建立套接字成功\n";
    }
//    2.连接服务器connect
    sockaddr_in _sin = {};
    _sin.sin_family = AF_INET;
    _sin.sin_port = htons(4567);
    _sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = connect(_sock, (sockaddr *) &_sin, sizeof(sockaddr_in));
    if(SOCKET_ERROR == ret){
        cout<<"错误!连接服务器失败\n";
    }
    else{
        cout<<"连接服务器成功\n";
    }
    
    while (true)
    {
        // 3.输入请求命令
        char cmdBuf[128]={};
        cin>>cmdBuf;
        // 4.处理请求命令
        if(0 == strcmp(cmdBuf, "exit"))
        {
            cout<<"收到exit,任务结束。\n";
            break;
        }
        else if( 0 == strcmp(cmdBuf, "login"))
        {
            Login login = {"fjt","fjtmm"};
            DataHeader dh = {sizeof(login), CMD_LOGIN};
            //5.向服务器发送请求命令
            send(_sock, (const char*)&dh, sizeof(dh), 0);
            send(_sock, (const char*)&login, sizeof(login), 0);
            // 接受服务器返回的数据
            DataHeader retHeader = {};
            LoginResult loginRet = {};
            recv(_sock, (char*) &retHeader, sizeof(retHeader), 0);
            recv(_sock, (char*) &loginRet, sizeof(loginRet), 0);
            cout<<"LoginResult:"<<loginRet.result;
        }
        else if( 0 == strcmp(cmdBuf, "logout"))
        {
            Logout logout = {"fjt"};
            DataHeader dh = {sizeof(logout), CMD_LOGOUT};
            send(_sock, (const char*)&dh, sizeof(dh), 0);
            send(_sock, (const char*)&logout, sizeof(logout), 0);
            // 接受服务器返回的数据
            DataHeader retHeader = {};
            LoginResult logoutRet = {};
            recv(_sock, (char*) &retHeader, sizeof(retHeader), 0);
            recv(_sock, (char*) &logoutRet, sizeof(logoutRet), 0);
            cout<<"LogoutRet:"<<logoutRet.result;
        }
        else {
            cout<< "不支持命令,请重新输入\n";
        }
    }
//    7.关闭套接字close
    close(_sock);
    cout<<"客户端已退出。\n";
    getchar();
    return 0;
}

2.15 将多次收发报文数据升级为一次收发

  • update: 之前报文数据分为头部和身体两个部分进行收发,容易出次。这里升级为一次收发,另身体部分公有继承头部。

https://github.com/surserrr/learn_socket/tree/4d6b4fdf9876332cf5b8bd1912a64fffb45357f7

发布了61 篇原创文章 · 获赞 15 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/surserrr/article/details/104433285