Network Programming Summary 2

1. TCP

TCP model

1. TCP building related functions:

 Socket

 1) Socket function:

2)bind

 3)listen

 4)accept

 5)recv

 Notice:

1> recv in TCP can be replaced by read; 2> recv in TCP can be replaced by recvfrom

6)send

  Notice:

1> send in TCP can be replaced by write; 2> send in TCP can be replaced by sendto

7) connect

 client:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("cfd=%d\n",cfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig
	
	//连接服务器
	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("connet");
		return -1;
	}
	printf("connect  sucess __%d__\n",__LINE__);

	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送
		if(send(cfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send sucess\n");

		//接收
		bzero(buf,sizeof(buf));
		
		res=recv(cfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if(0==res)
		{
			printf("cfd=%d 服务器下线 __%d__\n",	cfd,__LINE__);
			break;
		}
		printf("cfd=%d %s __%d__\n",cfd,buf,__LINE__);

	}

	close(cfd);
	return 0;
}

server:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("sfd=%d\n",sfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig

	//将IP和端口号绑定到套接字上
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	//阻塞函数,从已完成连接的队列头中获取一个客户端信息
	//该文件描述符才是与客户端通信的文件描述符
	int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
	if(newfd<0)
	{
		ERR_MSG("accept");
		return -1;
	}
	printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		//接收
	
		res=recv(newfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if(0==res)
		{
			printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
			break;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

		//发送
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send sucess\n");
	}

	close(newfd);
	close(sfd);
	return 0;
}

2.UDP

UDP model

 1. UDP related functions

1)socket

2)bind

3)recvfrom

 4) send two

 client:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define SER_IP "192.168.8.77" //本机IP 
#define SER_PORT 6666

#define CLI_IP "192.168.8.77" //本机IP 
#define CLI_PORT 8888

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
//*****************************************************	
	struct sockaddr_in cin;
	cin.sin_family  = AF_INET;      //必须填AF_INET
	cin.sin_port = htons(CLI_PORT);     //端口号的网络字节符
	cin.sin_addr.s_addr = inet_addr(CLI_IP);
	
	if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("client bind success\n");
//********************************************************
	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(SER_PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(SER_IP);

	struct sockaddr_in rcvaddr;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(rcvaddr);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送数据,发送给服务器
		if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送数据\n");

		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&rcvaddr,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(rcvaddr.sin_addr), ntohs(rcvaddr.sin_port),buf);
	}
	//关闭文件描述符
	close(cfd);
	
	return 0;
}

server:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.8.77" //本机IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(IP);

	//绑定 必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}

	struct sockaddr_in cin;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(cin);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),buf);
		
		strcat(buf,"*_*");
		if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送成功\n");
	}
	//关闭文件描述符
	close(sfd);
	
	return 0;
}

2. The connect function in UDP ( emphasis )

1>The connect function in TCP will cause a three-way handshake to connect the client to the server; the connect function in UDP only records the IP and port number of the peer into the kernel. At this time UDP can only communicate with the recorded peer.

2>The connect function in TCP can only be called once; the connect function in UDP can be called multiple times to refresh the IP address and port of the peer in the kernel. If you want to clear the address information of the peer in the kernel, you can modify the sin_family member to AF_UNSPEC

3>When UDP uses connect mode to send and receive messages, you can call the send write function or the sendto function.

1> sendto ( sd,buf,sizeof ( buf ) , NULL , 0 );

recvfrom can be replaced with recv read when filling in NULL in the subsequent address information structure.

2> recvfrom ( sockfd , buf, len , flags , NULL ,NULL );

 Advantages of UDP calling connect function:

1>Improve transmission efficiency:

a. Do not call connect: fill the kernel with the peer's address information --> send the message ---> clear the kernel information ---> fill the kernel with the peer's address information --> send the message ---> clear Kernel information

b. Called connect: fill the address information of the peer into the kernel --> send a message --> send a message --> send a message ---> clear the kernel information

2>Increase the stability of transmission:

a. Calling the UDP communication of the connect function can prevent the AB process from receiving the C process message during data transmission, causing transmission errors

3. UDP multipoint communication

【1】Network properties

setsockopt / getsockopt

 

 [2] Multipoint communication

1. Unicast

1> One-to-one communication mode between hosts, switches and routers only forward data, not copy it

2>Only two entities communicate with each other at a time, and both the sender and the receiver are uniquely determined.

2. Broadcast

1> One-to-many communication mode between hosts, the network unconditionally copies and forwards the signals sent by each host

2>All hosts under the same LAN can receive broadcast information

3> Prohibit broadcast data from passing through the router to prevent broadcast data from affecting large-area hosts

4> Broadcast data does not require a response, only UDP can broadcast

5>Broadcast address: valid network number + host number all 1

1) The sending end of the broadcast (similar to the client)

1> socket creates a report socket; bind is not required to bind

2> setsockopt sets allowed broadcast: level: SOL_SOCKET optname: SO_BROADCAST

3> Specify the address information structure of the receiving end

                a. IP: Fill in the broadcast IP

                b. PORT: It can be consistent with the one filled by the receiving end.

4> sendto sends broadcast data

2) The receiving end of the broadcast (similar to the server)

1> socket creates a report socket; bind must be bound

2> Fill in the address information structure of the receiving end itself

  a. IP: Fill in the broadcast IP: valid network number + host number all 1

                After calling the bind function, all available IP addresses of the local machine will be bound to the socket.

                b. PORT: It can be consistent with the one filled by the sender.

