随笔小屋 Logo
首页
瞬间
反馈
随笔小屋 Logo
首页 瞬间 反馈
  1. 首页
  2. linux
  3. tcp

tcp

  • linux
  • 发布于 2025-11-01
  • 287 次阅读
flor
flor

创建套接字

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建套接字
参数:domain  通信域,协议族
    PF_LOCAL/PF_UNIX  本地套接字,进程间通信
    PF_INET           基于IPv4的网络通信
    PF_INET6          基于IPv6的网络通信
    PF_PACKET         基于底层包的网络通信
参数:type  套接字类型
    SOCK_STREAM  流式套接字,基于TCP协议
    SOCK_DGRAM   数据报套接字,基于UDP协议
    SOCK_RAW     原始套接字,工作在传输层以下
protocol:特殊协议,对于流式和数据报套接字而言,只能取0
返回值:成功返回表示套接字对象的文件描述符,失败返回-1

基本地址结构,本身没有实际意义,仅用于泛型化参数
struct sockaddr {
  sa_family_t sa_family;  地址族
  char sa_data[14];       地址值
};
本地地址结构,用于AF_LOCAL/AF_UNIX域的本地通信
struct sockaddr_un {
  sa_family_t sun_family;  地址族(AF_LOCAL/AF_UNIX)
  char sun_path[];         本地套接字文件的路径
};
网络地址结构,用于AF_INET域的IPv4网络通信
struct sockaddr_in {
  sa_family_t sin_family;   地址族(AF_INET)
  in_port_t sin_port;       端口号(0~65535)  unsigned short
  struct in_addr sin_addr;  IP地址  unsigned int
};
网络地址结构,用于AF_INET域的IPv4网络通信
struct in_addr {
  in_addr_t s_addr;
};
typedef uint16_t in_port_t; 无符号16位整数
typedef uint32_t in_addr_t; 无符号32位整数

include <sys/socket.h>
int bind(int sockfd, struct sockaddr const* addr, socklen_t addrlen);
功能:将套接字和本机的地址结构绑定在一起
参数:sockfd  套接字描述符
    addr     自己的地址结构
    addrlen  地址结构的字节数
返回值:成功返回0,失败返回-1

include <sys/socket.h>
int connect(int sockfd, struct sockaddr const* addr, socklen_t addrlen);
功能:将套接字和对方的地址结构连接在一起
参数:sockfd:套接字描述符
    addr  对方的地址结构
    addrlen  地址结构的字节数
返回值:成功返回0,失败返回-1

include <sys/socket.h>
int listen(int sockfd, int backlog)
功能:启动侦听,在指定套接字上启动对连接请求的侦听功能
参数:sockfd  套接字描述符,在调用此函数之前是一个主动套接字,是不能感知连接请求的,在调用此函数并成功返回之后,是一个被动套接字,具有感知连接请求的能力
    backlog  未决连接请求队列的最大长度,一般取不小于1024的值
返回值:成功返回0,失败返回-1

include <sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
功能:等待并接受连接请求,在指定套接字上阻塞,直到连接建立完成
参数:sockfd  侦听套接字描述符
    addr:输出连接请求发起方的地址信息
    addrlen:输出连接请求发起方的地址信息字节数
返回值:成功返回可用于后续通信的连接套接字描述符,失败返回-1

include <sys/socket.h>
ssize_t recv(int sockfd, void* buf, size_t count, int flags);
功能:接收数据
参数:若flags取0则与read函数完全等价
    MSG_DONTWAIT 以非阻塞方式接收数据
    MSG_OOB      接收带外数据
    MSG_WAITALL  等待所有数据,即不接收到count字节就不返回
返回值:成功返回实际接收到的字节数,失败返回-1

include <sys/socket.h>
ssize_t send(int sockfd, void const* buf, size_t count, int flags);
功能:发送数据
参数:若flags取0则与write函数完全等价
    MSG_DONTWAIT  以非阻塞方式接收数据
    MSG_OOB       接收带外数据
    MSG_DONTROUTE 不查路由表,直接在本地网络中寻找目的主机
返回值:成功返回实际发送的字节数,失败返回-1

字节序转换

