前面学习的进程间通信:管道、消息队列、共享内存、信号、信号量
这5种通信方式都是依靠Liunx内核。他们的一个缺点就是,这些通信方式只能在单机运行。
所以引入网络编程可以实现多机通讯
网络的通讯 Linux与Android、IOS、各种单片机,或者是Android对Android等等。
1.何为网络通讯:通过通信双方地址。地址由IP地址和端口号组成。
①IP地址:负责定位主机。
②端口号:负责定位主机上的特定应用(主机上有着多种协议)。
端口号作用
一台拥有IP地址的主机可以提供许多服务,比如Web服务、 FTP服务、SMTP服务等。这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,:因为IP地址与网络服务的关系是
一对多的关系。实际上是通过"IP地址+端口号"来区分不同的服务的。
端口提供了种访问通道
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21.每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件 传送协议)服务器的UDP端口号都是69
Socket在Linux环境下,用于表示进程间网络通信的特殊文件类型。(类似于open函数中的fd文件描述符)
Socket(套接字)主要用到的协议
1、TCP(面向连接)双方必须连接成功才能发数据,类似打电话,主要用于对传输数据精确的情况,如传输指令。
2、UDP(面向报文)双方无须连接成功就能发数据,类似发短信,主要用于传输大数据量的情况,如视频。
两者区别:
1、TCP面向连接(如打电话要先拨号建立连接) ;UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多, 多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
总结:①TCP面向连接,连接只能点对点,UDP面向报文。②TCP保证可靠交付,UDP保证快速交付。③TCP资源消耗大,UDP小。
概念:字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
常见的字节序:大端字节序(将低序字节存储在起始地址),小端字节序(将高序字节存储在起始地址)
高低字节:例如0x01020304,左边字节为高字节,右边字节为低字节,01为高字节,04为低字节
对于数据 0x01020304,假设从地址0x4000开始存放,在大端和小端模式下,存放的位置分别为:
| 内存地址 | 大端字节序 | 小端字节序 |
| 0x4003(高地址) | 01 | 04 |
| 0x4002 | 02 | 03 |
| 0x4001 | 03 | 02 |
| 0x4000(低地址) | 04 | 01 |
| 储存后 | 0x01020304 | 0x04030201 |
IP地址转换API(IP地址是字符串要转换成网络能识别格式)
#include#include #include int inet_aton(const char *cp, struct in_addr *inp); //字符串转网络字节序 //第一个参数为字符串(ip地址)第二个参数是结构体 char *inet_ntoa(struct in_addr in); //网络字节序转字符串
不同设备间字节序需要进行转换,常用api有:
#include uint32_t htonl(uint32_t hostlong); //将无符号整数hostlong从主机字节顺序转换为网络字节顺序 uint16_t htons(uint16_t hostshort); //将无符号短整数hostshort从主机字节顺序转换为网络字节顺序 uint32_t ntohl(uint32_t netlong); //将无符号整数netlong从网络字节顺序转换为主机字节顺序 uint16_t ntohs(uint16_t netshort); //将无符号短整数netshort从网络字节顺序转换为主机字节顺序 //n:net(网络) h:host(主机) s:shut(短整型) l:long(长整型)
socket传输过程,参考大佬的图
常用的API: socket()函数获取套接字
原型:
int socket(int domain, int type, int protocol)
参数:
domain:指定所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
AF_INET IPv4 因特网
AF_INET6 IPv6 因特网
AF_UNIX Uinix域
AF_ROUTE 路由套接字
AF_KEY 密钥套接字
AF_UNSPEC 未指定
type: 指定socket类型:
SOCK_STREAM 指定TCP协议
字节流套接字提供可靠的、面向连接的通信流;他使用TCP协议,从而保证了数据传输正确性和顺序性
SOCK_DGRAM 指定UDP协议
数据报文套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,使用UDP协议
SOCK_RAW 指定底层协议
允许程序使用底层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用不便,主要用于一些协议的开发- 项目
protocol:通常赋值为0(让它3自动分配)
0 选择type类型的默认协议TCP协议
IPPROTO_TCP TCP传输协议
IPPPOTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议
IPPROTO_TIPC TIPC传输协议
功能:绑定IP号和端口号到socket_fd
原型:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
参数:
sockfd:是一个socket描述符
addr:是指向一个结构为sockaddr参数的指针,sockaddr中包含了地址、端口和IP地址的信息。在进行地址绑定的时候,需要将地址结构中的IP地址、端口、类型等结构struct sockaddr中的域进行设置之后才能进行绑定,这样进行绑定后才能将套接字文件描述符与地址等接合在一起。
现在常用的是struct sockaddr_in这个结构体(使用时需要强转成struct sockaddr类型)。如何查看struct sockaddr_in呢?在/uer/include 目录下查找,使用命令:grep "struct sockaddr_in {" * -nir 去在整个目录下查找。n显示行数,i不区分大小写,r 递归的(包括子目录) *表示当前目录的所有文件。
addrlen:结构体add的大小
返回值
成功返回0 ,失败返回-1,errno被设置
功能:listen函数使用主动连接套接字变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
listen函数一般在调用bind之后-调用accept之前调用。
函数原型
int listen(int sockfd, int backlog);
参数
sockfd:进行监听的套接字
backlog:指定在请求队列中允许的最大请求数
返回值
成功返回0 ,失败返回-1,errno被设置
功能:与客户端建立连接,如果已完成请求的队列为空将阻塞到下一个请求到来。
接受一个客户端的连接请求,并返回一个新的套接字。所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数据来完成的。
函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:监听的套接字
addr :(可选择不使用填NULL)
用于保存已连接的客户端的地址
addrled:(可选择不使用填NULL)
客户端地址长度
返回值:返回一个已连接的新的套接字,交互完成后该套接字就会被关闭。而第一个参数是服务器监听接字描述行。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。
失败返回-1,errno被设置。
功能:向服务端发出连接请求并进行连接
函数原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数
sockfd:与服务端连接的套接字
addr:服务端IP地址与端口号地址结构体指针
addrlen:常设置为 sizeof(struct sockaddr)
返回值
成功返回0 ,失败返回-1,errno被设置
例子:服务端
#include#include #include #include #include #include #include int main(int argc,char **argv) { int s_fd; int c_fd; struct sockaddr_in s_addr; struct sockaddr_in c_addr; char readbuf[128]; char sendbuf[128]; memset(&s_addr,0,sizeof(struct sockaddr_in)); memset(&s_addr,0,sizeof(struct sockaddr_in)); //-------------socket------------------------ s_fd=socket(AF_INET,SOCK_STREAM,0); if(s_fd==-1){ perror("socketn"); } //--------------bind------------------------- s_addr.sin_family=AF_INET; s_addr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],&s_addr.sin_addr); if(bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in))==-1){ perror("bind"); } //---------------listen----------------------- if(listen(s_fd,20)==-1){ perror("listen"); } int addr_size=sizeof(struct sockaddr_in); while(1){ -----------------------accept-------------------------------- c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&addr_size); if(c_fd==-1){ perror("accept"); } printf("client ip:%sn",inet_ntoa(c_addr.sin_addr)); if(fork()==0){ if(fork()==0){ --------------------------------write--------------------------------------- while(1){ memset(sendbuf,' ',sizeof(sendbuf)); gets(sendbuf); write(c_fd,sendbuf,strlen(sendbuf)); } } ---------------------------------read---------------------------------- while(1){ memset(readbuf,' ',sizeof(readbuf)); if(read(c_fd,readbuf,sizeof(readbuf))>0){ printf("client:%sn",readbuf); } } } } return 0; }
客户端:
#include#include #include #include #include #include #include #include int main(int argc,char **argv) { int c_fd; struct sockaddr_in c_addr; char readbuff[128]; char sendbuff[128]; ------------------socket---------------------- c_fd=socket(AF_INET,SOCK_STREAM,0); if(c_fd==-1){ perror("socketn"); } c_addr.sin_family=AF_INET; c_addr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],&c_addr.sin_addr); ---------------------connect---------------------- connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)); if(fork()==0){ ----------------------read--------------------- while(1){ memset(readbuff,' ',sizeof(readbuff)); if(read(c_fd,readbuff,sizeof(readbuff))==-1){ perror("read"); }else{ printf("server:%sn",readbuff); } } } ---------------------write-------------------- while(1){ memset(sendbuff,' ',sizeof(sendbuff)); gets(sendbuff); write(c_fd,sendbuff,strlen(sendbuff)); } return 0; }