3> recvfrom receives broadcast data

3. Multicast (multicast group)

1) Multicast sender (similar to client)

1>socket creates a report socket; bind is not required to bind

2> Specify the address information structure of the receiving end

  a. IP: Fill in the multicast IP (224.0.0.0~239.255.255.255, consistent with the receiver’s filling)

                b. PORT: It can be consistent with the one filled by the receiving end.

3> sendto sends multicast data

2) The receiving end of multicast (similar to server)

1> socket creates a report socket; bind must be bound

2> setsockopt joins the multicast group: level: IPPROTO_IP OPTNAME: IP_ADD_MEMBERSHIP   

3> Fill in the address information structure of the receiving end itself

          a. IP: Fill in the multicast IP 

                b. PORT: It can be consistent with the one filled by the sender.

4> recvfrom receives multicast data

Multicast snd:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define GRP_IP "243.1.2.3" //本机IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(GRP_IP);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		printf("请输入数据>>");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;

		//发送数据,发送给服务器
		if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("发送数据\n");
	}
	//关闭文件描述符
	close(cfd);
	return 0;
}

multicastrcv:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.79" //本机IP 
#define GRP_IP "224.1.2.3" //组播IP 
#define PORT 6666

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	//加入多播组
	struct ip_mreqn mq;
	mq.imr_multiaddr.s_addr = inet_addr(GRP_IP);//组播IP
	mq.imr_address.s_addr = inet_addr(IP);
	mq.imr_ifindex=2;
	
	if(setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mq,sizeof(mq))<0)
	{
		perror("setsockopt");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(GRP_IP);

	//绑定 必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	struct sockaddr_in cin;//存储数据包是从哪里来的
	socklen_t addrlen = sizeof(cin);

	char buf[128]="";
	ssize_t res =0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s : %d]: %s\n",inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),buf);
	}
	//关闭文件描述符
	close(sfd);
	return 0;
}

3. TFTP protocol

1) Overview of TFTP protocol : Simple File Transfer Protocol is a set of standard protocols suitable for file transfer on the network, using UDP for transmission

Features: Application layer protocol; implemented based on UDP protocol

Data transfer mode:

                     octet: binary mode (commonly used)

                     mail : is no longer supported

2) TFTP download model

 3) Summary of TFTP communication process

1>The server waits for the client's request on port 69

2>If the server approves the request, it uses the temporary port to communicate with the client.

3>The number of every packet changes (starting from 1)

4>Every data packet must be confirmed by ACK. If a timeout occurs, the last data packet or ACK packet needs to be resent.

5>The data length is transmitted in 512Byte. Data less than 512Byte means the end of data transmission.

 4) TFTP protocol analysis

 Code:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define SER_IP "192.168.171.1" //本机IP 
#define SER_PORT 69

int do_download(int cfd, struct sockaddr_in sin)
{
	//下载请求
	char filename[20]="";
	char buf[516]={0};
	printf("请输入要下载的文件名>>");
	scanf("%s",filename);
	while(getchar()!=10);

	short*p1 = (short*)buf;
	*p1 = htons(1);
	char*p2 = buf+2;
	strcpy(p2,filename);

	char*p3 = p2+strlen(p2);
	char*p4 = p3+1;
	strcpy(p4,"octet");

	int size=strlen(p2)+strlen(p4)+4;

	//发送下载请求协议
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("发送成功\n");

	int fd=-1;
	socklen_t  addrlen=sizeof(sin);
	ssize_t res =0;
	unsigned short num=0;
	int ret=0;	
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接收数据,必须接收数据包的发送方地址信息
		res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
		if(res<0)
		{
			ERR_MSG("recvfrom");
			ret=-1;
			break;
		}
		
		if(buf[1]==3)
		{
			if(htons(num+1)==*(unsigned short*)(buf+2))
			{
				//组数据包给服务器
				num++;
				if(1==ntohs(*(unsigned short*)(buf+2)))
				{
					fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664);
					if(fd<0)
					{
						ERR_MSG("open");
						return -1;
					}
				}
				if(write(fd,buf+4,res-4)<0)
				{
					ERR_MSG("write");
					ret=-1;
					break;
				}
				//回复ACK,由于ACK包和数据包前四个字节只有操作码不一致
				//直接修改数据包的操作码
				buf[1]=4;
				if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0)
				{
					ERR_MSG("sendto");
					ret =-1;
					break;
				}
				if(res-4<512)
				{
					printf("文件:%s 上传完毕\n",filename);
					break;
				}
			}
		}
		else if(buf[1]==5)//错误包
		{
			fprintf(stderr,"DOWNLOAD_EROR: %d: %s\n",
					ntohs(*(unsigned short*)(buf+2)),buf+4);
			break;
		}
	}
	close(fd);
	return ret;
}

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;      //必须填AF_INET
	sin.sin_port = htons(SER_PORT);     //端口号的网络字节符
	sin.sin_addr.s_addr = inet_addr(SER_IP);

	char c=0;
	while(1)
	{
		system("clear");
		printf("*****************\n");
		printf("******1.下载*****\n");
		printf("******2.上传*****\n");
		printf("******3.退出*****\n");
		printf("*****************\n");
		c=getchar();
		while(getchar()!=10);
		
		switch(c)
		{
		case'1':
			do_download(cfd,sin);
			break;
		case'2':
			break;
		case'3':
			goto END;
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
		printf("请输入任意字符清屏>>>");
		while(getchar()!=10);
	}

END:
	close(cfd);
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_57039874/article/details/130460061