TCP、UDP 网络编程

TCP、UDP 网络编程

实验目的

1.使用 TCP 进行套接字通信
2.使用 UDP 进行套接字通信 实验原理

1.TCP

在这里插入图片描述

2.UDP

在这里插入图片描述

3.用到的 API

(1)int socket(int domain, int type, int protocol);
根据指定的地址族、数据类型和协议来分配一个 socket 的描述字及其所用的资源。 domain:协议族,常用的有 AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE 其中 AF_INET 代表使用 ipv4 地址
type:socket 类型,常用的 socket 类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、
SOCK_SEQPACKET 等
protocol:协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC 等
(2)int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把一个地址族中的特定地址赋给 socket
sockfd:socket 描述字,也就是 socket 引用
addr:要绑定给 sockfd 的协地址
addrlen:地址的长度
通常服务器在启动的时候都会绑定一个众所周知的地址(如 ip 地址+端口号),用于提供服务,客 户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的 ip 地 址组合。这就是为什么通常服务器端在 listen 之前会调用 bind(),而客户端就不会调用,而是在 connect()时由系统随机生成一个
(3)int listen(int sockfd, int backlog);
监听 socket
sockfd:要监听的 socket 描述字
backlog:相应 socket 可以排队的最大连接个数
(4)int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
连接某个 socket
sockfd:客户端的 socket 描述字
addr:服务器的 socket 地址
addrlen:socket 地址的长度
(5)int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
TCP 服务器监听到客户端请求之后,调用 accept()函数取接收请求
sockfd:服务器的 socket 描述字
addr:客户端的 socket 地址
addrlen:socket 地址的长度
(6)ssize_t read(int fd, void *buf, size_t count);
读取 socket 内容
fd:socket 描述字
buf:缓冲区
count:缓冲区长度
(7)ssize_t write(int fd, const void *buf, size_t count); 向 socket 写入内容,其实就是发送内容
fd:socket 描述字
buf:缓冲区
count:缓冲区长度
(8)int close(int fd);
socket 标记为以关闭 ,使相应 socket 描述字的引用计数-1,当引用计数为 0 的时候,触发 TCP 客户 端向服务器发送终止连接请求。

实验内容

1.TCP

(1)server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define LISTENQ 10 // 可排队的最大连接数
int main() {
//TCP、UDP都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议
	int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//AF_INNT Ipv4地址 创建套接字
	struct sockaddr_in addr; 
	addr.sin_family = AF_INET; //使用IPv4地址
	addr.sin_port = htons(PORT); //绑定端口PORT
 	// serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
	addr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY 任意地址
	//将套接字绑定到 固定IP和固定端口
	bind(fd, (struct sockaddr*)&addr, sizeof(addr));//绑定  进行端口和IP地址的绑定
	// 将套接字设置为监听模式,等待连接到来	
	listen(fd, LISTENQ);//监听  //监听队列为LISTENQ

	int fd_;
	struct sockaddr_in addr_;
	int len = sizeof(addr_);
	int cnt = 0;
	while(1){
		printf("等待连接.......\n");
		if ((fd_ = accept(fd, (struct sockaddr*)&addr_, &len)) < 0) {//调用 accept 接受请求
			printf("error\n");
		}
		else{
			char read_buf[1024] = { 0 };
			char *write_buf = "Hello,客户端 client!";
			int recvlen = 0;
			cnt ++;
			printf("success:%d\n", cnt);
			// 通过read()、write()接受和发送数据
/*
			read(fd_, read_buf,sizeof(read_buf));
			printf("%s\n", read_buf);
			write(fd_, write_buf,strlen(write_buf));
*/
			// 通过recv()、send()接受和发送数据,接收并打印客户端数据

			if( ( recvlen = recv(fd_, read_buf, sizeof(read_buf), 0) ) > 0){
				printf("%s\n", read_buf);	
			}
			// 发送服务器端数据
			send(fd_, write_buf, strlen(write_buf), 0);
		}
	}


	// 关闭连接
	close(fd_);
	close(fd);
	return 0;
}
(2)client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 11111
#define IP "127.0.0.1"

int main() {
	int fd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字 流格式套接字(SOCK_STREAM)
	// SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
	//向服务器(特定的IP和端口)发起请求
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;  //使用IPv4地址
	addr.sin_port = htons(PORT); //端口
	addr.sin_addr.s_addr = inet_addr(IP);  //具体的IP地址
	//inet_pton(AF_INET, IP, &(addr.sin_addr.s_addr)); //访问的服务器IP   具体的IP地址
	// 向服务器发出连接请求 
	printf("发起连接.......\n");
	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {//建立连接 
		printf("error\n");

	}
	else {
		char read_buf[1024] = { 0 };
		char *write_buf = "Hello,服务器 server!";
		int recvlen = 0;
		printf("success\n");
		// 通过write()、read()发送和接受数据

/*		write(fd, write_buf, strlen(write_buf));
		read(fd, read_buf, sizeof(read_buf) -1 );
		printf("%s\n", read_buf);

*/

		// 通过recv()、send()发送和接受数据,接收并打印客户端数据
		send(fd, write_buf,  strlen(write_buf), 0);
		// 接收并打印客户端数据
		if( ( recvlen = recv(fd, read_buf, sizeof(read_buf), 0) )){
			printf("%s\n", read_buf);	
		}	
	}

	//关闭套接字
	close(fd);
	return 0;
}

2.UDP

(1)server_.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define BUFFSIZE 1024 
int main() {
	int fd = socket(AF_INET, SOCK_DGRAM, 0);//参数和 tcp 不同
	struct sockaddr_in addr; 
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	addr.sin_addr.s_addr = INADDR_ANY;
	bind(fd, (struct sockaddr*)&addr, sizeof(addr));//无需 listen 
	char buff[BUFFSIZE];
	socklen_t len = sizeof(addr);
	int cnt = 0;
	//接收数据,把数据原封不动发回给客户
	while(1){
		int n = recvfrom(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr, &len); 
		if( n == 0 ){
			break;
		}
		cnt ++;
		sprintf(buff, "%s:%d", buff, cnt);
		sendto(fd, buff, n, 0, (struct sockaddr*)&addr, len);
	}
	
	close(fd);

	return 0;
}
(2)client_.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 11111
#define BUFFSIZE 1024 
int main() {
	int fd = socket(AF_INET, SOCK_DGRAM, 0); // 数据报格式套接字(SOCK_DGRAM)
	// 数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字
	struct sockaddr_in addr;
	addr.sin_family = AF_INET; 
	addr.sin_port = htons(PORT);
	addr.sin_addr.s_addr = htonl(INADDR_ANY); 
	char buff[BUFFSIZE] = "服务器被连接的次数:";
	struct sockaddr_in addr_; 
	socklen_t len = sizeof(addr);

	//发送数据,打印从服务器发回的数据
	sendto(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr, len); 
	recvfrom(fd, buff, BUFFSIZE, 0, (struct sockaddr*)&addr_, &len); 

	printf("%s\n", buff);
	close(fd); 

	return 0;
}

实验结果

TCP:
在这里插入图片描述
在这里插入图片描述

UDP:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Hello Spring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值