初识Socket编程——基于流式套接字的服务器回射程序设计

实验二、基于流式套接字的服务器回射程序设计

0x00 实验内容

编写一服务器程序和客户程序,要求客户每输入一行数据,服务器接收后回送给客户程序,当客户输入“q”后退出。过程描述如下图所示:

UserTCP ClientTCP ServerInput Datasend - recvrecv - sendOutput DataUserTCP ClientTCP Server

0x01 实现过程

公共函数

// comm.h
#ifndef NETWORK1_2_1_A_COMM_H
#define NETWORK1_2_1_A_COMM_H
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <inaddr.h>
#include <time.h>
#include <string.h>

using namespace std;

#pragma comment(lib, "ws2_32.lib")

#define MAXLINE 4096     // 接收缓冲区长度
#define LISTENQ 1024     // 监听队列长度
#define SEVER_PORT 13131 // 端口

//初始化Windows Sockets DLL,协议版本号
int startUp()
{
    WORD w_version = MAKEWORD(2, 2);
    WSADATA wsa_data;
    int res = WSAStartup(w_version, &wsa_data);
    if (res != 0)
    {
        cout << WSAGetLastError() << "WSAStartup Error!" << endl;
        return -1;
    }
    if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
    {
        cout << WSAGetLastError() << "Version Error!" << endl;
        WSACleanup();
        return -1;
    }

    return 0; //WSAStartup()成功
}

// 释放WinSock Dll
int cleanUp()
{
    int res = WSACleanup();
    if (res == SOCKET_ERROR)
    {
        cout << WSAGetLastError() << "WSACleanup Error!" << endl;
        return -1;
    }
    return 0;
}

// 断开连接并释放WinSock Dll
int closeConn(SOCKET sock_conn)
{
    int res = closesocket(sock_conn); //关闭连接
    if (res == SOCKET_ERROR)
    {
        cout << WSAGetLastError() << "Close Socket Error!" << endl;
        return -1;
    }
    res = cleanUp();
    return res;
}

SOCKET tcpServerInit(u_short port)
{
    int res = -1;

    // 设置地址
    sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    //创建流式套接字
    SOCKET sock_listen = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_listen == INVALID_SOCKET)
    {
        cout << WSAGetLastError() << "Socket Error!" << endl;
        WSACleanup();
        return -1;
    }

    //绑定服务器地址
    res = bind(sock_listen, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (res == SOCKET_ERROR)
    {
        cout << WSAGetLastError() << "Bind Addr Error!" << endl;
        closeConn(sock_listen);
        return -1;
    }

    //设置服务器为监听状态 最长长度LISTENQ
    res = listen(sock_listen, LISTENQ);
    if (res == SOCKET_ERROR)
    {
        cout << WSAGetLastError() << "Listen Error!" << endl;
        closeConn(sock_listen);
        return -1;
    }

    return sock_listen;
}

int tcpEchoServer(SOCKET sock_conn)
{
    int res;
    char recv_data[MAXLINE + 1];

    do
    {
        // 清空接收缓存
        memset(recv_data, 0, MAXLINE);

        // 接收数据
        res = recv(sock_conn, recv_data, MAXLINE, 0);
        if (res > 0)
        {
            cout << "The Data Received From Client Is:" << recv_data << endl;
            // 回射接收的数据
            res = send(sock_conn, recv_data, res, 0);
            if (res == SOCKET_ERROR)
            {
                cout << WSAGetLastError() << "Send Error!" << endl;
                res = -1;
            }
            else
            {
                cout << "The Data Sent From Server Is:" << recv_data << endl;
            }
        }
        else
        {
            if (res == 0)
            {
                cout << "Client is closed!" << endl;
            }
            else
            {
                cout << WSAGetLastError() << "Recv Error!" << endl;
                res = -1;
            }
            break;
        }
    } while (res > 0);

    return res;
}

SOCKET tcpClientInit(char *server_ip, u_short port)
{
    int res;
    SOCKET sock_conn;

    // 设置服务器地址
    sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.S_un.S_addr = inet_addr(server_ip);

    //创建流式套接字
    sock_conn = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_conn == INVALID_SOCKET)
    {
        cout << WSAGetLastError() << "Socket Error!" << endl;
        cleanUp();
        return -1;
    }

    // 请求连接服务器
    res = connect(sock_conn, (sockaddr *)&server_addr, sizeof(server_addr));
    if (res == SOCKET_ERROR)
    {
        cout << WSAGetLastError() << "Connect Error!" << endl;
        closeConn(sock_conn);
        return -1;
    }

    return sock_conn; //连接成功
}

