一、Socket是什么
1、 socket套接字:
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
注意:其实socket也没有层的概念,它只是一个facade设计模式的应用,让编程变的更简单。是一个软件抽象层。在网络编程中,我们大量用的都是通过socket实现的。
2, 套接字的描述符是什么
其实在内核中我们进行套接字通信的时候开辟了两块空间用于我们数据的发送和接受,我们在创建套接字的时候其实操作的也时文件描述符。
3,什么时网络字节序
- 字节序是cpu在内存中进行数据存储的顺序,这可以分为大端字节序,小端字节序。(这是取决于cpu架构)
大端字节序:低地址存高位。
小端字节序:低地址存低位。
主机字节序:当前计算机的字节序
可以想象在两台不同的计算机因为计算机的字节序不同而导致我们数据的二义性,这使得我们收到的数据和发送的数据不一致。
这时候我们使用同一的规定,网络字节序(就是在网络传输中规定使用同一种字节序,这里是大端字节序)。
地址转换
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
判断当前机器的大小端
#include <iostream>
using namespace std;
int main(){
int p = 0x11223344;
char* pp = (char*)&p;
cout << *pp << endl;
system("pause");
return EXIT_SUCCESS;
}
4,套接字编程API
- socket接口
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:域参数指定通信域(常使用AF_INET)
type:套接字类型(通常使用TCP的SOCK_STREAM和udp的SOCK_DGRAM)
protocol:协议类型,常使用IPPROTO_TCP(也就是6),IPPROTO_UDP(17)。如果是0的话使用默认协议
返回值:生成一个套接字描述符,失败返回-1并设置errno
- bind接口
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
bind
为套接字 sockfd
指定本地地址 my_addr
. my_addr
的长度为addrlen
(字节).传统的叫法是给一个套接字分配一个名字. 当使用 socket(2),函数创建一个套接字时,它存在于一个地址空间(地址族), 但还没有给它分配一个名字
成功返回0,失败返回-1
- listen接口
#include <sys/socket.h>
int listen(int s, int backlog);
使其能够自动接收到来的连接并且为连接队列指定一个长度限制.注意是同时请求连接的最大数量
s:是套接字描述符
backlog:数量
返回值:成功返回0,失败返回-1.
Listen实现最大连接原理
在内核中存在两个队列,一个是未完成连接和已完成连接,而最大连接数量就是未完成连接队列的最大节点数
- connect接口
#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;并设置errno
- accept接口
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
s:套接字描述符
addr:用来保存客户端的地址信息
addrlen:变量的长度
返回值:失败返回-1,成功返回用于通信的套接字描述符
- 发送信息接口
#include <sys/types.h>
#include <sys/socket.h>
int send(int s, const void *msg, size_t len, int flags);
int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
int sendmsg(int s, const struct msghdr *msg, int flags);
send
, sendto
, 和 sendmsg
用于向另一个套接字传递消息. send
仅仅用于连接套接字,而 sendto
和 sendmsg
可用于任何情况下.
所以在tcp通信中使用send,在udp中使用sendto
s
:用于通信的套接字描述符
msg
:发送信息的缓冲区
len
:长度
flags
:属性选项设置
const struct sockaddr *to, socklen_t tolen
:这只是在sendto
中使用,需要指定地址信息
- 接受信息接口
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
recv,recvfrom,recvmsg其实和send,sendto,sendmsg类似。
理解sockaddr,sockaddr_in
其实sockaddr
和sockaddr_in
地址占用的是同样的大小,只是在先前unix阶段我们使用的是sockaddr
,所以在接口的设计中使用的是sockaddr
,但是在sockaddr
中我们不能很好的对空间进行赋值,所以我们要使用sockaddr_in
进行赋值然后进行强转