TCP、UDP的C/S程序工作流程,原理

TCPUDPC/S程序工作流程

 

一、面向连接的C/S程序工作流程(TCP)

服务器端工作流程

u 使用WSAStartup()函数检查系统协议栈安装情况

u 使用socket()函数创建服务器端通信套接口

u 使用bind()函数将创建的套接口与服务器地址绑定

u 使用listen()函数使服务器套接口做好接收连接请求准备

u 使用accept()接收来自客户端由connect()函数发出的连接请求

u 根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据

u 使用closesocket()函数关闭套接口(可以先用shutdown()函数先关闭读写通道)

u 最后调用WSACleanup()函数结束Winsock Sockets API

 

客户端程序工作流程

u 使用WSAStartup()函数检查系统协议栈安装情况

u 使用socket()函数创建客户端套接口

u 使用connect()函数发出也服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成)

u 连接建立后使用send()函数发送数据,或使用recv()函数接收数据

u 使用closesocet()函数关闭套接口

u 最后调用WSACleanup()函数,结束Winsock Sockets API

 

服务器与客户端五元组的建立

五元组

<协议>

<本地IP地址,本地端口号>

<远程IP地址,远程端口号>

服务器端五元组

socket()确定

由服务器端调用bind()时确定

accept()确定

客户端五元组

socket()确定

 由客户端的bind()调用确定。如果客户端没有进行bind()调用,或调用了bind()但没有指定具体地址或端口号,则由系统内核自动确定地址和端口

connect()确定

 

流程图:

TCP、UDP的C/S程序工作流程,原理_第1张图片

无连接的C/S程序工作流程(UDP)

无连接的数据报传输服务通信时,客户端与服务器端所使用的函数是类似的,其工作流程如下:

u 使用WSAStartup()函数检查系统协议栈的安装情况

u 使用socket()函数创建套接口,以确定协议类型

u 调用bind()函数将创建的套接口与本地地址绑定,确定本地地址和本地端口号

u 使用sendto()函数发送数据,或者使用recvfrom()函数接收数据

u 使用closesocket()函数关闭套接口

u 调用WSACleanup()函数,结束Windows Sockets API

注意事项:

u 通信的一方可以不用bind()绑定地址和端口,由系统分配

u 不绑定IP地址和端口号的一方必须首先向绑定地址的一方发送数据

u 无连接的应用程序也可以调用connect()函数,但是它并不向对方发出建立连接的请求,而是在本地返回,由内核将connect()中指定的目标IP地址和端口号记录下来,在以后的通信中就可以使用面向连接的数据发送函数send()和数据接收函数recv()

u 无连接的数据报传输过程中,作为服务器的一方必须先启动

u 无连接客户端一般不调用connect(),在数据发送前客户与服务器各自通过socket()bind()建立了半相关,发送数据时除指定本地套接口的地址外,还需要指定接收方套接口地址,从而在数据收发过程中动态建立全连接

流程图:

TCP、UDP的C/S程序工作流程,原理_第2张图片

Tcp server:
#pragma comment(lib, "ws2_32.lib")
#include
		#include
		#include
		//服务器使用的端口号为5050
		#define DEFAULT_PORT  5050
		void main( )
{
	int		iPort=DEFAULT_PORT;
        WSADATA		wsaData;
        SOCKET		sListen,
					sAccept;
	//客户地址长度
	int			iLen;
	//发送的数据长度
	int			iSend;
	//要发送给客户的信息
	char		             buf[ ]="I am a server.";
//服务器和客户的地址
struct sockaddr_in	ser,
	cli;
printf("-------------------------------\n");
printf("Server waiting\n");
printf("-------------------------------\n");
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
	  	printf("Failed to load Winsock.\n ");
		return;
}
//创建服务器端套接口
	sListen=socket(AF_INET,SOCK_STREAM,0);
		if(sListen==INVALID_SOCKET)
		{
			printf("socket( )Failed:%d\n", WSAGetLastError( ));
			return;
	               }
                     //以下建立服务器端地址
	              ser.sin_family=AF_INET;
	//htons( )函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
	ser.sin_port=htons(iPort);
	//htonl( )函数把一个四字节主机字节顺序的数转换为网络字节顺序的数
	//使用系统指定的IP地址INADDR_ANY
	ser.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(sListen,(LPSOCKADDR)&ser,sizeof(ser))==SOCKET_ERROR)
	{
			printf("bind( ) Failed: %d\n", WSAGetLastError( ));
			return;
	}
