网络套接字(socket)

目录

1 预备知识

1.1 端口号

1.2 TCP/UDP

1.3 网络字节序

1.4 sockaddr

2 socket接口

3 UDP简单程序

3.1服务端

3.2 客户端

4 TCP简单程序

4.1服务端

4.2 客户端


1 预备知识

1.1 端口号

        在IP数据报头中有着源ip地址和目的ip地址,由此将两个主机联系在了一起。但是在计算机的世界中实际上进行的是进程间的通信,因此我们不仅将两个主机上联系在一起,还需要将两个主机上的进程联系起来。而端口号就是能唯一标识一台主机上的某个进程的标识数据,使用端口号+ip地址就可以实现上述两主机间跨网络的进程通信。

特点:

        端口号(port)是一个2字节16位的整数,属于传输层协议的内容。
        IP地址 + 端口号能够标识网络上的某一台主机的某一个进程。
        一个进程可以绑定多个端口号,但是一个端口号只能被一个进程占用。

1.2 TCP/UDP

        套接字一般使用常规传输层的接口,简单了解解一下两个传输层协议。

        TCP        ——    UDP

        可靠传输  ——    不可靠传输
        有连接      ——     无连接
        字节流      ——    数据报

1.3 网络字节序

        发送端将发送缓冲区的数据按内存地址由低到高发送。接收端同样在接受缓冲区由低到高地址的方式接收。

        TCP/IP协议规定发送到网络数据中采用大端字节序,如果是小端机在发送或接收时要进行大小端的转换。

        可调用库函数进行主机字节序和网络字节序的转换。

#include <arpa/inet.h>
unit32_t htonl(unit32_t hostlong);
unit16_t htons(unit16_t hostshort);
unit32_t ntohl(unit32_t netlong);
unit16_t ntohs(unit16_t netshort);

1.4 sockaddr

        socket api的接口是sockaddr, 但是在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。

 

         in_addr 表示ipv4 的IP地址

2 socket接口

// 创建 socket 
int socket(int domain, int type, int protocol);
//返回文件描述符 (种类,类型,协议(固定0))
// 绑定端口号 
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (backlog先固定填5)
int listen(int socket, int backlog);
// 接收请求 
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

3 UDP简单程序

3.1服务端

        创建套接字>>绑定>>提供服务。

#include <iostream>
#include <sys/types.h>
#include <cstdio>
#include <cerrno>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
void Usage(std::string port)
{
    std::cout << "Usage:\n\t"
              << port << " port" << std::endl;
}
int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    const uint16_t port = atoi(argv[1]);
    // 1.创建套接字
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        std::cerr << "socket create fail:" << errno << std::endl;
        return 1;
    }
    // 2.绑定套接字

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    // 云服务器不允许用户直接bind,云服务器有多个ip
    // local.sin_addr.s_addr = inet_addr("127.0.0.1");
    local.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
        std::cerr << "socket bind fail:" << errno << std::endl;
        return 2;
    }
    // 3.提供服务
    bool quit = false;
    char buffer[1024];
    while (!quit)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, 
                                (struct sockaddr *)&peer, &len);
        std::cout << "client# " << buffer << std::endl;
        if (cnt > 0)
        {
            buffer[cnt] = 0;
            FILE* fp = popen(buffer,"r");
            std::string echo_hello = buffer;
            char line[1024] = {0};
            while(fgets(line,sizeof(line),fp))
            {
                echo_hello += line;
            }
            fclose(fp);
            sendto(sock, echo_hello.c_str(), echo_hello.size(), 0, 
                                (struct sockaddr *)&peer, len);
        }
    }
    return 0;
}

3.2 客户端

        客户端不需要显示绑定端口,创建套接字后直接对服务端请求服务。

