【Linux跬步积累】—— 网络编程套接字(二)

🌏博客主页:PH_modest的博客主页
🚩当前专栏:Linux跬步积累
💌其他专栏:
🔴 每日一题
🟡 C++跬步积累
🟢 C语言跬步积累
🌈座右铭:广积粮,缓称王!


一、TCP socket API

下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。

//创建socket文件描述符(TCP/UDP,客户端 + 服务器)
int socket(int domain,int type,int protocol);

//绑定端口号(TCP/UDP,服务器)
int bind(int socket,const struct sockaddr *address,socklen_t address_len);

//开始监听socket(TCP,服务器)
int listen(int socket,int backlog);

//接收请求(TCP,服务器)
int accept(int socket,struct sockaddr* address,socklen_t* address_len);

//建立连接(TCP,客户端)
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

二、TCP API 使用

1、服务端创建套接字

函数原型:

//创建socket文件描述符(TCP/UDP,客户端 + 服务器)
int socket(int domain,int type,int protocol);

使用示例:

//创建文件描述符
_listensock = socket(AF_INET,SOCK_STREAM,0);
if(_listensock < 0)
{
   
    std::cerr<<"socket error!"<<std::endl;
    exit(1);
}

2、服务端绑定

函数原型:

//绑定端口号(TCP/UDP,服务器)
int bind(int socket,const struct sockaddr *address,socklen_t address_len);

使用示例:

//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY;

if(bind(_listensock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
   
    std::cerr<<"bind error!"<<std::endl;
    exit(2);
}

3、服务端监听

函数原型:

//开始监听socket(TCP,服务器)
int listen(int socket,int backlog);

使用示例:

//监听
if(listen(_listensock,5) < 0)
{
   
    std::cerr<<"listen error!"<<std::endl;
    exit(3);
}

4、服务端获取连接

函数原型:

//接收请求(TCP,服务器)
int accept(int socket,struct sockaddr* address,socklen_t* address_len);

参数说明:

  • socket:特定的监听套接字,表示从这个套接字获取连接。
  • address:对端网络相关信息,包括协议家族、IP地址、端口号。
  • address_len:这是一个输入输出型参数,调用时传入期望读取的长度,返回时表示实际读取的长度。

accept函数返回的套接字是什么?和socket有什么区别?

  • accept函数获取连接时,是从socket监听套接字当中获取的,如果accept获取连接成功,此时就会返回接收到的套接字对应的文件描述符。

  • socket监听套接字的作用是用来获取客户端发来的新的连接请求。accept会不断从监听套接字当中获取新连接。

  • accept返回的套接字是为本次获取到的连接提供服务的。监听套接字是不断获取新的连接,真正为这些连接提供服务的是accept返回的套接字,而不是监听套接字。

  • 监听套接字可以看成饭店门口拉客的员工,当你被她说服进店之后,会有新的服务员单独为你提供服务,而这个新的服务员就是accept返回的套接字。

使用示例:

void Start()
{
   
    while(true)
    {
   
        struct sockaddr_in peer;
        memset(&peer,0,sizeof(peer));
        socklen_t len = sizeof(peer);
        int sockfd = accept(_listensock,(struct sockaddr*)&peer,&len);
        if(sockfd < 0)
        {
   
            std::cerr<<"accept error!"<<std::endl;
            continue;//这里不能直接退出,因为还需要获取其他连接
        }
        std::string client_ip = inet_ntoa(peer.sin_addr);//将网络序列转换为主机序列,同时将整数ip变为字符串ip
        int client_port = ntohs(peer.sin_port);//将网络序列转换为主机序列
        std::cout<<"get a new link-->"<<sockfd<<"["<<client_ip<<"]:"<<client_port<<std::endl;
    }
}

5、服务端接收连接测试

我们现在做一个简单的测试,测试一下当前服务器能否成功接受请求连接。

void Usage(char *proc)
{
   
    std::cout<<"Usage:\n\t"<<proc<<" local_port\n";
}

//./tcpserver port
int main(int argc,char *argv[])
{
   
    if(argc != 2)
    {
   
        Usage(argv[0]);
        exit(1);
    }
    int port = std::stoi(argv[1]);
    TcpServer* tsvr = new TcpServer(port);
    tsvr->InitServer();
    tsvr->Start();
    return 0;
}

我们编译运行之后,可以通过netstat命令来显示网络连接、路由表、接口统计等网络相关信息。


echo server

多进程版本

TcpServer.hpp

#pragma once
#include"TcpServer.hpp"
#include<iostream>
#include<string>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/wait.h>
#include"InetAddr.hpp"


static const int gbacklog = 15;

class TcpServer
{
   
public:
    TcpServer(uint16_t port):_port(port),_isrunning(false)
    {
   }
    void InitServer()
    {
   
        //1. 创建流式套接字
        _listensock = socket(AF_INET,SOCK_STREAM,0);
        if(_listensock < 0)
        {
   
            std::cerr<<"socket error!\n";
            exit(1);
        }
        std::cout<<"socket success,sockfd is:"<<_listensock<<std::endl;

        //2. bind
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = ::bind(_listensock,(struct sockaddr*)&local,sizeof(local));
        if(n < 0)
        {
   
            std::cerr<<"bind error!\n";
            exit(2);
        }
        std::cout<<"bind success,sockfd is:"<<_listensock<<std::endl;

        //3. tcp是面向连接的,所以通信之前,必须先建立连接,服务器是被连接的
        //   tcpserver启动,未来首先要一直等待客户的连接到来
        n = listen(_listensock,gbacklog);
        if(n < 0)
        {
   
            std::cerr<<"listen error!\n";
            exit(3);
        }
        std::cout<<"listen success,sockfd is:"<<_listensock<<std::endl;

    }

    void Service(int sockfd,InetAddr client)
    {
   
        printf("get a new link,info %s:%d,fd:%d\n",client.IP().c_str(),client.Port(),sockfd);
        std::string clientaddr = "["+client.IP()+":"+std::to_string(client.Port())+"]# ";
        while(true)
        {
   
            //读取数据
            char inbuffer[1024];
            ssize_t n = read(sockfd,inbuffer,sizeof(inbuffer)-1);
            if(n > 0)//回写
            {
   
                inbuffer[n] = 0;
                std::cout<<clientaddr<<inbuffer<<std::endl;

                std::string echo_string = "[server echo]# ";
                echo_string+=inbuffer;

                write(sockfd,echo_string.c_str(),echo_string.size());
            }
            else if(n == 0)//退出
            {
   
                //client 退出&&关闭链接了
                std::cout<<clientaddr<<" quit!\n";
                break;
            }
            else//报错
            {
   
                std::cerr<<clientaddr<<"read error!\n";
                break;
            }
        }
        //一定要关闭,因为文件描述符表是一个数组,数组容量是有限的
        close(sockfd);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PH_modest

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值