套接字:通过 socket() 函数来创建一个网络连接,或者说打开一个网络文件,socket() 的返回值是(socket描述符),同文件描述符。
Windows 也有类似“文件描述符”的概念,但通常被称为“文件句柄”。 Linux 平台则使用“描述符”。
Internet 套接字分成两种类型:
流格式套接字(SOCK_STREAM):TCP 用来确保数据的正确性,IP(Internet Protocol,网络协议)用来控制数据如何从源头到达目的地,也就是常说的“路由”。
面向连接好像有一条管道,它连接发送端和接收端,数据包都通过这条管道来传输。当然,两台计算机在通信之前必须先搭建好管道。按照顺序到达。
数据报格式套接字(SOCK_DGRAM):数据报套接字也使用 IP 协议作路由,但是它不使用 TCP 协议,而是使用 UDP 协议。
对于无连接的套接字,每个数据包可以选择不同的路径;到达没有顺序。
我们所说的 socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能干「访问网页」这样的事情,因为访问网页所需要的 http 协议位于应用层。
IP地址:一台计算机可以拥有一个独立的 IP 地址,一个局域网也可以拥有一个独立的 IP 地址(对外就好像只有一台计算机)。对于目前广泛使用 IPv4 地址,它的资源是非常有限的,一台计算机一个 IP 地址是不现实的,往往是一个局域网才拥有一个 IP 地址。【本机IP:127.0.0.1】
MAC地址:真正能唯一标识一台计算机的是 MAC 地址,每个网卡的 MAC 地址在全世界都是独一无二的。
端口号:为了区分不同的网络程序,计算机会为每个网络程序分配一个独一无二的端口号(Port Number),例如,Web 服务的端口号是 80,FTP 服务的端口号是 21,SMTP 服务的端口号是 25。
socket编程:
服务器:
int socket(int domain, int type, int protocol);//创建套接字
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);//将套接字与特定的IP地址和端口绑定
int listen(SOCKET sockfd, int backlog);//开启被动监听(默认的套接字是主动监听的)
int accept(int sockfd, struct sockaddr* addr, socklen_t* len);//函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。
ssize_t send (int s,const void *msg,size_t len,int flags);//发送数据
close(fd);
当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。
客户端:
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);//建立与服务器的连接
listen() 只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept() 函数。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。