前置C语言小知识点
stdin,stdout,stderr
名称 | 全称 | 含义 |
---|---|---|
stdin | standard input | 标准输入流 |
stdout | standard out | 标准输出流 |
stderr | standard error | 标准错误输出 |
我们来看下面几个函数
#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
#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;
}
复制代码
echo_client.c
#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也默认为一个字符), 将会截断发送, 我们可以使用下面方式。
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地址
#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 设置地址重用。
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 算法
socklen_t option;
int optlen = sizeof(option);
option = 1;
setsockopt(serv_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&option, optlen);
复制代码