#include
int socket(int family,int type,int protocol);
功能
创建一个用于网络通信的socket套接字(描述符)
参数
family:协议族(AF_INET、AF_INET6、PF_PACKET等)
type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)
protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等
返回值:
套接字
特点
创建套接字时,系统不会分配端口
创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的
ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen);
功能:
向to结构体指针中指定的ip,发送UDP数据
参数:
sockfd:套接字
buf: 发送数据缓冲区
nbytes: 发送数据缓冲区的大小
flags:一般为0
to: 指向目的主机地址结构体的指针
addrlen:to所指向内容的长度
返回值:
成功:发送数据的字符数
失败: -1
注意:
通过to和addrlen确定目的地址
可以发送0长度的UDP数据包
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能:将本地协议地址与sockfd绑定
参数:
sockfd: socket套接字
myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度
返回值 :
成功:返回0
失败:其他
ssize_t recvfrom(int sockfd, void *buf,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen);
功能 :
接收UDP数据,并将源地址信息保存在from指向的结构中
参数 :
sockfd:套接字
buf: 接收数据缓冲区
nbytes:接收数据缓冲区的大小
flags: 套接字标志(常为0)
from: 源地址结构体指针,用来保存数据的来源
addrlen: from所指内容的长度
返回值:
成功:接收到的字符数
失败: -1
注意:
通过from和addrlen参数存放数据来源信息
from和addrlen可以为NULL, 表示不保存数据来源
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "192.168.0.108"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, SERVER_IP, &dest_addr.sin_addr);
printf("UDP server %s:%d
", SERVER_IP, SERVER_PORT);
while(1)
{
char send_buf[512] = "";
fgets(send_buf, sizeof(send_buf), stdin);//获取输入
send_buf[strlen(send_buf)-1] = '\0';
sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));//发送数据
}
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "192.168.0.104"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVER_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("server bind port: %d
", SERVER_PORT);
int ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(ret != 0)
{
perror("bind");
close(sockfd);
exit(-1);
}
printf("receive data:
");
while(1)
{
int recv_len;
char recv_buf[512] = "";
struct sockaddr_in client_addr;
char client_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16
socklen_t cliaddr_len = sizeof(client_addr);
recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
//char *client_ip_pr=inet_ntoa(client_addr.sin_addr);
printf("client_ip:%s ,client_port:%d
",client_ip, ntohs(client_addr.sin_port));
printf("data(%d):%s
",recv_len,recv_buf);
}
close(sockfd);
return 0;
}
广播:由一台主机向该主机所在局域网内的所有主机发送数据的方式 ,广播只能用UDP或原始IP实现,不能用TCP
广播的用途:
单个服务器与多个客户主机通信时减少分组流通
地址解析协议(ARP)
动态主机配置协议(DHCP)
网络时间协议(NTP)
广播的特点:
处于同一子网的所有主机都必须处理数据
UDP数据包会沿协议栈向上一直到UDP层
运行音视频等较高速率工作的应用,会带来大负
局限于局域网内使用
UDP广播地址
{网络ID,主机ID}
网络ID表示由子网掩码中1覆盖的连续位
主机ID表示由子网掩码中0覆盖的连续位
定向广播地址:主机ID全1
例:对于192.168.220.0/24,其定向广播地址为 192.168.220.255
通常路由器不会转发该广播
受限广播地址:255.255.255.255
路由器从不转发该广播
套接字选项
int setsockopt(int sockfd, int level,int optname,const void *optval,socklen_t optlen);
功能:
设置套接字选项值
参数:
sockfd:套接字
level: 被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET
optname:准备设置的选项,option_name可以有哪些取值,这取决于level
optval:类型
optlen:
返回值:
成功:0
失败: -1
示例
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "192.168.0.104"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd=0;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
printf("server bind port: %d
", SERVER_PORT);
int opt=1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));
char send_buf[1024]={0};
strcpy(send_buf,"this is broadbast msg
");
int len = sendto(sockfd,send_buf,strlen(send_buf),0,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(len<0)
{
printf("send error
");
close(sockfd);
return -1;
}
printf("send success
");
close(sockfd);
return 0;
}
多播: 数据的收发仅仅在同一分组中进行多播的特点:
多播地址标示一组接口
多播可以用于广域网使用
在IPv4中,多播是可选的
UDP多播地址
IPv4的D类地址是多播地址
十进制:224.0.0.1 - 239.255.255.254
十六进制:E0.00.00.01 - EF.FF.FF.FE 特殊的IP地址多播地址:224.0.0.1:是所有主机组,子网上所有具有多播能力的节点(主机、路由器、打印机)必须在所有具有多播能力的接口上加入该组。224.0.0.2:是所有路由器组,子网上所有多播路由器必须在所有具有多播能力的接口上加入该组。
多播地址分类:
224.0.0.0 -- 224.0.255 链路局部的多播地址,是低级拓扑和维护协议保留的,多播路由器不转发以这些地址为目的地址的数据报
224.0.1.0 -- 224.0.1.255: 为用户可用的组播地址(临时组地址),可以用于 Internet 上的。224.0.2.0 -- 238.255.255.255: 用户可用的组播地址(临时组地址),全网范围内有效239.0.0.0 -- 239.255.255.255: 为本地管理组播地址,仅在特定的本地范围内有效
struct in_addr{
in_addr_t s_addr;
};
struct ip_mreq{
struct in_addr imr_multiaddr;//多播组ip
struct in_addr imr_interface;//将要添加到的多播组ip
};
套接口选项
int setsockopt(int sockfd, int level,int optname,const void *optval, socklen_t optlen);
功能:
设置套接字选项值
参数:
sockfd:套接字
level: 被设置的选项的级别,IPPROTO_IP
optname: IP_ADD_MEMBERSHIP :加入多播组 ,IP_DROP_MEMBERSHIP :离开多播组
optval:类型 ip_mreq{}
optlen:
返回值:
成功:0
失败: -1
为什么要用组播:
单播和组播是寻址方案的两个极端(要么单个、要么全部),多播则是两者之间的一种折中的方案,多播数据报只应该由对它感兴趣的接口接收。另外,广播一般局限于局域网内使用,多播则既可用于局域网,也可跨广域网使用。
(1) 知道服务器的ip、port
(2) 主动连接 服务器
socket : 创建TCP套接字(主动)
connect:连接服务器
send:发送数据到服务器
recv: 接受服务器的响应
close:关闭连接
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
int connect(int sockfd,const struct sockaddr *addr,socklen_t len);
功能:主动跟服务器建立链接
参数:
sockfd:socket套接字
addr: 连接的服务器地址结构
len: 地址结构体长度
返回值:
成功:0 失败:其他
注意:
• connect建立连接之后不会产生新的套接字
• 连接成功后才可以开始传输TCP数据
#include
ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
功能:用于发送数据
参数:
sockfd: 已建立连接的套接字
buf: 发送数据的地址
nbytes: 发送缓数据的大小(以字节为单位)
flags: 套接字标志(常为0)
注意:不能用TCP协议发送0长度的数据包
#include
ssize_t recv(int sockfd, void *buf,size_t nbytes, int flags);
功能:用于接收网络数据
参数:
• sockfd:套接字
• buf: 接收网络数据的缓冲区的地址
• nbytes:接收缓冲区的大小(以字节为单位)
• flags: 套接字标志(常为0)
返回值:成功接收到字节数
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "192.168.0.107"
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
int err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 主动连接服务器
if(err_log != 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
char send_buf[1024] = "";
printf("send data to [%s:%d]
",SERVER_IP,SERVER_PORT);
while(1)
{
printf("send:");
fgets(send_buf,sizeof(send_buf),stdin);
send_buf[strlen(send_buf)-1]='\0';
send(sockfd, send_buf, strlen(send_buf), 0); // 向服务器发送信息
}
close(sockfd);
return 0;
}
做为TCP服务器需要具备的条件
• 具备一个可以确知的地址
• 让操作系统知道是一个服务器,而不是客户端
• 等待连接的到来
对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始
bind用法和UDP服务端使用一样,
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
#include
int listen(int sockfd, int backlog);
功能:
• 将套接字由主动修改为被动
• 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数:
• sockfd: socket监听套接字
• backlog:连接队列的长度
返回值:
• 成功:返回0
• 失败:其他
#include
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
功能:从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)
参数:
• sockfd: socket监听套接字
• cliaddr: 用于存放客户端套接字地址结构
• addrlen:套接字地址结构体长度的地址
返回值:已连接套接字
注意:返回的是一个已连接套接字,这个套接字代表当前这个连接
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 8080
int main(int argc, char *argv[])
{ //创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVER_PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
err_log = listen(sockfd, 10);
if(err_log != 0)
{
perror("listen");
close(sockfd);
exit(-1);
}
printf("listen client port=%d...
",SERVER_PORT);
while(1)
{
struct sockaddr_in client_addr;
char cli_ip[INET_ADDRSTRLEN] = "";
socklen_t cliaddr_len = sizeof(client_addr);
int connfd;
connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);
if(connfd < 0)
{
perror("accept");
continue;
}
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("----------------------------------------------
");
printf("client ip=%s,port=%d
", cli_ip,ntohs(client_addr.sin_port));
char recv_buf[2048] = "";
while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 )
{
printf("
recv data:
");
printf("%s
",recv_buf);
}
close(connfd); //关闭已连接套接字
printf("client closed!
");
}
close(sockfd); //关闭监听套接字
return 0;
}
留言与评论(共有 0 条评论) “” |