首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TCP/IP网络编程-4~9章学习笔记

TCP/IP网络编程-4~9章学习笔记

作者头像
用户2825413
发布2019-07-16 10:57:22
发布2019-07-16 10:57:22
55600
举报
运行总次数:0

前置C语言小知识点

stdin,stdout,stderr

名称

全称

含义

stdin

standard input

标准输入流

stdout

standard out

标准输出流

stderr

standard error

标准错误输出

我们来看下面几个函数

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>

#define BUF_SIZE 5

int main(int argc, char *argv[])
{
    char message[BUF_SIZE];

    fputs("请向输入流一个字符串:", stdout); //printf
    fgets(message, BUF_SIZE, stdin); //scanf
    fputs(message,stderr); //output: message
}

上面我们使用到了stdout、 stdin, 并且最后还写入到 stderr流, 输出到了控制台. stdout和stderr都能输出到控制台, 除了语义上区别外, stderr是没有缓冲的,他立即输出,而stdout默认是行缓冲,也就是它遇到‘\n’,才向外输出内容,如果你想stdout也实时输出内容,那就在输出语句后加上fflush(stdout),这样就能达到实时输出的效果

fputs、fgets指定到流的操作(文件流), 对应的直接输入输出还有 puts、gets,这里不再推荐使用puts、gets了, 他们之间也有区别

gets()丢弃输入中的换行符,但是puts()在输出中添加换行符。另一方面,fgets()保留输入中的换行符,fputs()不在输出中添加换行符,因此,puts()应与gets()配对使用,fputs()应与fgets()配对使用。

编码实践 echo 小案例

echo_server.c

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define BUF_SIZE 5

int main(int argc, char *argv[])
{
    char message[BUF_SIZE];
    int str_len, i;

    struct sockaddr_in serv_addr, clnt_addr;

    int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
    {
        printf("socket() error");
        exit(1);
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(9600);

    if (bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        printf("bind() error");
        exit(1);
    }

    if (listen(serv_sock, 5) == 1)
    {
        printf("listen() error");
        exit(1);
    }

    int clnt_addr_sz = sizeof(clnt_addr);
    for (i = 0; i < 5; i++)
    {
        int clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_sz);
        if (clnt_sock == -1)
        {
            printf("accept() error");
            exit(1);
        }

        while (str_len = read(clnt_sock, message, BUF_SIZE) > 0)
        {
            write(clnt_sock, message, str_len);
        }

        close(clnt_sock);
    }

    close(serv_sock);
    return 0;
}
代码语言:javascript
代码运行次数:0
运行
复制

echo_client.c

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define BUF_SIZE 5

int main(int argc, char *argv[])
{
    char message[BUF_SIZE];
    int str_len, i;

    struct sockaddr_in serv_addr, clnt_addr;

    int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (serv_sock == -1)
    {
        printf("socket() error");
        exit(1);
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(9600);

    if (connect(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
        printf("connect() error");
        exit(1);
    }

    while (1)
    {
        fputs("请输入您的信息,按Q键退出\n", stdout);
        fgets(message, 1024, stdin);

        //因为fgets会保留输入中换行符,故判断加\n
        if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
        {
            break;
        }

        write(serv_sock, message, sizeof(message));
        read(serv_sock, message, BUF_SIZE - 1);
        printf("Message from server: %s\n", message);
    }

    close(serv_sock);
    return 0;
}

上面代码简单完成了 echo 的操作(我们输入什么,服务端返回什么)

我们发现当数据超过5个字符时候(\n也默认为一个字符), 将会截断发送, 我们可以使用下面方式。

代码语言:javascript
代码运行次数:0
运行
复制
str_len = write(serv_sock, message, strlen(message));

recv_len = 0;
while (recv_len < str_len)
{
    recv_cnt = read(serv_sock, &message[recv_len], BUF_SIZE - 1);
    if (recv_cnt == -1)
    {
        printf("read() error");
        exit(1);
    }
    recv_len += recv_cnt;
}

上面将是循环接收数据, 直到接收完毕退出循环体

gethostbyname 函数 根据域名获取IP地址

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct hostent *host;

    host = gethostbyname("www.xueba100.com");

    printf("h_name=%s\n", host->h_name);
    printf("h_addrtype=%d\n", host->h_addrtype);

    int i;
    for (i = 0; host->h_addr_list[i]; i++)
    {   
        //将IP指针转换为 in_addr 结构体, 再调用inet_ntoa转换为字符串形式
        printf("Ip addr: %s\n", inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));
    }
}

setsockopt 设置socket选项

这里举例说明 设置 SO_REUSEADDR 选项

当我们主动关闭服务端时候, 将会产生TIME_OUT, 这样会导致端口地址无法重用,规范中规定等待 2MSL 时间才可以使用。 我们可以使用 setsockopt 设置地址重用。

代码语言:javascript
代码运行次数:0
运行
复制
socklen_t option;
int optlen = sizeof(option);
option = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&option, optlen);

与之对应的 getscokopt 函数, 获取选项

Nagle 算法

只有收到前一数据的 ACK 消息时, Nagle 算法才发送下一数据。

TCP 套接字默认使用的 Nagle 算法交换数据, 因此最大限度地进行缓冲, 直到收到 ACK。

如果不使用 Nagle 无需等待 ACK 的前提下连续传输, 大大提高传输速度.

使用 Nagle 交互图

把图画残了。。。 当我们传输大文件, 注重传输速度时候可以禁用 Nagle 算法, 如果考虑到传输内容很小, 头部信息就有可能几十个字节, 可以使用 Nagle 算法, 减少网络传输次数。

禁用 Nagle 算法

代码语言:javascript
代码运行次数:0
运行
复制
socklen_t option;
int optlen = sizeof(option);
option = 1;
setsockopt(serv_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&option, optlen);

end…

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 呆呆熊的技术路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前置C语言小知识点
  • 编码实践 echo 小案例
  • gethostbyname 函数 根据域名获取IP地址
  • setsockopt 设置socket选项
  • Nagle 算法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档