//进入监听状态
if(listen(sListen,5)==SOCKET_ERROR)
{
		printf("listen( ) Failed:%d\n",WSAGetLastError( ));
		return;
}
//初始化客户地址长度参数
		iLen=sizeof(cli);
//进入一个无限循环,等待客户的连接请求
while(1)
{
		sAccept=accept(sListen,(struct sockaddr*)&cli,&iLen);
		if(sAccept==INVALID_SOCKET)
		{
		printf("accept( ) Failed: %d\n", WSAGetLastError( ));
				break;
		}
		//输出客户IP地址和端口号
		printf("Accepted client IP:[%s],port:[%d]\n",
				inet_ntoa(cli.sin_addr),
				ntohs(cli.sin_port));
		//给连接的客户发送信息
		iSend=send(sAccept,buf,sizeof(buf),0);
		if(iSend==SOCKET_ERROR)
		{	
			printf("send( ) Failed.:%d\n", WSAGetLastError( ));
			break;
		}
else	if(iSend==0)
				break;
				else
				{
					printf("send( ) byte:%d\n",iSend);
					printf("-------------------------------\n");
				}
			closesocket(sAccept);
}
closesocket(sListen);
WSACleanup( );
}
Tcp CLIENT:
#pragma comment(lib, "ws2_32.lib")
#include
	#include
		//服务器端口号为5050
		#define DEFAULT_PORT  5050
		#define DATA_BUFFER   1024
		void main(int argc,char *argv[ ])
		{
		 WSADATA		wsaData;
		SOCKET		sClient;
		 int 		iPort= DEFAULT_PORT;
//从服务器端接收的数据长度
		 int 					iLen;
		 //接收数据的缓冲
		 char					buf[DATA_BUFFER];
		 //服务器端地址
	 	struct sockaddr_in	 ser;
	 	//判断输入的参数是否正确
if(argc<2)
		 {
		//提示在命令行中输入服务器IP地址
		printf("Usage:client [server IP address]\n");
		return;
		 }
		 //接收数据的缓冲区初始化
		 memset(buf,0,sizeof(buf));
		 if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
	 {
			printf("Failed to load Winsock.\n");
			return;
		 }
//填写要连接的服务器地址信息
		ser.sin_family=AF_INET;
		ser.sin_port=htons(iPort);
		//inet_addr( )函数将命令行的点分IP地址转化为用二进制表示的网络字节顺序的IP地址
		ser.sin_addr.s_addr=inet_addr(argv[1]);
		//建立客户端流式套接口
		sClient=socket(AF_INET,SOCK_STREAM,0);
		if(sClient==INVALID_SOCKET)
		{
		printf("socket( ) Failed:%d\n", WSAGetLastError( ));
			return;
		}
		//请求与服务器端建立TCP连接
		if(connect(sClient,(struct sockaddr*)&ser,sizeof(ser))==INVALID_SOCKET)
		{
		printf("connect( ) Failed:%d\n", WSAGetLastError( ));
			return;
		}
else
			{
			//从服务器端接收数据
			iLen=recv(sClient,buf,sizeof(buf),0);
			if(iLen==0)
			return;
			else if(iLen==SOCKET_ERROR)
		{
		printf("recv( ) Failed:%d\n", WSAGetLastError( ));
		return;
		}
			printf("recv( ) data from server:%s\n",buf);
		}
		 closesocket(sClient);
		 WSACleanup( );
		}
UDP server:
#include 
#include 

#pragma comment(lib, "WS2_32.lib")


#define DEFAULT_PORT  2010
#define DATA_BUFFER   2000

