Linux-C socket网络通信 03.25
socket网络通信 03.25
整体结构类似
依然属于进程间的通信
网络三要素:
IP地址:确定网络中某一台计算机的位置
端口号:确定是哪个应用程序
通信协议:通信时,通信双方预定好的规则
应用程序是静态的,电脑运行起来就是进程
越往上,越偏向软件;越往下,越偏向硬件
数据从应用层开始,每向下一次,会打包一次,直至物理层
TCP:
1、通信时候一定要验证通信双方是否都在线(验证的是,客户端与对应的服务器是否都在线)
2、传输介质:流式IO(byte)
3、理论上,每一次的传输不会限制大小
UDP:
1、不用验证是否在线
2、传输介质:数据报文包
3、每次只能传输64KB
TCP牺牲效率,提高传输的稳定性;UDP牺牲稳定性,提升效率
在大部分使用情况下,TCP连接外网,UDP连接本机;UDP适合在不管对方是否在线的情况下使用
本机IP—网络回环地址:127.0.0.1
ping 127.0.0.1 ping给自己,再自己接收
IPV4:8个字节,
服务器:被动连接
客户端:主动发送请求
SOCKET分类:
•流式套接字(SOCK_STREAM):流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP 保证了数据传输的正确性和顺序性。
•数据报套接字(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。使用数据报协议UDP协议。
•原始套接字(SOCK_RAW):原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。
sockaddr套接字地址结构:
structsockaddr{unsignedshortsa_family;//address族, AF_xxxcharsa_data[14];//14 bytes的协议地址};//sa_family 一般来说, IPV4使用“AF_INET”。//sa_data 包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一起的sockaddr_in地址结构:
structsockaddr_in{shortintsin_family;//Internet地址族unsignedshortintsin_port;//端口号structin_addrsin_addr;//Internet地址unsignedcharsin_zero[8];//添0(和struct sockaddr一样大小)};structin_addr{union{unsignedlongints_addr;}};注:sockaddr和sockaddr_in两个数据类型是等效的,可以相互转换,通常使用sockaddr_in更为方便
sin_port----字节顺序转换函数:
四个函数的功能:网络字节顺序与本地字节顺序之间相互转换
•htons()——“Host to Network Short” –主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes)
• htonl()——“Host to Network Long” –主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes)
• ntohs()——“Network to Host Short”–网络字节顺序转换为主机字节顺序(对无符号短型进行操作2bytes)
• ntohl()——“Network to Host Long ”–网络字节顺序转换为主机字节顺序(对无符号长型进行操作4bytes)
之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO。
网络字节顺序NBO(Network Byte Order):
按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题(大端:低位存放在高字节)。
主机字节顺序(HBO,Host Byte Order):
不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关(小端:低位存放在低字节)。
如 Intel x86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12 。
如IBM power PC结构下,short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78。
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序 。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。
socket()函数:
功能:为了执行I/O,服务端和客户端必须先调用socket(),产生TCP套接字,作为TCP通信的传输端点;
返回:调用成功,则返回一个较小的非负整数,失败则返回-1;
intsocket(intfamily,inttype,intprotocol);//指明协议族family=AF_INET;//IPv4协议;family=AF_INET6;//IPv6协议;family=AF_ROUTE;//路由套接口。//指明产生套接字的类型type=SOCK_STREAM;//字节流套接口,TCP常用的形式;type=SOCK_DGRAM;//数据报套接口,UDP常用的形式 ;type=SOCK_RAW;//原始套接口//protocol是协议标志,一般在调用socket()时将其设置为0。(但如果是原始套接字,就需要为其指定一个常值)bind()函数
功能:为调用socket()函数产生的套接字(socketfd)分配一个本地协议地址,建立地址与套接字(socketfd)的对于关系。
返回:该函数调用成功返回0,若出错则返回-1,并置错误号errno;
intbind(intsockfd,conststructsockaddr*server,socklen_len addrlen);//sockfd参数是套接字函数返回的套接字描述符;//server参数是指向特定于协议的地址结构的指针,指定用于通信的本地协议地址;//addrlen参数是指定该套接字地址结构的长度;listen()函数
功能:将未连接的套接字转换成被动套接字,使它处在监听模式下,指示内核应接受发向该套接字(socketfd)的连接请求。
返回:调用成功返回0,若出错则返回-1,并置errno值;
intlisten(intsockfd,intbacklog);//sockfd参数是要设置的描述符;//backlog参数规定了请求队列中的最大连接数;accept()函数
功能:使服务器接受客户端的连接请求。函数由 TCP 服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被进入睡眠。
返回:调用成功:
(1)accept()函数的返回值,已连接套接字描述符;
(2)addr参数参会客户端的协议地址,包括IP地址和端口号等;
(3)addrlen参数返回客户端地址结构的大小。
如果调用失败则返回-1,并置errno值。
intaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);//sockfd参数是由sockfd()函数产生的套接字描述符,在调用accept()函数前,已经调用listen()函数将此套接字变成了监听套接字;//addr参数是用来返回连接对方(客户端)的套接字地址结构;//addrlen参数是用来返回连接对方的套接字的结构长度connect()函数
功能:让TCP客户端使用来配置套接字,建立一个与TCP服务器的连接。(激发TCP的三次握手过程)
返回:调用成功后返回0,若出错则返回-1;
intconnect(intsockfd,conststructsockaddr*addr,socklen_t addrlen);//sockfd参数是有sockd()函数返回的套接字描述符;//addr参数是指向服务器的套接字地址结构的指针;//addrlen参数是该套接字地址结构的大小close()函数
功能:关闭套接字,并立即返回到进程。
intclose(intsockfd);特殊文件描述符号:
•标准输入STDIN_FILENO
•标准输出STDOUT_FILENO
•标准错误STDERR_FILENO
每个进程被加载后,默认打开0,1,2这三个文件描述符
htons()
头文件:#include <arpa/inet.h>
unsignedshortinthtons(unsignedshortinthostshort);Server
#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>using namespace std;intmain(){structsockaddr_ins_addr;intlen=0;charbuf[50]={0};intsocketfd=0;intacceptfd=0;intclient_num=0;socketfd=socket(AF_INET,SOCK_STREAM,0);if(socketfd==-1){perror("socket error");}else{s_addr.sin_family=AF_INET;//ipv4s_addr.sin_addr.s_addr=INADDR_ANY;//Internet地址s_addr.sin_port=htons(10086);len=sizeof(s_addr);if(bind(socketfd,(structsockaddr*)&s_addr,len)==-1){perror("bind error");}sleep(1);if(listen(socketfd,10)==-1)//监听,判断 请求队列中的最大连接数为10{perror("listen error");}cout<<"ready OK"<<endl;//网络通道准备好了while(1)//死循环,保证服务器长时间运行{cout<<"wait"<<endl;acceptfd=accept(socketfd,NULL,NULL);//等待客户端上线,阻塞式函数,没有响应,会一直在//返回客户端的IP地址cout<<"client line succuss --- acceptfd="<<acceptfd<<endl;//网络只能从4开始,0 1 2分别表示操作系统标准输入、输出、报错;3是文件IO操作时候文件描述符号client_num++;if(!fork())//开辟子进程进行后续工作{while(1){read(acceptfd,buf,sizeof(buf));printf("Get msg from client%d: %s\n",client_num,buf);bzero(buf,sizeof(buf));}}}}close(socketfd);return0;}Client
#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<iostream>#include<stdio.h>#include<string.h>using namespace std;intmain(){structsockaddr_ins_addr;intlen=0;intsocketfd=0;charbuf[50]={0};//socketfd=socket(AF_INET,SOCK_STREAM,0);if(socketfd==-1){perror("socket error");}else{s_addr.sin_family=AF_INET;s_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//本机回环ip地址即虚拟机ip 192.168.136.0s_addr.sin_port=htons(10086);len=sizeof(s_addr);//客户端向服务器发送连接请求if(connect(socketfd,(structsockaddr*)&s_addr,len)==-1){perror("connect error");}else{cout<<"connect success"<<endl;while(1){cin>>buf;write(socketfd,buf,sizeof(buf));}}}close(socketfd);return0;}桥接与NAT连接:
虚拟机网络在桥接连接方式下,虚拟机的ip为主机下的网段,若主机ip地址改变,则虚拟机ip也会改变
虚拟机网络在NAT连接方式下,会生成始终固定的局域网段,而虚拟机的ip为网段中的一个ip地址,因此NAT连接相比桥接,有优点是虚拟机的ip不受系统ip地址变化而变化
