ZigZag Sin
登 陆
前面没有了 后面没有了

编写传统 Socket 程序

乔红
2020-11-25 21:03 阅读 2898

”阻塞“,”非阻塞“,”同步“,”异步“

”阻塞“,”非阻塞“,”同步“,”异步“。如果你写过 Socket 的网络程序,你一定在搜索资料的时候看到过这些名词。这些名词是什么意思?为什么在进行 Socket 编程的时候要引入这些概念?这些概念会带为我们的程序带来哪些优势?本期,我将会为大家一一讲解这些概念。相信学习完这些知识,你就能写出高效的 Socket 程序。

最传统的 Socket 程序

我们先来看一下最传统的 Socket 程序应该如何编写。以 TCP 为例,我们将写两个程序,一个程序作为 Server,一个程序作为 Client。

作为 TCP Server,程序流程如下。

Socket TCP

  • 1,调用 socket 函数创建一个 socket 的文件描述符。
  • 2,调用 bind 函数,将 socket 绑定到指定端口和指定 ip 上面去。
  • 3,调用 listen 函数。
  • 4,循环调用 accept 函数,accept会阻塞线程,直到有 Client 连接上来,accept 函数会返回代表与该 Client 建立的连接的文件描述符,之后与该 Client 收发数据,用这个文件描述符即可。
  • 5,完成所有任务后,挑出循环,调用 close 函数关闭文件描述符。

Server 端代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>

int main()
{
    int server_sock = socket(AF_INET, SOCK_STREAM, 0);
    if(server_sock < 0) {
        printf("socket() error");
        return -1;
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(1234);
    local.sin_addr.s_addr = inet_addr("0.0.0.0");
    socklen_t len = sizeof(local);
    if(bind(server_sock, (struct sockaddr*)&local ,len) < 0) {
        printf("bind() error");
        return -1;
    }

    if(listen(server_sock, 5) < 0) {
        printf("listen() error");
        return -1;
    }

    struct sockaddr_in remote;
    socklen_t socketLen = sizeof(struct sockaddr_in);

    while(1)
    {
        int sock = accept(server_sock, (struct sockaddr*)&remote, &socketLen);
        while(1){
            int dataLen = 32;
            unsigned char data[dataLen];

            int recvedDataLen = 0;
            while(recvedDataLen < dataLen){
                int ret = recv(sock, data + recvedDataLen, dataLen - recvedDataLen, 0);
                recvedDataLen += ret;
            }

            int sendedDataLen = 0;
            while(sendedDataLen < dataLen){
                int ret = send(sock, data + sendedDataLen, dataLen - sendedDataLen, 0);
                sendedDataLen += ret;
            }
        }
    }

    close(server_sock);

    return 0;
}

在这段代码中,当有客户端连接之后,我们的程序会进入这个部分。

int dataLen = 32;
unsigned char data[dataLen];

int recvedDataLen = 0;
while(recvedDataLen < dataLen){
    int ret = recv(sock, data + recvedDataLen, dataLen - recvedDataLen, 0);
    recvedDataLen += ret;
}

int sendedDataLen = 0;
while(sendedDataLen < dataLen){
    int ret = send(sock, data + sendedDataLen, dataLen - sendedDataLen, 0);
    sendedDataLen += ret;
}

这部分的代码中,我们的 Server 会先接收 32 个字节长度的数据,然后将这 32 个字节的数据再发回到 Client 端。

接下来,我们再来编写 Client 的代码

Client 端代码

作为 TCP Client,程序流程如下。

Socket TCP

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <time.h>
#include <chrono>
#include <thread>

int main()
{
    int SERVER_PORT = 1234;
    const char * SERVER_ADDR = "127.0.0.1";

    struct sockaddr_in serverAddr;
    bzero(&serverAddr, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    serverAddr.sin_port = htons(SERVER_PORT);

    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);

    int ret = connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (ret < 0) {
        printf("连接失败\n");
        close(clientSocket);
        return -1;
    }

    while(1){
        int dataLen = 32;
        unsigned char data[] = {
                'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                'A', 'B', 'C', 'D', 'E', 'F'
        };

        int sendedDataLen = 0;
        while(sendedDataLen < dataLen){
            int ret = send(clientSocket, data + sendedDataLen, dataLen - sendedDataLen, 0);
            printf("ret: %d\n", ret);
            sendedDataLen += ret;
        }

        int recvedDataLen = 0;
        while(recvedDataLen < dataLen){
            int ret = recv(clientSocket, data + recvedDataLen, dataLen - recvedDataLen, 0);
            printf("ret: %d\n", ret);
            recvedDataLen += ret;
        }

        for(int i=0;i<dataLen;i++){
            printf("%c", data[i]);
        }
        printf("\n");

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}

在这端段程序中,我们让 Client 去连接 Server 端,连接成功之后,会先发送 32 字节的数据出去,然后再尝试接收 32 个字节的数据,之后休眠 1 秒,再循环发送 32 字节数据。这正好和服务端的程序相互配合。

代码仓库

前面没有了 后面没有了
给我买个键盘吧。。。求打赏。。。
欢迎加群,一起交流~~~