#include <iostream>
#include <sys/types.h>
#include <cerrno>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <string.h>
#include <netinet/in.h>
void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << "server_ip server_port" << std::endl;
}
//./udp_client server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }

    // 1.创建套接字
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        std::cerr << "socket create fail:" << errno << std::endl;
        return 1;
    }
    //你要给谁发
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);
    //客户端使用随机端口,由操作系统自动绑定
    // 2.使用服务
    while (1)
    {
        // // 输入数据
        // std::string message;
        // std::cout << "输入#";
        // std::cin >> message;
        std::cout << "MyShell $";
        char line[1024];
        fgets(line, sizeof(line), stdin);

        sendto(sock, line, strlen(line), 0, (struct sockaddr *)&server, sizeof(server));

        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        char buffer[1024];
        ssize_t cnt = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&tmp, &len);
        if (cnt > 0)
        {
            buffer[cnt] = 0;
            std::cout << "server echo#" << buffer << std::endl;
        }
    }
    return 0;
}

4 TCP简单程序

4.1服务端

        创建套接字>>绑定>>监听>>创建线程提供服务>>每个线程关闭自己的文件描述符。

#include <iostream>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
void Usage(std::string proc)
{
    std::cout << proc<< " port" << std::endl;
}
void Service(int new_sock)
{
    // 提供服务
    while (true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << "client#" << buffer << std::endl;
            std::string echo_string = "server echo#";
            echo_string += buffer;
            write(new_sock, echo_string.c_str(), echo_string.size());
        }
        else if (s == 0)
        {
            std::cout << "client quit ..." << std::endl;
            break;
        }
        else
        {
            std::cerr << "read fail" << errno << std::endl;
            break;
        }
    }
}
void* Handler(void* args)
{
    pthread_detach(pthread_self());
    int sock = *(int*)args;
    delete (int*)args;
    Service(sock);
    close(sock);
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        std::cerr << "socket create:" << errno << std::endl;
        return 2;
    }
    // 2 bind
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
        std::cerr << "bind fail" << std::endl;
        return 3;
    }

    // 3 监听
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        std::cerr << "listen fail:" << errno << std::endl;
        return 4;
    }
    signal(SIGCHLD, SIG_IGN); // Linux中父进程忽略子进程的SIGCHLD信号,子进程自动退出释放资源
    while (true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock < 0)
            continue;
        //得到连接的客户信息
        uint16_t cli_port = ntohs(peer.sin_port);
        std::string cli_ip = inet_ntoa(peer.sin_addr);

        std::cout << "get a new link ->" << cli_ip << ":" << cli_port << ":" << new_sock << std::endl;

        //多线程版
        pthread_t tid;
        int* pram = new int(new_sock);
        pthread_create(&tid,nullptr,Handler,pram);


        // //多进程版
        // pid_t id = fork();
        // if (id < 0)
        //     continue;
        // else if (id == 0)
        // {
        //     // child
        //     close(listen_sock);
        //     Service(new_sock);
        //     close(new_sock); //防止文件描述符泄露
        //     exit(0);
        // }
        // else
        // {
        //     // father do nothing
        //     close(new_sock);
        // }
        // //单进程版
        // Service(new_sock);
    }
    return 0;
}

4.2 客户端

        创建套接字后连接服务器,即可请求服务。

#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << "server_ip server_port" << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string svr_ip = argv[1];
    uint16_t svr_port = atoi(argv[2]);

    // 1 创建socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        std::cerr << "socket creat fail" << std::endl;
        return 2;
    }
    // 2 client不用显示bind、listen、accept
    // 获取自身信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(svr_ip.c_str());
    server.sin_port = htons(svr_port);
    // connect
    if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
    {
        std::cerr << "socket connect fail" << std::endl;
        return 2;
    }
    std::cout<<"connect success"<<std::endl;

    //进行服务请求
    while (true)
    {
        std::cout << "please enter:";
        char buffer[1024];
        fgets(buffer, sizeof(buffer) - 1, stdin);
        write(sock, buffer, strlen(buffer));
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << buffer << std::endl;
        }
    }
    return 0;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值