第3章 套接字编程简介
3.1、概述
套接字地址结构的传递主要是两个方向;从进程到内核、和从内核到进程,
地址转换函数;就是输入的是带.点的10进制数应该要转换为u32 int类型的数
大小端问题,本地操作系统大小端不确定,但是网络传输一般都是采用大端模式,及高位数据在低地址。
3.2、套接字地址结构
大多数套接字函数都是需要一个指向套接字地址结构的指针作为函数参数的,因此每个协议族都定义他自己的套接字地址结构,并且以socketaddr_开头。
3.2.1、IPV4的套接字地址结构 scoketaddr_in
在POSIX规范中只需要我们填充三个字段;sin_family、sin_addr、sin_port。
注意 IPV4地址和TCP/UDP端口号在套接字地址结构中总是以网络字节序来存储及大端模式,因此赋值时记得转换。
3.2.2、通用套接字地址结构 socketaddr
需要注意的是 因为套接字函数是在ANSI C之前定义的,因此这些函数使用的也都是struct socketaddr这个作为参数类型的,因此我们在使用IPV4,6对应套接字类型的时候必须要进行强制转换的。
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
因此在使用的时候
struct socketaddr_in seraddr;
bind(sockfd, (const struct sockaddr *)&seraddr, sizeof(seraddr));//必须要强制转换,因为内核处理的时候是以sockaddr 格式去解析的
3.2.3、IPV6套接字地址结构 socketaddr_in6
3.2.4、新的通用套接字地址结构 socketaddr_storage
3.3、值 - 结果的传递 传递大小的到底传值还是指针
从进程到内核传递套接字 地址结构的函数有;bind、connect、sendto
他们其中都有两个参数是套接字地址结构的指针,和该结构体的大小,从而内核才知道到进程复制多大内存来解析
connect(sockfd, (const struct sockaddr *)&seraddr, sizeof(seraddr));
从内核到进程传递套接字 地址结构的函数有;accept、recvform、getsockname、getpeername;他们其中都有两个参数是套接字地址结构的指针,和该结构体的大小的整数变量的指针,对比为什么要返回整形变量的指针,在于需要传递结果回来,存储到这个整形变量里面。
accept(sockfd, (struct sockaddr *)&cliaddr, &len);
3.4、字节排序函数;解决大小端问题
用来将主机short类型的字节顺序转为网络字节顺序。
htons : h(host) to n(net) s (short):
用来将网络 short 类型的字节顺序转为主机字节顺序。
ntohs : n(net) to h(host) s(short) :
用来将主机字节顺序转换为网络字节顺序,只不过转换变量的类型不同为 unsigned long
htonl : h(host) to n (net) l (long ) :
用来将网络字节顺序转换为主机字节顺序,转换的变量为 unsigned long
ntohl: n (net) to h(host) l (long) :
————————————————
版权声明:本文为CSDN博主「zw1996」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zw1996/article/details/114113581
3.5、字节操作函数
3.6、字符串和网络子序中的转换函数
3.9、实现readn、writen、readline函数
readline 读取的时候如果每读一个字符就read一下这样效率太低了,但是如果有一个缓冲的话就效率高很多了。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <stdlib.h>
static int read_cnt;
static char *read_ptr;
static char read_buff[1000];
//读取n个字符,返回读出的字节数
int readLine(int fb, void *vpr, int n);
static int Myread(int fb, char *vpr);
int readLine_Quilt(int fb, void *vpr, int nmax);
int main()
{
int fb;
fb = open("./test.txt", O_RDONLY);
if (fb < 0)
{
printf("open: eror\n");
return -1;
}
char *p;
p = (char *)malloc(sizeof(char)*100);
while(1)
{
memset(p,0,sizeof(p));
if(readLine_Quilt(fb, p, 100) > 0)
{
if(p == NULL)
break;
printf("%s", p);
}
else
{
break;
}
}
return 0;
}
static int Myread(int fb, char *vpr)
{
if(read_cnt <= 0)
{
if((read_cnt = read(fb, read_buff, sizeof(read_buff))) < 0)
{
return -1;
}
else if(read_cnt == 0)
{
return 0;
}
read_ptr = read_buff;
}
read_cnt--;
*vpr = *read_ptr++;
return 1;
}
int readLine_Quilt(int fb, void *vpr, int nmax)
{
char *ptr = vpr;
char c;
int nread;
int n;
for( n = 1; n < nmax; n++)
{
if((nread = Myread(fb, &c)) == 1)//有个缓冲,先读到存储到那里,
{
*ptr++ = c;
if(c == '\n')break;
}
else if(nread == 0)
{
*ptr = 0;
return n-1;
}
else
{
return -1;
}
}
*ptr = 0;
return n;
}
int readLine(int fb, void *vpr, int nmax)
{
char *ptr = vpr;
char c;
int nread;
int n;
for( n = 1; n < nmax; n++)
{
if((nread = read(fb, &c, 1)) == 1)//每读一个字节就要read一下,效率低
{
*ptr++ = c;
if(c == '\n')break;
}
else if(nread == 0)
{
*ptr = 0;
return n-1;
}
else
{
return -1;
}
}
*ptr = 0;
return n;
}
第4章 基于TCP套接字编程
4.1、概述一个完成的TCP客户端/服务器程序
4.2、socket函数
为了执行网络IO,则一个进程必须做的一件事情就是调用socket函数,指定期望的通信协议类型,也就是要执行IO通信通道所支持的协议类型进行指定。
int socket(int domain, int type, int protocol);
domain;表示网络域,就是网络范围,ipv4还是ipv6有相关宏设置AF_INET、AF_INET6
type;通信协议的类型,tcp,udp...也有相关的宏来设置
protocol 一般传0表示无特例,默认协议
也可查看这篇对socket接口的基本介绍和案例
https://blog.csdn.net/zw1996/article/details/114113581