socket编程(上)
1.网络数据的五元组信息
- 源ip地址:标识网络数据从哪台主机发出
- 源端口:标识网络数据从源ip对应的这台主机的哪个进程产生
- 目的ip:表示网络数据要去哪一台主机
- 目的端口:通过目的ip找到目的主机之后,通过目的端口找到相应的进程
- 协议:双方传输数据时,使用什么协议(一般是TCP/IP)
2.网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
- 小端字节序:低位放在低地址
- 大端字节序:低位放在高地址
- 主机字节序:指的是机器本身的字节序,如果是大端,则主机字节序为大端。如果是小端,则主机字节序为小端
- 网络字节序:规定网络传输数据的时候采用大端字节序进行传输
主机字节序和网络字节序的互相转换
主机字节序转换成网络字节序
ip:unint32_t
uint32_t htonl(uint32_t hostlong);
port:uint16_t
uint16_t htons(uint16_t hostshort);
网络字节序转换成主机字节序
ip:unint32_t
uint32_t ntohl(uint32_t hostlong);
port:uint16_t
uint16_t ntohs(uint16_t hostshort);
3.TCP/UDP协议特性与区别
UDP:
- 无连接:UDP双方在发送数据之前是不需要进行沟通的,只需要知道对方的ip和端口就可以发送
- 不可靠:不保证UDP数据是可靠、有序的到达对方
- 面向数据报:UDP和应用层/网络层递交数据时都是整条数据进行交付的
TCP:
- 面向连接:TCP在发送数据之前会先建立连接(1.确保对方能正常通信。2.沟通双方发送后续数据的细节(例如序号))
- 可靠传输:TCP保证传输的数据是可靠的、有序的到达对端的
- 面向字节流:1.对于传输的数据没有传输的边界
2.对于接收方而言,可以按照1任意的字节进行接收
4.UDP-socket编程
流程:
服务端:创建套接字,绑定地址信息
客户端:创建套接字,不推荐绑定地址信息(可以绑定)
为什么要创建套接字?
将进程和网卡进行绑定,进程可以从网卡中接收数据,也可以通过网卡发送数据
为什么要绑定地址信息?
绑定ip,绑定端口是为了在网络中可以标识出来一台主机和一个进程
对于接收方而言,发送数据的人就知道接收方在哪台主机哪个进程了
对于发送方而言,能够标识网络数据从哪台机器哪个进程发送出去的
5.编程接口
创建套接字:
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
- domain:地址域-选择一个具体的协议族进行沟通,TCP/UDP,可以认为在指定网络层使用什么协议
AF_UNIX:本地域套接字(在同一台机器使用文件进行通信,不用跨机器)
AF_INET:ipv4版本的ip协议
AF_INET6:ipv版本的IP协议- type:套接字的类型
SOCK_DGRAM:用户数据报套接字-对应UDP
SOCK_STREAM:流式套接字-对应TCP- protocol:协议
0:表示按照套接字类型选择默认协议
也可以执行具体的协议:
IPPROTO_TCP(6):代表TCP协议
IPPROTO_UDP(17):代表UDP协议- 返回值:返回套接字操作句柄,本质上就是一个文件描述符
大于等于0:创建成功
小于0:创建失败
绑定接口:
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
- sockfd:套接字描述符
- addr:绑定的地址信息
- addrlen:绑定的地址信息长度
发送接口:
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *deat_addr,socklen_t addrlen);
- sockfd:套接字描述符
- buf:要发送的数据
- len:要发送数据的长度
- flags:0(阻塞发送)
- dest_addr:地址信息结构,包含了目的ip,目的端口
- addrlen:地址信息长度
- 返回值:成功,返回正常发送的数据;失败,返回-1
接收接口:
ssize_t resvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);
- sockfd:套接字描述符
- buf:程序员准备的接收数据的缓冲区
- len:最大能接受数据的大小
- flags:0(阻塞接收)
- src_addr:源ip+源端口
- addrlen:这是一个出参,返回地址信息的长度
通用数据结构:
struct sockaddr{
sa_family_t sa_family;//地址域,占用两个字节
char sa_data[14];
};
struct sockaddr_in{
sa_family_t sin_family;//地址族
uint16_t sin_port;//端口号
struct in_addr sin_addr;//32位IP地址
char sin_zero[8];//预留未使用
};
struct in_addr{
In_addr_t s_addr;//32位IPV4地址
};
代码:
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(){
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sockfd<0){
perror("socket");
return 0;
}
printf("sockfd is %d\n",sockfd);
//2.绑定地址信息
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(28989);
//1.将点分十进制的ip转换成无符号的32位整数
//2.将无符号的32位整数转换成网络字节序
addr.sin_addr.s_addr=inet_addr("10.0.16.3");
int ret=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret<0){
perror("bind");
return 0;
}
while(1){
sleep(1);
}
return 0;
}