P17 UDP编程概述
P18 2.05网络编程接口socket
1、一种文件描述符,代表了通信管道的一个端点
2、可以是文件的操作,read,write, close等等
3、得到socket套接字的方法时调用socket
socket分类
1、SOCK_STREAM,流式套接字
2、SOCK_DGRAM,数据报套接字
3、SOCK_RAM,原始套接字
P19 2.06UDP编程CS架构
流程图:服务器和客户端
服务器:
创建套接字socket()
将服务器的ip地址,端口号与套接字进行绑定bind
接受数据recvfrom
发送数据sendto
客户端
创建socket
发送sendto
接受recvfrom
关闭套接字close
P20 2.07创建套接字
int socket(int domain, int type, int protocol);
功能:返回创建一个套接字
参数:
family:协议族(AF_INET, AF_INET6)
type:套接字类型(SOCK_STREAM,SOCK_DGRAM)
protocol:协议类别(通常写0就行,自动匹配)
特点:
1、创建套接字时,系统不会分配端口
2、创建套接字默认属性时主动的,即主动发起服务的请求;但作为服务器时,往往需要修改为被动
P21 2.08创建udp套接字代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
创建一个用于UDP网络编程的套接字
int fd;
fd = socket(AF_INET,SOCK_DGRAM, 0);
if(fd == -1)
{
perror("");//...
}
printf("%d\n", fd); //打印3
return 0;
}
P22 2.09 IPv4套接字地址结构--注意区分sockaddr和sockaddr_in
UDP编程-发送、绑定、接收数据
头文件:#include<netinet/in.h> struct in_addr { in_addr_t s_addr; } struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; } 为了使不同格式地址能被传入套接字函数,地址必须要强制转换成通用套接字地址结构 struct sockaddr { sa_family_t sa_family; char sa_data[14]; }//通用的结构体
P23 2.10两种地址结构体的使用场合
例如:struct sockaddr_in my_addr;声明结构体,赋值ip,端口号等信息
当调用编程接口函数,且改函数需要传入地址结构时,需要struct_sockaddr进行强制转换
bind(soockfd, (struct sockaddr *)&myaddr, sizeof(myaddr));
P24 2.11sendto函数
因为sendto函数中已经携带了对方的ip地址和端口号,所以可以直接向对方发送数据
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flag, const struct sockaddr *to, socklen_t addrlen)
功能:向to结构体发送udp数据
参数:sockfd
buf
nbytes
flags
to:保存着对方的ip地址和端口号
addrlen
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
功能:向to结构体指针中指定的ip,发送UDP数据
参数:
buf:发送数据缓冲区(发送的内容)
flag=0 一般情况
dest_addr:
addrlen:
返回值:
成功返回发送数据字节数
失败返回-1
P25 2.12向网络调试助手发送数据
网络调试器设置:
客户端代码:
#include <stdio.h>
#include <arpa/inet.h> //htons、inet_addr
#include <sys/socket.h> //socket
#include <string.h>
#include <unistd.h>//close
#include <sys/types.h>
#include <stdlib.h> //exit
int main(int argc, char *argv[])
{
*****************************************************************************************
//第二步:填充服务器网络信息结构体sockaddr_in
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9000);
//server_addr.sin_addr.s_addr = "192.168.1.1"; 这样时错误的
server_addr.sin_addr.s_addr = inet_addr("192.168.1.1");
//因为s_addr是in_addr_t类型,也就是32为无符号整型,不能直接将192.168..直接赋值
inet_pton(AF_INET, "192.168.1.101", &server_addr.s in_addr.s_addr);
**********************************************************************************
//第一步:创建套接字
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
perror("");
*************************************************************************************
//第二步:发送数据
while(1)
{
char buf[128] = {0};
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0';//把buf字符串中的\n转换成\0
sendto(sock_fd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr,sizeof(server_addr));
char read_buf[128];
recvfrom(sock_fd, read_buf, sizeof(read_buf), 0, NULL, NULL);
printf("%s\n",read_buf);
}
close(sock_fd);
return 0;
}
//如果想要通用,传入参数ip地址和端口号即可
//inet_addr(argv[1])
//htons(atoi(argv[2]))先把输入的字符串转换成整型数
if(argc <3)
{
fprintf(stderr, "Useg:%s ip port\n", argv[0]);
exit(1);
}
P26 2.13绑定函数bind
udp网络程序想要获取数据需要什么条件:
确定的ip地址
确定的port
怎么完成上面的条件呢?
接收端 使用bind函数,来完成地址结构与sock套接字的绑定,这样ip、port就固定了
发送端 在sendto函数中指定接收端的ip、port
由于服务器是被动的,客户端是主动的,所以一般先运行服务器,后运行客户端,所以服务器
需要固定自己的信息(ip,port),这样客户端才可以找到服务器并与之通信,但是客户端一般不需要bind,因为系统会自动给客户端分配ip地址和端口号。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:绑定
参数:先填充再传值
返回值:
P27 2.14bind代码(反正我是没办法复现出来)
编写一个服务器的基本代码:
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#defined N 128
//一般服务器都需要执行bind函数
int main(int argc, char *argv[])
{
if(argc <3)
{
fprintf(stderr, "Useg:%s ip port\n", argv[0]);
exit(1);
}
//第一,创建套接字
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("");
exit(-1);
}
//第二,将服务器的网络信息结构体与套接字绑定
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
//第三,将网络信息结构体与套接字绑定
if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1)
{
perror("");
exit(1);
}
//接收数据
char buf[N] = "";
struct sockaddr_in clientaddr;
socklen_t addrlen = sizeof(clientaddr);
if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1) 阻塞再这里了,接收不到消息!!!!!!!!!!!
{
perror("");
exit(1);
}
//可以打印客户端的信息:端口号和ip
printf("ip:%s, port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("from client:%s\n", buf);
return 0;
}
P28 2.15recvfrom函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收udp数据,并将源地址信息保存在from指向的结构体中
参数:sockfd
buf
len
flag
src_addr
addr_len
注意:from和addrlen可以为NULL,不保存信息来源的信息
P29 2.16接收网络调试数据
在调试器设置固定的IP和端口号:
//第三步接收数据
char buf[N] = "";
struct sockaddr_in clientaddr;
socklen_t addrlen = sizeof(struct sockaddr_in)
if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
{
perror("");
exit(1);
}