void main( )
{
	int		iPort=DEFAULT_PORT;
	WSADATA		wsaData;
	SOCKET		sListen;
	int			iLen; //客户地址长度
	char Sendbuf[ ]="Hello! I am a server.";	//发送的数据
	char buf[DATA_BUFFER];
	struct sockaddr_in	ser,cli;	//服务器和客户的地址
	printf("-------------------------------\n");
	printf("Server waiting\n");
	printf("-------------------------------\n");
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
	{
		printf("Failed to load Winsock.\n ");
		return;
	}
	sListen=socket(AF_INET,SOCK_DGRAM,0);//创建服务器端套接口
	if(sListen==INVALID_SOCKET)
	{
		printf("socket( )Failed:%d\n", WSAGetLastError( ));
		return;
	}
	//以下建立服务器端地址
	ser.sin_family=AF_INET;
	//htons( )函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
	ser.sin_port=htons(iPort);
	//htonl( )函数把一个四字节主机字节顺序的数转换为网络字节顺序的数
	ser.sin_addr.s_addr=htonl(INADDR_ANY);//使用系统指定的IP地址INADDR_ANY
	if(bind(sListen,(LPSOCKADDR)&ser,sizeof(ser))==SOCKET_ERROR)
	{
		printf("bind( ) Failed: %d\n", WSAGetLastError( ));
		return;
	}
	while(1)	//进入一个无限循环
	{
		int SenderAddrSize = sizeof(cli);
		memset(buf,0,DATA_BUFFER);
		iLen=recvfrom(sListen,buf,sizeof(buf),0,(SOCKADDR *) &cli, &SenderAddrSize);
		if(iLen==SOCKET_ERROR)
		{	
			printf("send( ) Failed.:%d\n", WSAGetLastError( ));
			break;
		}
		else	if(iLen==0) break;
		else
		{
			printf("recvfrom:%s\n",buf);
			printf("-------------------------------\n");
		}
		iLen = sendto(sListen,Sendbuf,sizeof(Sendbuf),0, (SOCKADDR *) &cli,sizeof(cli));
		if(iLen==SOCKET_ERROR)
		{	
			printf("send( ) Failed.:%d\n", WSAGetLastError( ));
			break;
		}
		else
		{
			printf("send( ) byte:%d\n",iLen);
			printf("-------------------------------\n");
		}
	}
	closesocket(sListen);
	WSACleanup( );
}
UDP client:
#include 
#include 

#pragma comment(lib, "WS2_32.lib")


#define DEFAULT_PORT  2010
#define DATA_BUFFER   2000


void main(int argc,char *argv[ ])
{
	WSADATA		wsaData;
	SOCKET		sClient;
	int 		iPort= DEFAULT_PORT;
	int 		iLen; //从服务器端接收的数据长度
	char		buf[DATA_BUFFER]; //接收数据的缓冲
	struct sockaddr_in	 ser;//接收数据的缓冲
	if(argc<2)  //判断输入的参数是否正确
	{
		printf("Usage:client  [server IP address]\n");
		//提示在命令行中输入服务器IP地址
		return;
	}
	memset(buf,0,sizeof(buf));//接收数据的缓冲区初始化
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
	{
		printf("Failed to load Winsock.\n");
		return;
	}
	ser.sin_family=AF_INET;//填写要连接的服务器地址信息
	ser.sin_port=htons(iPort); //htons( )函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
	ser.sin_addr.s_addr=inet_addr(argv[1]);
	sClient=socket(AF_INET,SOCK_DGRAM,0);//建立客户端流式套接口
	if(sClient==INVALID_SOCKET)
	{
		printf("socket( ) Failed:%d\n", WSAGetLastError( ));
		return;
	}
	char a1[] = "how do you do I am client ";
	sendto(sClient,a1,sizeof(a1),0, (SOCKADDR *) &ser,sizeof(ser));
	int SenderAddrSize = sizeof(ser);
	iLen=recvfrom(sClient,buf,sizeof(buf),0,(SOCKADDR *) &ser, &SenderAddrSize);
	//从服务器端接收数据
	if(iLen==0) return;
	else if(iLen==SOCKET_ERROR)
	{
		printf("recv( ) Failed:%d\n", WSAGetLastError( ));
		return;
	}
	printf("recv data from server,Len =%d,test is:\n %s\n",iLen,buf);	
	closesocket(sClient);
	WSACleanup( );
}


你可能感兴趣的:(随笔小记)