C++(15)UDP和TCP-网络编程

166 阅读3分钟

1.TCP和UDP

TCP和UDP属于传输层

image-20220721232551478

TCP属于数据流协议,基于连接,保证传输的稳定和数据完整性。类似于打电话

UDP属于数据报协议,不基于连接,可能导致数据丢失。类似于发短信

2.Linux中的应用

(1)socket(套接字)编程

概念

socket也叫套接字,最初用于进程间通信,当前主要用于网络通信,通过socket可以使网络通信使用固定的步骤来实现连接和传输。

socket在网络编程中类似于文件描述符在文件IO中的应用。用来代表一个网络通信连接,socket描述符本质上也是一个非负的整数。

socket属于TCP/IP中的传输/应用层。

(2)sokcet的应用

1)获取socket描述符的函数 ---------- socket函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
//像文件IO的open
参数:
    domain - 协议簇(多个协议的集合)
  
        AF_INET6:IPV6通信
   type - 通信类型(传输层协议)
       SOCK_STREAM:数据流协议(TCP)
       SOCK_DGRAM:数据报协议(UDP)
   protocol - 协议
       由于通信协议已经由前两个参数确定,直接给0
返回值:
    成功返回一个socket描述符,失败返回-1                

在socket通信中,使用C/S架构,属于服务器客户端模式,一般C/S架构属于一服务器对多客户端,通信由客户端发起,服务器被动响应。

image-20220721233056370

(3)TCP服务器和客户端的实现

image-20220721233101788

1)服务器实现

1.获取socket描述符
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
2.准备通信地址(ip和端口号)
    struct sockaddr_in addr;
    //....
3.绑定socket描述符和通信地址 ------ bind函数
    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    //参数就是描述符和地址,地址需要强转
4.将socket改为监听模式 ------- listen函数
    int listen(int sockfd, int backlog);
    //backlog表示处理和监听队列的最大长度
5.等待客户端连接 -------- accept
//该函数是一个阻塞函数,会一直阻塞直到有客户端连上来位置
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    //后两个参数传出连上来的客户端的地址
    //返回新的socket描述符(用于和连上来的客户端通信)
//连接和通信有不同的两个描述符,一个负责通信,一个负责连接
6.通过上一步得到的新描述符,使用read/write和客户端通信
7.不在通信,使用close关闭描述符断开连接    

2)客户端实现

1.获取socket描述符
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
2.准备服务器通信地址(ip和端口号)
    struct sockaddr_in addr;
    //....    
3.连接服务器 -------- connect函数
    int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    //参数和bind一致,返回0(成功)和-1(失败)
6.通过上一步得到的新描述符,使用read/write和客户端通信
7.不在通信,使用close关闭描述符断开连接     

注意:准备通信地址一般包含字节顺序转换。


代码实现:

github.com/Yu-1120/C-P…

//tcp-server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc,char **argv)
{
	if(argc!=3){
		printf("Usage:%s ip port\n",argv[0]);
		exit(-1);
	}

	//1.创建socket描述符
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd==-1){
		perror("socket");
		exit(-1);
	}

	//2.准备通信地址
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));//端口号
	addr.sin_addr.s_addr = inet_addr(argv[1]);//IP


	//3.bind
	int res = bind(sockfd, (struct sockaddr *)&addr,sizeof(addr));
	if(res==-1){
		perror("bind");
		exit(-1);
	}

	//4.listen
	listen(sockfd,100);

	//5.等待客户端连接
	while(1){
		struct sockaddr_in cilent;
		socklen_t len = sizeof(cilent);

		int newfd = accept(sockfd, (struct sockaddr *)&cilent, &len);
		if(newfd==-1){
			perror("accept");
			exit(-1);
		}
		printf("%s到此一游!\n",inet_ntoa(cilent.sin_addr));

		//6.和客户端通信
		char buf[100] = {};
		res = read(newfd, buf, sizeof(buf));
		if(res<=0)
			break;

		printf("%s说:%s\n", inet_ntoa(cilent.sin_addr), buf);
		close(newfd);
	}


	close(sockfd);
	return 0;
}
//tcp-client


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc,char **argv)
{
	if(argc!=3){
		printf("Usage:%s ip port\n",argv[0]);
		exit(-1);
	}

	//1.创建socket描述符
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd==-1){
		perror("socket");
		exit(-1);
	}

	//2.准备通信地址
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));//端口号
	addr.sin_addr.s_addr = inet_addr(argv[1]);//IP


	//3.connect
	int res = connect(sockfd, (struct sockaddr *)&addr,sizeof(addr));
	if(res==-1){
		perror("connect");
		exit(-1);
	}

	//和服务器通信
	write(sockfd, "hello", 6);

	close(sockfd);

	return 0;
}