【linux网络编程】基于UDP的server-client简单实现

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返回值检查的效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值