1.创建和连接

大端存储:高位字节存在低地址处
小端存储:低位字节存在低地址处
现代pc大多数采用小端存储,所以小端也被称为主机字节序
所以发送方一般都要把小段转化为大端再发送
接收方根据自身情况进行决定
大端存储就被称为网络字节序     (同一主机不同语言编写的也要考虑字节序的问题)
                                    java是大端

主机字节序和网络字节序的转换

linux提供四个函数转换字节序

#incldue<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int hostlong);
unsigned short int ntohs(unsigned short int hostshort);

通常长整型用来转换ip地址
短整型用来转换port

通用socket地址

1.#include<bits/socket.h>
struct sockaddr{
    sa_family_t  sa_family;       //地址族类型
    char sa_data[14];   //存放socket地址值
}
tip:先分类型,再识别值
注意这是一个结构体     因为14字节存不下,所以用下面的

2.

struct sockaddr_storage{
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[128-sizeof(__ss_align)];
}
这个结构体提供了足够大的空间,而且内存是对齐的(__ss_align)的作用

地址族类型 与 协议族类型(domain)的对应

AF_UNIX      PF_UNIX                       本地域  //协议族 protocol family
AF_INET      PF_INET                        IPV4
AF_INET6     PF_INET6                       IPV6

PF和AF存在<bits/socket.h>中,而且他俩有完全相同的值所以常常混用

PF_UNIX:文件路径名,可长达108字节
AF_INET:16bit端口加32bit ipv4地址,一共48bit,6字节
AF_INET6:16端口号,32流标识,128的ipv6地址,32的范围id,一共26字节

专用socket地址

因为通用地址不好操作
linux为各个协议族提供了专门的socket地址结构
1.UNIX:
    #include<sys/un.h>
    struct sockaddr_un{
        sa_family_t sin_family;    //地址族AF_UNIX
        char sun_path[108];       //文件路径名
    }
    
2.ipv4
struct sockaddr_in{
    sa_family_t sin_family;      地址族AF_INET
    u_int16_t sin_port;
    struct in_addr sin_addr;    ipv4的地址结构
}    
struct in_addr{
        u_int32_t s_addr;    //ipv4地址,要用网络字节序表示
}

3.ipv6
struct sockaddr_in6{
    sa_family_t sin6_family;     地址族 AF_INET6
    u_int16_t sin6_port;          端口号,网络字节序表示
    u_int32_t sin6_flowinfo;      信息流,应设置为0
    struct in6_addr sin6_addr;    ipv6地址结构
    u_int32_t sin6_scope_id;  
}
struct in6_addr{
    unsigned char sa_addr[16];  // ipv6地址,要用网络字节序表示
}
所有 专用类型的socket类型的变量在实际使用的时候都要转换为通用socket地址类型sockaddr(强转)
因为各种接口接收的参数类型都是sockaddr类型

ip地址转换函数

网络字节序整数(二进制)<-------->点分十进制字符串

ipv4:
#include<arpa/inet.h>
in_addr_t inet_addr(const char*strptr)     10->2 失败返回INADDR_NONE
int inet_aton(const char *cp,struct in_addr *inp) 10->2 结果存在inp指向的地址
成功返回1,失败返回0
char *inet_ntoa(struct in_addr in );   2-->10
        该函数内部用一个静态变量存储转化的结果,所以不可重入,只能设置一次


ipv4和ipv6
#include<arpa/inet.h>
int inet_pton(int af,const char * src,void *dst)    10--->2
结果存入dst指向的内存中
af指定地址族;AF_INET/AF_INET6
成功返回1,失败返回0
const char *inet_ntop(int af,const void *src,char *dst,socklen_t cnt);
2----》10
cnt指定目标存储单元的大小
成功返回目标存储单元的地址

创建socket

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
domain:协议族类型,tcp/ip使用PF_INET(ipv4)
                            /PF_INET6(ipv6)
                            /PF_UNIX
type:服务类型  SOCK_STREAM   SOCK_DGRAM
                流服务          数据报
对于tcp/ip而言,流服务对应着tcp协议
              数据报对应着udp协议                
SOCK_NONBLOCK:新建的socket设为非阻塞
SOCK_CLOEXEC:用fork调用创建子进程时在子进程中关闭该socket
protocol:在前两个参数的情况下,再选择一个具体的协议,前两个参数已经决定她的值
几乎所有的情况下都设为0,表示使用默认协议
成功返回一个文件描述符,失败返回-1   

命名socket(bind)

只有命名了,客户端才知道怎么去链接这个socket
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct socketaddr * my_addr,socklen_t addrlen)
把sockfd和my_addr地址绑定,addrlen指出socket地址的长度
成功返回0,失败返回-1并设置errno:
                            EACCES:地址受保护,进超级用户可访问
                            EADDRINUSE:被绑定的地址正在使用

监听socket(listen)

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

sockfd 被监听的socket
backlog内核监听队列的最大长度
监听队列如果超过backlog,客户端将受到ECONNREFUSED
Linux内核2.2之前,backlog指处于半连接(syn_recv)和全连接(established)的socket的上限,
在2.2之后,它表示处于完全链接的socket的上限
处于半连接的上限由tcp_max_syn_backlog内核参数定义,典型值为5
listen成功返回0,失败返回-1

接收连接accept

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

sockfd是执行过listen系统调用监听的socket
addr用来获取被接受连接的远端的socket地址
addrlen 表示该地址的长度
accept连接成功返回一个新的连接标识符(socket),可以通过这个新的文件描述符来进行通信
失败返回-1
accept只是从监听队列中取出连接,而不管连接处于何种状态,更不关心网络状况的变化

客户端:发起连接connect

服务端通过listen调用被动的接收连接,那么客户端需要调用connect主动连接。
int connect(int sockfd, const strucr sockaddr *server_addr,socklen_t addrlen);

socketfd参数:系统调用返回一个scoket
server_addr:是服务器监听的socket地址
addrlen:这个地址的长度
connect成功时返回0
一旦成功,sockfd就唯一标识了这个连接,客户端就可以读写socket和服务端通信
失败返回-1,errno:ECONNREFUSED  目标端口不存在
                ETIMEDOUT 超时

关闭连接

int clsoe(int sockfd);
close 系统调用并非总是立即关闭一个连接,而是将sockfd引用-1,只有sockfd变为0,才真正关闭连接

在多进程中,一次fork会使父进程的fd+1,所以我们必须对父子进程的fd都执行close
(如要立即终止,使用shutdown)
int shutdown(int fd,int howto)
howto:SHUT_RD  只关闭socketfd上读的这一块,程序不能再执行读操作,且缓冲区中的数据被丢弃
      SHUT_WR  关闭写的
      SHUT_RDWR  读写同时关闭

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值