1.主机字节序和网络字节序

主机字节序: 不同的芯片,所采用的数值存储方式是不同:大端模式&小端模式
网络字节序: 统一使用大端模式来表示数据

2.字节序的转化
#include <netinet/in.h>
uint32_t ntohl (uint32_t __netlong); // 网络字节序转化为	主机字节序 long
uint16_t ntohs (uint16_t __netshort); // 网络字节序转化为主机字节序 short
uint32_t htonl (uint32_t __hostlong); // 主机字节序转化为网络字节序 long
uint16_t htons (uint16_t __hostshort); // 主机字节序转化	为网络字节序 short

2.套接字的地址结构

运行在两个不同主机上的进程想要通信: 必须知道 IP地址 端口号
// 通用的地址结构
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
// IPV4 专有的地址结构
struct sockaddr_in
{
sa_family_t sin_family; // 地址簇 AF_INET
uint16_t sin_port; // 端口号: 将主机字节序转化为网络字节序 0--1024 系统预留 1025 -- 4096 知名端口号 4097 --
65535
struct in_addr sin_addr;
};
struct in_addr
{
uint32_t s_addr; // IP地址 以字符串形式来表示一个点分十进制。 IP地址的转化
};

IP地址转化的方法

uint32_t inet_addr (const char *__cp); // 将点分十进制的字符串转化为uint32_t类型
char * inet_ntoa (struct in_addr __in); // 将struct in_addr类型的变量转化为char*字符串

3. TCP的网络接口

/*
创建socket套接字
返回值: 成功返回文件描述符 socket 失败返回-1
domain: 协议簇 AF_INET TCP/IP协议
type: 具体的协议 SOCK_STREAM --> tcp SOCK_DGRAM --> UDP
protocol: 在前两个值的协议基础下的一个具体协议,一般默认设置为0
*/
int socket (int __domain, int __type, int __protocol);
/*
命令(绑定)socket套接字
返回值: 成功返回0, 失败返回-1
fd: socket方法返回的套接字的文件描述符
addr: 服务器的地址结构变量的地址 需要类型强转
len: addr的长度
*/
int bind (int __fd, struct sockaddr * __addr, socklen_t __len);
/*
启动监听方法
返回值: 成功返回0, 失败返回-1
fd: socket方法返回的套接字的文件描述符
n: 内核创建的用于维护已完成连接的客户端的个数: n+1
*/
int listen (int __fd, int __n);
/*
获取一个连接
返回值: 成功返回描述这个连接的文件描述符, 失败返回-1
fd: socket创建的文件描述符
addr: 用于保存客户端的地址信息
addr_len: addr的长度
*/
int accept (int __fd, struct sockaddr * __addr, socklen_t *__addr_len);
/*
读取数据
fd: 需要读取数据的文件描述符
buf: 读取的数据存储的缓冲区的首地址
n: 一次能够读取的数据长度,单位是字节
flag: 标志,默认给0
*/
ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);
/*
发送数据
fd: 需要写入数据的文件描述符
buf: 写入的数据存储的缓冲区的首地址
n: 一次写入的真实数据长度,单位是字节
flag: 标志,默认给0
*/
ssize_t send (int __fd, const void *__buf, size_t __n, int __flags);
/*
发起连接的方法 -- 客户端程序使用
返回值: 成功返回0, 失败返回-1
fd: socket创建的文件描述符
addr: 服务器的地址信息
len: addr的长度
*/
int connect (int __fd, struct sockaddr * __addr, socklen_t __len);
// 关闭一个文件描述符
int close(int __fd);

4. TCP服务器端的编程流程

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<string.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>



int main()
{
//创建用于监听的套接字,这个套接字是一个文件描述符,用于检测有没有客户端发起一个新的连接
    int listenfd = socket(AF_INET,SOCK_STREAM,0);
    assert(listenfd != -1);
    
    struct sockaddr_in addr;
    memset(&addr,0,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port =htons(6000);//转化端口号
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");//回环地址

// 将得到的监听的文件描述符和本地的IP端口进行绑定
    int res = bind(listenfd,(struct sockaddr*)&addr,sizeof(addr));
    assert(res != -1);
    
//设置监听(成功之后开始监听,监听的是客户端的连接)
    res = listen(listenfd,5);
    assert(res != -1);

//通信
    while (1)
    {
        struct sockaddr_in cli_addr;
        socklen_t cli_len = sizeof(cli_addr);
        int c = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_len);
        if(c == -1)
        {
            printf("Get One Client Link Error\n");
            continue;
        }

        while (1)
        {
            char buff[128] = {0};
            int n = recv(c,buff,127,0);//读取数据放在buff中,一次读取127个
            if(n <= 0)
            {
                printf("Client will unlink\n");
                break;
            }
            printf("%d : %s\n",c,buff);
            send(c,"OK",2,0);
        }
        close(c);
    }
    
//断开连接,关闭套接字(四次挥手)
    close(listenfd);


    return 0;
}

5. TCP客户端的编程流程

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<string.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{

//创建一个通信的套接字,需要指定服务器的IP和端口号y
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);

    struct sockaddr_in ser_addr;
    memset(&ser_addr,0,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port =htons(6000);//转化端口号
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//回环地址


//连接服务器,需要知道服务器绑定的IP和端口
    int res = connect(sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
    assert(res != -1);

//通信
    while (1)
    {
        
        printf("input: ");
        char buff[128] = {0};
        fgets(buff,127,stdin);
        if(strncmp(buff,"end",3) == 0)
        {
            break;
        }

        send(sockfd,buff,strlen(buff) - 1,0);//\n不发

        memset(buff,0,128);
        recv(sockfd,buff,127,0);
        printf("%s\n",buff);
    }

//断开连接
    close(sockfd);
    
    return 0;
}

6.运行结果

在这里插入图片描述
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