华清远见25072班网络编程学习day2
重点内容:
一、套接字通信
socket函数:
int socket(int domain, int type, int protocol);
功能:为通信方提供一个通信端点,并返回控制该端点的文件描述符(最小未使用原则)
参数1:通信域(使用系统提供的宏值)
AF_UNIX, AF_LOCAL 同一主机间的多个进程的通信 详情请看 man 7 unix
AF_INET 提供ipv4的通信协议 详情请看 man 7 ip
AF_INET6 提供ip v6的通信协议 详情请看 man 7 ipv6
参数2:通信语义(通信方式)
SOCK_STREAM 提供TCP的通信方式
SOCK_DGRAM 提供UDP的通信方式
参数3:如果参数2中唯一指定的一种通信方式,那么该参数填0
返回值:成功返回一个新的用于控制套接字的文件描述符,失败返回-1并置位错误码
eg:
创建TCP通信端点:int sfd = socket(AF_INET, SOCK_STREAM, 0)
创建UDP通信端点:int sfd = socket(AF_INET, SOCK_DGRAM, 0)
二、基于TCP的通信
原理图
bind函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:为套接字绑定地址空间
参数1:要被绑定的套接字文件描述符
参数2:是一个通用地址信息结构体,该结构体的目的是为了将实际的结构体地址强制类型转换,防止编译器警告的,实际的结构体取决于地址族
对于AF_UNIX、AF_LOCAL:而言
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
对于AF_INET而言:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* 端口号的网络字节序 */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr { uint32_t s_addr; /* ip地址的网络字节序 */ };
参数3:就是参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
listen函数
int listen(int sockfd, int backlog
功能:将套接字设置成监听状态,注意,只有面向连接的套接字,才能启动监听
参数1:要被设置成监听的套接字文件描述符
参数2:未连接队列的大小,一般为128,也就是一次性处理连接的个数
返回值:成功返回0,失败返回-1并置位错误码
accept函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:阻塞等待客户端的连接请求,如果有新的客户端发来连接请求,那么会解除阻塞,并创建一个用于通信的套接字文件描述符
参数1:服务器的套接字文件描述符
参数2:用于接收客户端地址信息的结构体,传递一个结构体变量的地址,如果不想接收对方地址信息,填NULL即可
参数3:用于接收对方地址信息结构体的大小,如果参数2填NULL,那么参数3也是NULL
返回值:成功返回一个创建出来的用于通信的套接字文件描述符(最小未分配原则),失败返回-1并置位错误码
connect函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:将套接字连接到对端
参数1:套接字文件描述符
参数2:对端地址信息结构体
参数3:参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
三、基于UDP通信
原理图
sendto函数
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
功能:从sockfd文件描述符中发送buf作为起始地址的消息,一共发送len的长度,给dest_addr指向的地址中
参数1:发送消息端的套接字文件描述符
参数2:要发送的消息的起始地址
参数3:要发送的数据长度
参数4:是否以阻塞的形式发送 0:表示阻塞 MSG_DONTWAIT:表示非阻塞
参数5:要发送的数据对端地址信息结构体
参数6:参数5的大小
返回值:成功返回本次发送的字节数,失败返回-1并置位错误码
recvform函数:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
功能:从sockfd套接字文件描述符中读取长度为len的数据,放入到buf作为起始地址的容器中,并且可以接收对端地址信息
参数1:接收消息的套接字文件描述符
参数2:存放接收消息的容器的起始地址
参数3:参数2的大小
参数4:是否阻塞接受 0:表示阻塞 MSG_DONTWAIT:表示非阻塞
参数5:用于接受对端地址信息的结构体
参数6:参数5的大小
返回值:
如果成功接收了消息,那么返回的是消息的总字节数,当然也可以接收长度为0的字符串,并不代表这对端已经下线
如果以非阻塞形式接受消息,但是并没有任何消息收到时,也会返回0 失败返回-1并置位错误码
作业:
1> TCP和UDP通信
TCP通信:
程序源码:
01tcpser.c
#include <25072head.h>
#define SER_IP "192.168.153.128" //服务器ip地址
#define SER_PORT 8888 //服务器端口号
int main(int argc, const char *argv[])
{
//1.创建一个用于连接的套接字文件描述符
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
printf("sfd=%d\n",sfd);
//2.给套接字绑定ip地址和端口号
//2.1准备地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET; //通信域地址族
sin.sin_port=htons(SER_PORT); //服务器端口号
sin.sin_addr.s_addr=inet_addr(SER_IP); //服务器ip地址//2.2执行绑定工作
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
//3.将套接字设置成被动监听状态
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
printf("listen success\n");
//4.阻塞等待客户端的连接
//4.1定义地址信息结构体,用于接收客户端地址信息
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd==-1)
{
perror("accept error");
return -1;
}
printf("[%s:%d]-连接成功了newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
//5.实现数据的收发
char buf[128]="";
while(1)
{
//清空buf
bzero(buf,sizeof(buf));
//从套接字中读取消息
int res=read(newfd,buf,sizeof(buf));
if(res==0)
{
printf("客户端已下线\n");
close(newfd);
return -1;
}
printf("[%s:%d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
strcat(buf,"*_*");
write(newfd,buf,strlen(buf));
}
close(sfd);
return 0;
}
02tcpcli.c
#include <25072head.h>
#define SER_IP "192.168.153.128" //服务器ip地址
#define SER_PORT 8888 //服务器端口号
#define CLI_IP "192.168.153.128" //服务器ip地址
#define CLI_PORT 7777 //服务器端口号int main(int argc, const char *argv[])
{
//1.创建一个用于连接的套接字文件描述符
int cfd=socket(AF_INET,SOCK_STREAM,0);
if(cfd==-1)
{
perror("socket error");
return -1;
}
//2.给客户端套接字绑定ip地址和端口号
//2.1填充地址信息结构体
struct sockaddr_in cin;
cin.sin_family=AF_INET;
cin.sin_addr.s_addr=inet_addr(CLI_IP);
cin.sin_port=htons(CLI_PORT);
if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))==-1)
{
perror("bind error");
return -1;
}
//3.链接服务器
//3.1组装对端地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET; //通信域地址族
sin.sin_port=htons(SER_PORT); //服务器端口号
sin.sin_addr.s_addr=inet_addr(SER_IP); //服务器ip地址
//3.2连接操作
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("connect error");
return -1;
}
//4.数据通信
char buf[128]="";
while(1)
{
//从终端输入要发送的消息
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
//判断输入内容
if(strcmp(buf,"quit")==0)
{
break;
}
//将消息发送给服务器
write(cfd,buf,strlen(buf));
printf("发送成功\n");
//接受服务器发来的消息
bzero(buf,sizeof(buf));
int res=read(cfd,buf,sizeof(buf));
if(res==0)
{
printf("服务器已下线\n");
break;
}
printf("收到服务器消息:%s\n",buf);
}
//5.关闭套接字
close(cfd);
return 0;
}
UDP通信:
03udpser.c
#include <25072head.h>
#define SER_IP "192.168.153.128" //服务器ip地址
#define SER_PORT 8888 //服务器端口号
int main(int argc, const char *argv[])
{
//1.创建一个用于通信的套接字文件描述符
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
//2.给套接字绑定ip地址和端口号
//2.1封装地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET; //通信域地址族
sin.sin_port=htons(SER_PORT); //服务器端口号
sin.sin_addr.s_addr=inet_addr(SER_IP); //服务器ip地址//2.2执行绑定工作
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
//3.数据收发
char buf[128]="";
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
while(1)
{
//清空容器
bzero(buf,sizeof(buf));
recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen);
printf("[%s:%d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
strcat(buf,"*_*");
sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&cin,sizeof(cin));
printf("发送成功\n");
}
//关闭套接字
close(sfd);return 0;
}
04udpcli.c
#include <25072head.h>
#define SER_IP "192.168.153.128"
#define SER_PORT 8888
int main(int argc, const char *argv[])
{
//1.创建一个用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}//2.绑定ip和端口号(可选)
//3.数据收发
char buf[128] = ""; //数据容器
struct sockaddr_in sin; //目标地址
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
socklen_t addrlen = sizeof(sin);
while(1)
{
//清空容器
bzero(buf, sizeof(buf));
fgets(buf, sizeof(buf), stdin); //从终端获取数据
buf[strlen(buf)-1] = 0;
if(strcmp(buf,"quit")==0)
{
break;
}
//将数据发送给服务器
sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));
printf("发送成功\n");
//接收服务器发来的消息
bzero(buf, sizeof(buf));
//recvfrom(cfd, buf, sizeof(buf), 0, (struct socket*)&sin, &addrlen);
//recvfrom(cfd, buf, sizeof(buf), 0, NULL,NULL);
read(cfd, buf, sizeof(buf));
printf("收到服务器消息为:%s\n", buf);
}
//4.关闭套接字
close(cfd);
return 0;
}
2> 牛客网
3>思维导图