
通信流程 --- 无连接(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 <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h> /* superset of previous */
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
-
- 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 <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h> /* superset of previous */
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
-
- 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,两个都是阻塞函数。所以必须使用多任务来同时处理,可以使用多进程或者多线程来处理。
服务器端

客户端
