通信流程 --- 无连接(connect accept)的过程
UDP 无法判断客户端是否退出:
使用心跳包: 使用客户端, 定时给服务器发送内容
udp流程:(类似发短信)
server:
创建数据报套接字(socket(,SOCK_DGRAM,))----->有手机
绑定网络信息(bind())-----------> 绑定IP和port(发短信知道发给谁)
接收信息(recvfrom())------------>接收信息,同时可以获取到发送者的IP和port
关闭套接字(close())-------------->接收完毕
client:
创建数据报套接字(socket())----------------------->有手机
指定服务器的网络信息------------------------------>有对方号码
发送信息(sendto())---------------------------->发送短信,根据填充的结构体信息
关闭套接字(close())--------------------------->发送完
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据
参数:
sockfd:套接字描述符
buf:接收缓存区的首地址
len:接收缓存区的大小
flags:0
src_addr:发送端的网络信息结构体的指针
addrlen:发送端的网络信息结构体的大小的指针
返回值:
成功接收的字节个数
失败:-1
0:客户端退出
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据
参数:
sockfd:套接字描述符
buf:发送缓存区的首地址
len:发送缓存区的大小
flags:0
src_addr:接收端的网络信息结构体的指针
addrlen:接收端的网络信息结构体的大小
返回值:
成功发送的字节个数
失败:-1
注意:
1、对于TCP是先运行服务器,客户端才能运行。
以下内容面试可能会问: 感兴趣可以自己测试一下
4、UDP,客户端当使用send的时候,上面需要加connect,,这个connect不是代表连接的作用,而是指定客户端即将要发送给谁数据。这样就不需要使用sendto而用send就可以。
5、在TCP里面,也可以使用recvfrom和sendto,使用的时候将后面的两个参数都写为NULL就OK。
/*服务器创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc < 2)
{
printf("plase input \n");
return -1;
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.绑定 ip+port 填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
socklen_t len = sizeof(saddr); //结构体大小
//bind绑定ip和端口
if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
{
perror("bind err");
return -1;
}
printf("bind success\n");
char buf[128] = {0};
while (1)
{
//接收信息
if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
{
perror("recvfrom err");
return -1;
}
printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),buf);
//发送信息
printf("server:");
fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
{
break;
}
if (buf[strlen(buf)] == '\0')
{
buf[strlen(buf) - 1] = '\0';
}
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
}
close(sockfd);
return 0;
}
/*客户端创建代码 */
#include
#include /* See NOTES */
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if(argc<3)
{
printf("plase input ")
}
//1.创建套接字,用于链接
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(saddr); //结构体大小
char buf[128] = {0};
int ret;
while (1)
{
//发送信息
printf("client:");
fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
{
break;
}
if (buf[strlen(buf)] == '\0')
{
buf[strlen(buf) - 1] = '\0';
}
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
//接受信息
if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
{
perror("recvfrom err");
return -1;
}
printf("server buf:%s\n", buf);
}
close(sockfd);
return 0;
}
练习:实现如客户端发送"hello"给服务器端,服务器接着给客户端回,"recv:hello!!!!!"。
作业:熟练记忆TCP\UDP客户端服务器,标准为可以默写出来。
利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件,服务也可以自己发送通知给所有客户端。
UDP客户端不会直接互连,所以不会获知其它客户端地址,所有客户端地址存储在服务器端。
数据结构可以选择线性数据结构
链表节点结构体:
struct node{
struct sockaddr_in addr;//data memcmp
struct node *next;
};
消息对应的结构体(同一个协议)
typedef struct msg_t
{
int type;//'L' C Q enum un{login,chat,quit};
char name[32];//用户名
char text[128];//消息正文
}MSG_t;
int memcmp(void *s1,void *s2,int size)
功能:比较两个空间内的值是否完全相同
客户端不f仅需要读取服务器消息,而且需要发送消息。读取需要调用recvfrom,发送需要先调用fgets,两个都是阻塞函数。所以必须使用多任务来同时处理,可以使用多进程或者多线程来处理。
服务器端
客户端