网络编程 day02

6. 字节序

端口转换

主机字节序转换为网络字节序 (小端序->大端序)

#include <arpa/inet.h>
u_long htonl (u_long hostlong);	// host to net long
u_short htons (u_short short);	// host to net short

网络字节序转换为主机字节序(大端序->小端序)

u_long ntohl (u_long hostlong);
u_short ntohs (u_short short);

IP转换

主机字节序转换为网络字节序 (小端序->大端序)

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t  inet_addr(const char *strptr);  //该参数是字符串
typedef uint32_t in_addr_t;

功能: 主机字节序转为网络字节序
参数: const char *strptr: 字符串
返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示),
否则NULL

网络字节序转换为主机字节序(大端序->小端序)

char *inet_ntoa(stuct in_addr inaddr);

功能: 将网络字节序二进制地址转换成主机字节序。
参数: stuct in_addr in addr: 只需传入一个结构体变量
返回值: 返回一个字符指针, 否则NULL;
注意:

struct in_addr
{
    in_addr_t s_addr;
};

7. TCP编程

C/S:client/server 客户端与服务器
B/S:browser/server 浏览器与服务器

1. 流程

在这里插入图片描述
服务器

  1. 创建套接字——socket()
  2. 指定网络信息
  3. 绑定套接字——bind()
  4. 监听套接字——listen()
  5. 接收客户端连接请求——accept()
  6. 创建新的套接字——socket()
  7. 接收/发送消息——recv()/send()
  8. 关闭套接字——close()

客户端

  1. 创建套接字——socket()
  2. 指定服务器网络信息
  3. 连接请求——connect()
  4. 接收/发送消息——recv()/send()
  5. 关闭套接字——close()

2. 函数接口

socket

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

功能:创建套接字
参数:
int domain协议族
int type套接字类型
int protocol协议
返回值:成功返回套接字文件描述符,失败返回-1,更新errno
补充:

  1. int domain协议族
宏名含义
AF_UNIX, AF_LOCAL本地通信
AF_INETipv4
AF_INET6ipv6
  1. int type套接字类型
宏名含义
SOCK_STREAM流式套接字
SOCK_DGRAM数据报套接字
SOCK_RAW原始套接字
  1. int protocol协议:0 自动匹配底层

bind

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能:绑定套接字
参数:
int sockfd:套接字
const struct sockaddr *addr:用于通信结构体,需要强转
socklen_t addrlen:结构体大小
返回值:成返回0,失败返回-1,更新errno
补充:

struct sockaddr_in 
{
    sa_family_t    sin_family;		// ipv4协议
    in_port_t      sin_port;  		// 端口号
    struct in_addr sin_addr;  		// IP地址
};
struct in_addr 
{
    uint32_t       s_addr;    		// IP地址
};

listen

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);

功能:监听套接字,将主动套接字变为被动套接字
参数:
int sockfd:连接套接字的文件描述符
int backlog:同时响应客户端请求链接的最大个数
返回值:成功 0 失败-1,更新errno
补充:
参数:int backlog:不能写0,一般写6
不同平台可同时链接的数不同
两个队列:
 队列1:保存正在连接
 队列2,连接上的客户端

accept

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能:阻塞等待客户端的连接请求
参数:
int sockfd:链接套接字
struct sockaddr *addr: 客户端网络信息结构体,需要强转
socklen_t *addrlen:客户端网络信息结构体的大小
返回值: 成功:通信套接字的文件描述符,失败:-1,更新errno
补充:

  1. 如果不需要关心具体是哪一个客户端,那么可以填NULL
  2. 获取的ip和端口号需要转换大小端

recv

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

功能:接收数据
参数:
int sockfd:用于通信的套接字文件描述符
void *buf:存放通信内容
size_t len: 获取的内容的大小
int flags
  一般填0,等价于read
   MSG_DONTWAIT 非阻塞
返回值:
 成功:接收的字节个数,为0时表示客户端退出
 失败:< 0,更新errno

connect

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:用于连接服务器
参数:
int sockfd:通信用的套接字
const struct sockaddr *addr:服务器网络信息
socklen_t addrlen:结构体的大小
返回值:成功返回0,失败返回-1,更新errno

send

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

功能:发送数据
参数:
int sockfd通信用的套接字
const void *buf存放通信内容
size_t len发送的内容的大小
int flags一般填0,等价于write

4. 通信代码优化过程

  1. 优化服务器代码,客户端链接成功后,可以循环多次通信,当客户端输入quit时,客户端退出。
  2. 优化服务器代码客户端输入quit退出后,服务器不退出,等待下一个客户端连接
     循环服务器:一个服务器可以连接多个客户端,但是不能同时
  3. 地址和端口都通过参数传入
  4. 自动获取本机地址
  5. 增加来电显示功能:显示客户端连入的地址和端口

5. 最终通信代码

服务器

int main(int argc, char const *argv[])
{
    // 变量定义
    int fd_socket = -1;       // 连接套接字文件描述符
    struct sockaddr_in saddr; // 服务器网络信息
    struct sockaddr_in caddr; // 客户端网络信息
    int acc = -1;             // 通信套接字的文件描述符
    int len = sizeof(caddr);  // 客户端网络信息结构体大小
    char buf[128] = {};       // 存放收发的内容
    int ret = -1;             // 返回值

    // 1. 创建套接字
    fd_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (fd_socket < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("fd_socket : %d\n", fd_socket);

    // 2. 指定网络信息
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 3. 绑定套接字
    if (bind(fd_socket, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind ok\n");

    // 4. 监听套接字
    // 将主动套接字变成被动套接字
    // 队列1:未连接
    // 队列2:已连接
    if (listen(fd_socket, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen ok\n");

    while (1)
    {
        // 接收客户端连接请求
        acc = accept(fd_socket, (struct sockaddr *)&caddr, &len);
        if (acc < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("accfd : %d\n", acc);
        printf("ip:%s\tport:%d\t", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        while (1)
        {
            // 接收消息
            ret = recv(acc, buf, sizeof(buf), 0);
            if (ret < 0)
            {
                perror("recv err");
                return -1;
            }
            else if (ret == 0)
            {
                printf("client close\n");
                break;
            }
            // 使用通信信息
            printf("buf : %s\n", buf);
            memset(buf, 0, sizeof(buf));
        }
    }

    // 关闭文件描述符
    close(acc);
    close(fd_socket);
    return 0;
}

客户端

int main(int argc, char const *argv[])
{
    int sockfd = -1;    // 套接字文件描述符
    char buf[128] = {}; // 通信内容
    int ret = 0;        // 返回值

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd : %d\n", sockfd);

    // 指定服务器网络信息
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);

    // 连接请求
    ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (ret)
    {
        perror("connect err");
        return -1;
    }
    printf("connect ok\n");

    // 发送消息
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);
        if(buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
        if(!strcmp(buf, "quit"))
        {
            printf("client close\n");
            break;
        }
        send(sockfd, buf, strlen(buf), 0);
    }

    // 关闭文件描述符
    close(sockfd);
    return 0;
}

8. 粘包

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值