1 server端思路
(1)lfd = socket(AF_INET, STREAM, 0); SOCK_DGRAM --- 报式协议。
(2)bind();
(3)listen(); --- 可有可无
while(1){
(4)read(cfd, buf, sizeof) --- 被替换 --- recvfrom() --- 涵盖accept传出地址结构。
小-- 大
write();--- 被替换 --- sendto()---- connect
}
close();
查看recvfrom函数原型
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:(struct sockaddr *)&addr 传出。 对端地址结构
// addrlen:传入传出。
// 返回值: 成功接收数据字节数。 失败:-1 errn。 0: 对端关闭。
2 client端思路
(1)connfd = socket(AF_INET, SOCK_DGRAM, 0);
(2)sendto(‘服务器的地址结构’, 地址结构大小)
(3)recvfrom()
(4)写到屏幕
(5)close();
查看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:数据长度
// flags: 0
// src_addr:(struct sockaddr *)&addr 传入。 目标地址结构
// addrlen:地址结构长度。
// 返回值:成功写出数据字节数。 失败 -1, errno
server.c实现
/*************************************************************************
> File Name: udp_server.c
> Author: Winter
> Created Time: 2022年07月10日 星期日 15时09分35秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ctype.h>
#include <fcntl.h>
#define SERVER_PORT 9527
void sys_error(const char* str) {
perror(str);
exit(1);
}
int main(int argc, char* argv[])
{
// 服务器地址结构
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
// memset(&server_addr, 0, sizeof(server_addr));
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
char buf[BUFSIZ];
char client_ip[BUFSIZ];
// 1创建socket
int lfd = socket(AF_INET, SOCK_DGRAM, 0);
if (lfd == -1) {
sys_error("socket error");
}
// 端口复用
int opt = 1;
int rr = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void*)(&opt), sizeof(opt));
// int rr = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (rr == -1) {
sys_error("setsockopt error");
}
// 2绑定服务器地址结构
int res = bind(lfd, (struct sockaddr*)(&server_addr), sizeof(server_addr));
if (res == -1) {
sys_error("bind server error");
}
printf("accept client......\n");
while (1) {
// 接收客户端信息
client_addr_len = sizeof(client_addr);
int n = recvfrom(lfd, buf, BUFSIZ, 0, (struct sockaddr*)(&client_addr), &client_addr_len);
if (n == -1) {
sys_error("recvfrom error");
}
if (n == 0) {
printf("the %d client has been closed......\n", lfd);
}
// 输出客户端信息
printf("reveiced from the %d client, ip = %s, port = %d\n", lfd,
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)),
ntohs(client_addr.sin_port)
);
// 输出到屏幕
write(STDOUT_FILENO, buf, n);
// 小写转大写
for (int k = 0; k < n; k++) {
buf[k] = toupper(buf[k]);
}
// 输出到屏幕
write(STDOUT_FILENO, buf, n);
// 回发给客户端
n = sendto(lfd, buf, n, 0, (struct sockaddr*)(&client_addr), sizeof(client_addr));
if (n == -1) {
sys_error("sendto error");
}
}
close(lfd);
return 0;
}
client.c
/*************************************************************************
> File Name: udp_client.c
> Author: Winter
> Created Time: 2022年07月10日 星期日 15时09分44秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#define SERVER_PORT 9527
void sys_perror(const char* str) {
perror(str);
exit(1);
}
int main(int argc, char* argv[])
{
char buf[BUFSIZ];
int n = 0;
// 1创建socket
int lfd = socket(AF_INET, SOCK_DGRAM, 0);
if (lfd == -1) {
sys_perror("socket error");
}
// 服务器地址结构
struct sockaddr_in server_addr;
// memset(&server_addr, 0, sizeof(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, "127.0.0.1", &server_addr.sin_addr.s_addr);
// 绑定地址结构
int res = bind(lfd, (struct sockaddr*)(&server_addr), sizeof(server_addr));
// if (res == -1) {
// sys_perror("bind client error");
// }
// 从键盘读取数据
while (fgets(buf, BUFSIZ, stdin) != NULL) {
// 向服务器发送数据
n = sendto(lfd, buf, strlen(buf), 0, (struct sockaddr*)(&server_addr), sizeof(server_addr));
if (n == -1) {
sys_perror("sendto error");
}
// 从客户端接收信息
n = recvfrom(lfd, buf, BUFSIZ, 0, NULL, 0);
if (n == -1) {
sys_perror("recvfrom error");
}
// 写在客户端
write(STDOUT_FILENO, buf, n);
}
close(lfd);
return 0;
}
这段代码有bug,当不检查client.c中bind函数的返回值时,client可以运行,但是加上返回值检查就是出现端口已用
不加bind返回值检查的效果
加bind返回值检查的效果