模型
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>//服务器:socket()//创建socketstruct sockaddr_in//准备通信地址bind()//绑定socket和addr sendto()/recvfrom //进行通信close() //关闭socket//客户端:socket()//创建socket://准备通信地址:服务器的地址sendto()/recv() //进行通信:close() //关闭socket:socket()
//创建网络端点,返回socket文件描述符,失败返回-1设errnoint socket(int domain, int type, int protocol);domain :协议族(protocol family)(网络通讯(IP)还是本地通讯(xxx.socket))
- AF_INET用于实现给予ipv4网络协议的网络协议
type :协议(TCP还是UDP) - SOCK_DGRAM //数据报套接字, 实现包括但不限于UDP协议, which is不可靠,无连接的数据报通信方
protocol: 特殊协议, 一般给0
准备通信地址:
struct sockaddr{//主要用于函数的形参类型, 很少定义结构体变量使用, 叫做通用的通信地址类型//$man bindsa_family_t sa_family;charsa_data[14];}struct sockaddr_in{ //准备网络通信的通信地址 //$man in.hsa_family_t sin_family; //协议族, 就是socket()的domain的AF_INETin_port_t sin_port; //端口号struct in_addrsin_addr; //IP地址,//当前网段的最大ip地址是广播地址,即,xxx.xxx.xxx.255。//255.255.255.255在所有网段都是广播地址}struct in_addr{ in_addr_t s_addr; //整数类型的IP地址}bind()
//把通信地址和socket文件描述符绑定,用在服务器端,成功返回0,失败返回-1设errnoint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd: socket文件的fd(returned by socket())
addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
addrlen: 通信地址的大小, 使用sizeof();
sendto()
//向指定的socket和相应的地址发送消息,成功返回实际发送数据的大小,失败返回-1设errnossize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);dest_addr:目标地址(收件人信息)
addrlen: 目标地址的大小
ANote- send(sockfd, buf, len, flags);等价于 sendto(sockfd, buf, len, flags, NULL, 0);
- recv()/ send()表示通过sockfd收发数据, 因为tcp下, 收发之前sockfd已经和相应的地址连接了,所以不需要指定收发谁的/给谁, 但是udp因为收发时没有连接, 所以需要指定
recvfrom()
//从指定的socket和相应的地址接受消息,并提供来电显示的功能,成功返回实际接收的数据大小,失败返回-1设errnossize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);src_addr: 结构体指针, 用于保存数据发送方的通信地址
addrlen: 指针类型, 用于保存发送方的地址大小Note:
- recv(sockfd, buf, len, flags); 等价于 recvfrom(sockfd, buf, len, flags, NULL, 0);
- accept() and recvfrom() 后面的参数是用来提供来电显示的
- 一个server对应多个client
- server可以不知道client的地址, 但是client得知道server的地址
- send data 一定要知道对方地址
- receive data不需要知道对方地址
- TCP/IP的socket都是SOCK_STREAM的,全程连接,通过socket就能找到对方地址, send data的话,直接丢给socket就行
- UDP/IP的socket是SOCK_DGRAM的,不全程连接,不能通过socket找到对方,send data的话,server中需要使用recvfrom()来知道client的地址, 所以肯定要sendto();client本来就知道server的地址, 直接sendto()
- recvfrom()的唯一意义就是在udp-server中配合sendto()使用
- 因为不能通过socket找到对方, 只要是udp发消息, 就得通过sendto()
| | server | client |
| TCP/IP | send();recv() | send();recv() |
| UDP/IP | recvfrom();sendto() | recv();sendto() |
例子-一对一的upd/ip协议的服务器模型
//udp/ip server 五步走#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>int main(){//1. 创建socketint sockfd=socket(AF_INET,SOCK_DGRAM,0);if(-1==sockfd)perror("socket"),exit(-1);//2. 准备通信地址struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(8888);addr.sin_addr.s_addr=inet_addr("176.43.11.211");//3. 绑定socket和通信地址int res=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));if(-1==res)perror("bind"),exit(-1);printf("bind success
");//4. 进行通信char buf[100]={0};struct sockaddr_in recv_addr; //为使用recvfrom得到client地址做准备, 最终为sendto()做准备socklen_t len=sizeof(recv_addr);res=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&recv_addr,&len);if(-1==res)perror("recvfrom"),exit(-1);char* ip=inet_ntoa(recv_addr.sin_addr); //将recvfrom获得client地址转换成点分十进制字符串printf("data received from client :%s is:%d
",ip,res); res=sendto(sockfd,"I received",sizeof("I received"),0,(struct sockaddr*)&recv_addr,len);//使用recvfrom获得的client地址if(-1==res)perror("sendto"),exit(-1);//5. 关闭socketres=close(sockfd);if(-1==res)perror("close"),exit(-1);printf("close success
");return 0;}//udp/ip client#include<stdio.h>#include<stdlib.h>#include<unistd.h>//close()#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>int main(){int sockfd=socket(AF_INET,SOCK_DGRAM,0);if(-1==sockfd)perror("socket"),exit(-1);printf("create socket succesfully
");struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(8888);addr.sin_addr.s_addr=inet_addr("176.43.11.211");//这个是server的地址, 虽然没有connect, which means 不能通过socket找到这个地址, 但是我们还是知道这个地址的, sendto()是可以直接用的int res=sendto(sockfd,"hello",sizeof("hello"),0,(struct sockaddr*)&addr,sizeof(addr));if(-1==res)perror("sendto"),exit(-1);printf("data sent size:%d
",res);char buf[100]={0};res=recv(sockfd,buf,sizeof(buf),0);if(-1==res)perror("recv"),exit(-1);printf("data received from server:%s
",buf);res=close(sockfd);if(-1==res)perror("close"),exit(-1);return 0;}本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-11/136763.htm