int tcpEchoClient(SOCKET sock_conn)
{
    int res = 0;
    char send_data[MAXLINE], recv_data[MAXLINE];
    memset(send_data, 0, MAXLINE);
    memset(recv_data, 0, MAXLINE);

    while (cin.getline(send_data, MAXLINE))
    {
        if (*send_data == 'Q' || *send_data == 'q')
        {
            cout << "Input End!" << endl;

            // 关闭发送
            res = shutdown(sock_conn, SD_SEND);
            if (res == SOCKET_ERROR)
            {
                cout << WSAGetLastError() << "Shutdown Error!" << endl;
            }
            return 0;
        }

        // 发送数据
        res = send(sock_conn, send_data, (int)strlen(send_data), 0);
        if (res == SOCKET_ERROR)
        {
            cout << WSAGetLastError() << "Send Data Error!" << endl;
            return -1;
        }

        cout << "The Data Sent From Client Is:" << send_data << endl;

        // 接收数据 -- 阻塞模式接收
        res = recv(sock_conn, recv_data, MAXLINE, 0);
        if (res > 0)
        {
            cout << "The Data Received From Server Is:" << recv_data << endl;
        }
        else
        {

            if (res == 0)
            {
                cout << "Server is closed!" << endl;
            }
            else
            {
                cout << WSAGetLastError() << "Recv Error!" << endl;
                res = -1;
            }
            break;
        }

        // 清空缓存
        memset(recv_data, 0, MAXLINE);
        memset(send_data, 0, MAXLINE);
    }
    return res;
}

#endif //NETWORK1_2_1_A_COMM_H

Server 端

// server.cpp
#include "comm.h"

int main(int argc, char *argv[])
{
    int res = -1;
    char buff[MAXLINE]; //缓冲区
    SOCKET sock_conn;
    SOCKET sock_listen;

    // 启动
    res = startUp();
    if (res == -1) return -1;

    //监听
    sock_listen = tcpServerInit(SEVER_PORT);
    if (sock_listen == -1) return -1;

    cout << "Server Start!" << endl;

    // 迭代服务器 -- 循环监听用户的连接请求
    while (true)
    {
        //接受客户端连接请求  建立连接
        sock_conn = accept(sock_listen, nullptr, nullptr);

        if (sock_conn != INVALID_SOCKET)
        {
            cout << "Conn Success!" << endl;

            //处理链接
            res = tcpEchoServer(sock_conn);
            if (res == -1)
            {
                cout << "Disconnected or Sth. Error" << endl;
            }
        }
        else
        {
            cout << WSAGetLastError() << "Accept Error!" << endl;
            closeConn(sock_listen);
            return -1;
        }

        if (closesocket(sock_conn) == SOCKET_ERROR)
        {
            cout << WSAGetLastError() << "Close Error!" << endl;
        }
    }

    // 关闭连接
    closeConn(sock_listen);
    return 0;
}

Client 端

// client.cpp
#include "comm.h"

int main()
{
    int res = -1;
    SOCKET sock_conn;
    char server_ip[] = "127.0.0.1";

    // 启动
    res = startUp();
    if (res == -1) return -1;

    //连接
    sock_conn = tcpClientInit(server_ip, SEVER_PORT);
    if (sock_conn == -1) return -1;

    cout << "Client Start!" << endl; // 连接服务器成功
    res = tcpEchoClient(sock_conn);  //处理连接
    closeConn(sock_conn);            //关闭连接

    return res;
}

0x02 一些说明

  1. 服务器是一次接收一次发送的模式,说实话,这种模式不是太好;当服务器没有返回数据或者返回时丢包的情况下客户端会卡死;因此,最好阻塞模式情况下都设置超时时间,以避免无限期的空等。
  2. 同理,服务器端也是,最好设置超时时间。
  3. 有一些注释在上一篇博文《初识Socket编程——基于流式套接字的时间同步服务器设计》中出现过,建议对比着看。
发布了46 篇原创文章 · 获赞 53 · 访问量 8万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览