一.网络通信模型
TCP/IP:Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。
OSI/ISO:OSI模型,即开放式通信系统互联参考模型(Open System Interconnection,OSI/RM,Open Systems Interconnection Reference Model),是国际标准化(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。
TCP/UDP(传输层协议)
TCP:通过面向连接、端到端和可靠的数据包发送。(通常用来传输需要保密数据)
UDP:不面向连接、不可靠的数据包发送(通常用来传输大数据)
IPV4/IPV6:(网络层)
IPV4:IP地址是32位即,2的32次方
IPV6:IP地址是64位即,2的64次方
将IPV4转化用的是NAT(网络地址转换)
一般操作系统会自动完成传输层、网络层、物理层的衔接
操作系统五大功能:进程调度(CPU)、内存管理、文件系统(硬盘)、设备管理(外设)、网络管理(计算机网络)
MAC 地址:物理地址(在物理层定义的)48位,每个网卡都有一个物理地址。前三个为厂商ID,后三个字节为流水号
二.unix网络编程流程
三.函数socket()
int socket(int domain, int type, int protocol);
创建socket, 返回文件描述符
domain(域): AF_INET(IPV4)(机器与机器联系)、AF_INET6(IPV6)、AF_UNIX(命名socket)(进程与进程之相互通信)、AF_UPSPEC
type(类型): SOCK_DGRAM(数据报文)、 SOCK_RAW(原始报文)、 SOCK_SEQPACKET(带序列的报文)、 SOCK_STREAM(TCP报文)
protocol: IPPROTO_IP、IPPROTO_IPV6、IPPROTO_ICMP(ping命令,服从ICMP协议)、IPPROTO_RAW、IPPROTO_TCP、 IPPROTO_UDP
返回值是 fd(文件描述符)
四.函数bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
将socket和相应的IP地址、端口绑定
sockfd: socket()返回的文件描述符
addr: 绑定的IP和端口
addrlen: addr的长度
五.函数ulisten()、函数accept()
int listen(int sockfd, int backlog);
开始监听套接字
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
开始等待客户端连过来套接字
addr 和 addrlen 一般是NULL,他是客户端套接字的标识(包括客户端IP和端口信息等)
注:1.1024以下的端口需要root权限才可以监听,原因是这些端口大部分已经被厂商使用了
2.端口号是16位最大值:65535
3.网络socket数据必须是大端字节序 htons()将小端字节序换成大端字节序 主机到网络转成short(2个字节16个位)
六.函数connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
连接指定的服务器,其中addr指定连接服务器的IP和端口
七.函数shutdown()、函数close()
int close(int fd);
关闭socket
int shutdown(int sockfd, int how);
半关闭socket
how: SHUT_RD、 SHUT_WR、 SHUT_RDWR
八.函数select()
while { read(fd1, buf, sizeof(buf) ); read(fd2, buf, sizeof(buf) ); read(fd3, buf,sizeof(buf) ); }
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
集合最大文件描述符 可读的文件描述符集合 可写的文件描述符集合 异常的文件描述符集合 超时时间
fd_set rdset, wrset, errset; int maxfd;
FD_ZERO(&rdset); FD_ZERO(&wrset); FD_ZERO(&errset);
FD_SET(fd1, &rdset); FD_SET(fd2, &rdset); FD_SET(fd3, &rdset);
FD_SET(fd1, &wrset); FD_SET(fd3, &errset);
maxfd = MAX(fd1, fd2); maxfd = MAX(maxfd, fd3);
struct timeval tv; tv.tv_sec = 30; tv.tv_usec = 0;
while( (rv=select(maxfd+1, &rdset, &wrset, &errset, &tv) )> 0 )
//while( (rv=select(maxfd+1, NULL, NULL, NULL, NULL) )> 0 ) 不关心可写、出错,一直阻塞不要超时
{
if (FD_ISSET(fd1, &rdset)) { read(fd1, buf, sizeof(buf)); }
if (FD_ISSET(fd2, &rdset)) { read(fd2, buf, sizeof(buf)); }
if (FD_ISSET(fd3, &rdset)) { read(fd3, buf, sizeof(buf)); }
if (FD_ISSET(fd1, &wrset)) { write(fd3, buf, sizeof(buf)); }
if (FD_ISSET(fd3, &errset)) { printf("fd3 get error"); }
}
注:可以用来延时毫秒级select(0,NULL,NULL,NULL,1ms)
九.简单的服务器端程序
int main(int argc, char **argv)
{
int listen_fd, new_fd = -1;
struct sockaddr_in serv_addr; char buf[1024];
listen_fd = socket(AF_INET, SOCK_STREAM, 0); //IPV4 TCP协议 listen_fd = 3
memset(&serv_addr, 0, sizeof(serv_addr)); //对地址清零
serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(8889); //1
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //2 任何地址即IP
bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //3
listen(listen_fd, 13); //13表示最多13个客户端排队
while(1) {
new_fd = accept(listen_fd, NULL, NULL); //监听客户端信息(IP、端口)4
memset(buf, 0, sizeof(buf)); read(new_fd, buf, sizeof(buf)); printf("read '%s' from client\n", buf);
write(new_fd, "goodbye", strlen("goodbye"));
sleep(1); close(new_fd);
}
close(listen_fd);
}
注:1.网络socket数据必须是大端字节序 htons()将小端字节序换成大端字节序 主机到网络转成short(2个字节16个位),网络转换成主机ntohs()
2.主机到网络转成long(4个字节32个位)
3.sockaddr 与前面的 sockaddr_in不同,这里的sockaddr是通用套接字结构体兼容IPV4和IPV6
4.accept()会返回一个新的文件描述符来表示客户端的信息,此时new_fd与客户端就绑定起来(相当于可以通信的文件描述符),并且accept()为阻塞模式
十.客户端程序
int main(int argc, char **argv)
{
int conn_fd = -1;
struct sockaddr_in serv_addr;
char buf[1024];
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8889);
inet_aton( "127.0.0.1", &serv_addr.sin_addr ); //127.0.0.1表示本机器
connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //1
write(conn_fd, "hello", strlen("hello"));
memset(buf, 0, sizeof(buf));
read(conn_fd, buf, sizeof(buf)); printf("read '%s' from client\n", buf);
sleep(1); close(conn_fd);
}