1.Socket套接字
Socket套接字是一个编程接口(网络编程接口)
是一个特殊的文件描述符(read/write)
socket可以用于TCP/IP,也可用于IPC,
蓝牙....、
socket是独立与具体协议的编程接口,这个接口位域
TCP/IP的四层模型中的应用层和传输层之间
Sokcet套接字类型由三种
(1)流式套接字(SOCK_STREAM)
面向字节流 针对采用TCP的应用
(2)数据报套接字(SOCK_DGRAM)
针对采用UDP通信的应用 比如视频 ,可能掉包,TCP不会掉包
(3)原始套接字(SOCK_RAW)
直接跳过传输层
2.基本的TCP套接字编程流程
任何一个网络应用都有 通信双方
Client 客户端
Server 服务端
TCP网络应用
TCP Server
TCP Client
任何一个网络应用
传输层协议(TCP/UDP) + IP + 端口
“网络地址”
任何网络应用一方都需要一个网络地址来标识自己
TCP网络应用的数据传输流程的大概过程
建立连接:
“三次握手”
发送/接收网络数据
write/send/sendto
read/recv/recvfrom
关闭连接
“四次挥手”
TCP网络应用的编程流程
TCP Server
socket :创建一个socket套接字
bind: 把服务器的套接字和网络地址绑定在一起
如果你想要别人来主动连接你,你就需要
bind一个地址,并且把这个地址告诉别人
不调用bind,不代表你的socket没有地址
相反,无论是否调用bind ,socket通信时,
内核都会动态的为你socket指定一个地址
listen: 监听 让套接字进入一个监听的状态
accept:接收客户端的连接
如果你多次调用accept,就可以和不同的
客户建立连接
至此 你们就可以等待用户来连接你 三次握手
write/send/sendto
read/recv/recvfrom
......
close/shutdown:“四次挥手”
TCP Client
socket :创建套接字
bind :可要可不要 如果需要别人来连接你 就需要
connect :主动和TCP Server建立连接
三次握手
write/send/sendto
read/recv/recvfrom
......
close/shutdown:“四次挥手”
3.socket具体的API函数解析
(1)socket:创建一个套接字
NAME
socket - create an endpoint for communicationSYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);
int domain:指定域 协议族,socket接口不仅仅局限于
TCP/IP,还能用于蓝牙 IPC ...
每一种下面又有很多自己的协议,所以我们把Ipv4
下面的所有协议都归类到一个域去
AF_INET Ipv4协议族
AF_INET6 Ipv6协议族
int type :指定你要创建套接字的类型
SOCK_STREAM:流式套接字 -》tcp
SOCK_DGRAM: 数据包套接字 -》UDP
SOCK_RAW: 原始套接字
int protoco :指定具体的应用层协议的时候
指定为0 (私有协议)
返回值
成功返回一个套接字描述符(>2 文件描述符 任意一个进程 系统都会为他打开三个文件 标准输入标准输出标准出错)
失败返回-1 同时errno被设置
(2)网络地址结构体
socket接口不经用于ipv4也用于ipv6
.....
不同的协议族,它们地址的类型也是不一样
socket编程接口有一个通用的“网络地址接口”
虚拟机里面的地址: /usr/src/linux-headers-5.4.0-89-generic/include/linux
struct sockaddr {
sa_family_t sin_family; /* address family, AF_xxx */0
指定协议族
char sa_data[14]; /* 14 bytes of protocol address */
};
IPV4 协议地址的结构
struct sockaddr_in
{
sa_family_t sin_family;//指定协议族为 AF_INET
u_int16_t sin_port;//端口号
struct in_addr sin_addr;//ip地址
char sin_zero[8];//凑字数的 为了和sockaddr结构体保持大小一致
};struct in_addr
{
in_addr_t s_addr;
};
把 主机的字节序 转为 网络字节序
NAME
htonl, htons, ntohl, ntohs - convert values between host and network byte
order
h host
n network
l long 32bits
s short 16bitsSYNOPSIS
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
IP地址间的转化函数
点分式 《===》socket 接口的类型
SYNOPSIS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>a: addr 点分式字符串
n: network 网络地址
a to n 点分式转网络地址
int inet_aton(const char *cp, struct in_addr *inp);
const char *cp :指向要转换的点分式字符串
"192.168.31.4"
struct in_addr *inp :指向接收地址的结构 用来保存转换后的IP地址
返回值
成功返回0
失败返回-1 同时errno被设置
inet_addr 把cp指向的点分式ip字符串转化为
ip地址的结构中的成员
in_addr_t inet_addr(const char *cp);
inet_network 和 inet_addr的功能是一样的
实现的方式不一样
in_addr_t inet_network(const char *cp);
网络地址 转化为 点分式IP地址字符串
char *inet_ntoa(struct in_addr in);
(3) bind 把ipv4的网络地址 绑定到一个socket上面去
NAME
bind - bind a name to a socketSYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd :要绑定的套接字文件描述符
addr :通用的网络地址结构体的指针 强制转换
addrlen :指定addr指向的地址结构的长度 sizeof
返回值
成功返回0
失败返回-1 同时errno被设置
(4)listen:让套接字进入监听模式
NAME
listen - listen for connections on a socketSYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);
sockfd :要监听的套接字的描述符
backlog : 同时能够处理请求(同时进行三次握手)的数目
5,10
返回值
成功返回0
失败返回-1 同时errno被设置
(5)accept:用于TCP Server接收一个客户端的tcp connection建立的请求
NAME
accept, accept4 - accept a connection on a socketSYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd :一个监听套接字;
addr :网络地址的指针 指向的空间用于保存客户端的地址信息的
addrlen :上一个参数的指向的空间大小是多少呢?
返回值
成功返回与该客户端的 连接套接字的描述符
后续与该客户端的数据交换都是通过这个套接字描述符。
失败返回-1 同时errno被设置
(6)connect 主要用于tcp client 去连接tcp server
NAME
connect - initiate a connection on a socketSYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd :套接字文件描述符
addr :你要和谁建立连接
addrlen :网络地址结构体的长度是多少
返回值
成功返回0
失败返回 -1;
(7)往套接字上面发送数据
write/send/sendto
NAME
send, sendto, sendmsg - send a message on a socketSYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd:套接字的文件描述符
buf : 要发送的数据在哪里
len : 要发送的数据有多大
flags : 一般填0
返回值:
成功返回实际发送的字节数
失败返回-1 同时errno被设置
sendto 可以用于TCP 也可以用于UDP
UDP只能使用sendto发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
socket buf len flags 同上面一样
dest_addr :要发送给谁 目标的地址和端口
addrlen :地址结构的大小
返回值
成功返回实际发送的字节数
失败返回-1 同时errno被设置
(8)从套接字上接收数据
read/recv/recvfrom
NAME
recv, recvfrom, recvmsg - receive a message from a socketSYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
从套接字上面接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd :要从那个套接字上面接收数据
buf :数据放哪里
len :想要接收多大的数据 要小于buf大小(单位:字节)
flags :一般为0 阻塞接收数据
MSG_DONTWAIT 非阻塞接收数据
返回值
成功返回实际接收的字节数
失败返回-1 同时errno被设置
UDP只能用recvfrom ,recvfrom可以用于TCP也可以用与UDP
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr: 发送者的网络地址信息,不是指你要接收谁的信息
可以填NULL 表示不关心谁发给我
是一个sockaddr指针
addrlen: src_addr指向空间的大小 返回值
成功返回实际接收的字节数
失败返回-1 同时errno被设置
(9)关闭套接字
close/shutdown
NAME
shutdown - shut down part of a full-duplex connectionSYNOPSIS
#include <sys/socket.h>int shutdown(int sockfd, int how);
sockfd :要关闭那个套接字
how :有三个选项
SHUT_RD:关闭读
SHUT_WR:关闭写
SHUT_RDWR:关闭读写 等效与 close
返回值
成功返回0
失败返回-1 同时errno被设置