uint32_t htonl(uint32_t hostlong);//长整形主机字节序到网络字节序
uint32_t ntohl(uint32_t netllong);//长整形网络字节序到主机字节序
uint16_t htons(uint16_t hostshort);//短整形主机字节序到网络字节序
uint16_t ntohs(uint16_t netshort);//短整形网络字节序到主机字节序
in_addr_t inet_addr(char const* ip);
点分十进制字符串地址 --》网络字节序形式整数地址
int inet_aton(char const* ip, struct in_addr* nip);
点分十进制字符串地址 --》网络字节序形式整数地址
char* inet_ntoa(struct in_addr nip);
网络字节序形式整数地址 --》点分十进制字符串地址
步骤 服务器描述 服务器函数 客户机函数 客户机描述 步骤
1 创建套接字 socket socket 创建套接字 1
2 准备地址结构 sockaddr_in sockaddr_in 准备地址结构 2
3 绑定地址 bind — — —
4 启动侦听 listen — — —
5 等待连接 accept connect 请求连接 3
6 接收请求 recv send 发送请求 4
7 发送响应 send recv 接收响应 5
8 关闭套接字 close close 关闭套接字 6
//基于tcp协议的服务器
#include<stdio.h>
#include<string.h>
#include<ctype.h>// toupper()
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<errno.h>
//信号处理函数,负责收尸
void sigchild(int signum){  
    while (1){
        pid_t pid = waitpid(-1, NULL, WNOHANG);
        if(pid == -1){
            if(errno == ECHILD){
                printf("没有子进程");
                break;
            }else{
                perror("waitpid");
                return;
            }    
        }else if (pid == 0){
            printf("子进程运行中");
            break;
        }else{
            printf("回收了%d\n",pid);
        }    
    }
};

int main(void){
    //17号信号捕获处理
    if(signal (SIGCHLD,sigchild)== SIG_ERR){
        perror("signal");
        return -1;
    };
    printf("服务器:创建套接字\n");
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1){
        perror("socket");
        return -1;
    }
    printf("服务器:组织地址结构\n");
    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(8981);//字节序
    ser.sin_addr.s_addr = inet_addr("192.168.102.251");
    printf("服务器:绑定套接字和地址结构\n");
    if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){
        perror("bind");
        return -1;
    }
    printf("服务器:启动侦听\n");
    if(listen(sockfd,1024) == -1){
        perror("listen");
        return -1;
    }
    
    for(;;){

        printf("服务器:等待客户端的连接\n");
        struct sockaddr_in cli;//用来输出客户端的地址结构
        socklen_t len = sizeof(cli);//用来输出地址结构大小
        int conn = accept(sockfd,(struct sockaddr*)&cli,&len);
        if(conn == -1){
            perror("accept");
            return -1;
        }
        printf("服务器:接收到%s:%hu的客户端的连接\n",
                inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
        printf("服务器:业务处理\n");
    
        //创建子进程,子进程负责和客户端通信
        pid_t pid = fork();
        if(pid == -1){
            perror("fork");
            return -1;
        }
        if(pid == 0){
            //关闭侦听套接字
            close (sockfd);
            for(;;){
                //接收客户端发来的小写的串
                char buf[64] = {};
                ssize_t size = read(conn,buf,sizeof(buf)-1);
                if(size == -1){
                    perror("read");
                    return -1;
                }
                if(size == 0){
                    //对方关闭套接字
                    break;
                }
                //转大写
                for(int i = 0;i < strlen(buf);i++){
                    buf[i] = toupper(buf[i]);
                }
                //回传给客户端
                if(write(conn,buf,strlen(buf)) == -1){
                    perror("write");
                    return -1;
                }
            }
            //通信结束,关闭通信套接字
            close (conn);
            return 0;
        }
        //父进程代码
        close(conn);
    }
    printf("服务器:关闭套接字\n");
    close(sockfd);
    return 0;
}
//基于tcp的客户端
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>

int main(void){
    printf("客户端:创建套接字\n");
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1){
        perror("socket");
        return -1;
    }
    printf("客户端:组织服务器的地址结构\n");
    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(8980);
    ser.sin_addr.s_addr = inet_addr("192.168.249.129");
    printf("客户端:发起连接请求\n");
    if(connect(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){
        perror("connect");
        return -1;
    }
    printf("客户端:业务处理\n");
    for(;;){
        //通过键盘获取小写的串
        char buf[64] = {};
        fgets(buf,sizeof(buf),stdin);
        //! 退出
        if(strcmp(buf,"!\n") == 0){
            break;
        }
        //发给服务器
        if(send(sockfd,buf,strlen(buf),0) == -1){
            perror("send");
            return -1;
        }
        //接收回传的大写的串
        if(recv(sockfd,buf,sizeof(buf)-1,0) == -1){
            perror("recv");
            return -1;
        }
        //显示
        printf("%s",buf);
    }
    printf("客户端:关闭套接字\n");
    close(sockfd);
    return 0;
}

湘ICP备2025147565号-1
gongan beian 湘公网安备43102602000213号
CPU --% | 内存 0.00G/0.00G (0%) | 网络 无活动网卡
服务器资源占用 更新时间 --:--:--