TCP、UDP的C/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()确定 |
流程图:
无连接的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 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( );
}