循环服务器
编写一个服务器端程序和客户端程序。服务器端程序与客户端程序的TCP连接建立后首先向客户端发送一条内容为“Connect succeed.Please send a message to me”,然后等待接收客户端发送来的一条消息,收到后显示该消息并关闭连接,继续等待其他客户的连接请求。客户程序在服务器的连接建立成功后接收并显示从服务器收到的信息,然后从键盘接收一行信息发送给服务器。
服务器程序需要完成如下工作:
(1).初始化Winsock库;
(2).创建监听套接字;
(3).填写要绑定的本地地址结构;
(4).给监听套接字绑定本地地址;
(5).让监听套接字开始监听;
(6).循环执行:调用accept()函数接收连接请求,如果accept()成功返回,则使用accept()函数返回的套接字向客户发送一条消息,然后接收客户返回的信息并显示。服务器程序依次为到达的连接请求提供服务,与一个客户通信完成后才会与另一个客户连接,如果各步操作都不发生错误,服务器程序将不会结束。这种服务器被称为“循环服务器”。
客户端程序需要完成的主要工作步骤如下:
(1).初始化Winsock库;
(2).创建通信套接字;
(3).填写要连接的服务器的地址结构;
(4).与服务器建立连接;
(5).连接成功后接收一条服务器发来的信息并显示,然后向服务器发送一条信息;
(6).关闭套接字,卸载Winsock库,结束程序。
整个过程的流程图:
服务器端程序:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
/*************定义相关变量******************/
SOCKET sock_server,newsock; //监听套接字 已连接套接字
struct sockaddr_in addr; //服务器地址
struct sockaddr_in client_addr; //客户端地址
char msgbuffer[256]; //接收缓冲区
char msg[]="Connect succeed.\n"; //发送信息
/************初始化winsock2.DLL************/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/*************创建监听套接字****************/
if((sock_server=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写绑定服务器地址**************/
int addr_len=sizeof(struct sockaddr_in);
memset((void *)&addr,0,addr_len); //把地址结构体清0
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr.s_addr=htonl(INADDR_ANY); //使用本机IP地址作为服务器地址
/**********监听套接字绑定服务器地址*********/
if(bind(sock_server,(struct sockaddr *)&addr,sizeof(addr))!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}
/**********将套接字设置为监听状态***********/
if(listen(sock_server,0)!=0){
cout<<"listen 函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}else
cout<<"listenning.....\n";
/********循环接受连接请求并收发数据*********/
int size;
while(true){
if((newsock=accept(sock_server,(struct sockaddr *)&client_addr,&addr_len))==INVALID_SOCKET){
cout<<"accept函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
break;
}else
cout<<"成功接收一个连接请求"<<endl;
//成功接收一个连接以后 先发送信息
size=send(newsock,msg,sizeof(msg),0); //给客户端发送信息
if(size==SOCKET_ERROR){
cout<<"发送信息失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(newsock);
continue;
}else
cout<<"发送信息成功!\n";
if((size=recv(newsock,msgbuffer,sizeof(msgbuffer),0))<0){
cout<<"接收信息失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(newsock);
continue;
}else if(size==0){
cout<<"对方已关闭连接!\n";
closesocket(newsock);
continue;
}else
cout<<"收到的信息为:"<<msgbuffer<<endl;
closesocket(newsock);
}
closesocket(sock_server);
WSACleanup();
system("pause");
}
客户端程序:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
/************定义相关变量*************/
SOCKET sock_client; //客户端套接字
struct sockaddr_in server_addr; //存放服务器地址
int addr_len=sizeof(struct sockaddr_in);
char msgbuffer[256]; //接收缓冲区
/********初始化winsock2.DLL**********/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/************创建套接字**************/
if((sock_client=socket(AF_INET,SOCK_STREAM,0))<0){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写服务器地址************/
char IP[20];
cout<<"请输入服务器IP地址:";
cin>>IP;
memset((void *)&server_addr,0,addr_len); //把地址结构体清0
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=inet_addr(IP);
/*********套接字与服务器连接***********/
if(connect(sock_client,(struct sockaddr *)&server_addr,addr_len)!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_client);
WSACleanup();
return 0;
}
/******循环接受连接请求并收发数据*******/
int size;
if((size=recv(sock_client,msgbuffer,sizeof(msgbuffer),0))<0){
cout<<"接收信息失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_client);
return 0;
}else if(size==0){
cout<<"对方已关闭连接!\n";
closesocket(sock_client);
WSACleanup();
return 0;
}else
cout<<"从服务器收到的信息为:"<<msgbuffer<<endl;
cout<<"从键盘输入一行文字发送给服务器!\n";
cin>>msgbuffer;
if((size=send(sock_client,msgbuffer,sizeof(msgbuffer),0))<0){
cout<<"发送信息失败!错误代码:"<<WSAGetLastError()<<endl;
}else if(size==0){
cout<<"对方已关闭连接!\n";
}else
cout<<"信息发送成功!\n";
closesocket(sock_client);
WSACleanup();
system("pause");
}
程序运行效果:
服务器端:
客户端:
一对一连接 实现多次发送数据
下面的例子是服务器只能连接一个客户,但是客户可以不断地该服务器发送消息:
服务器端:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
/*************定义相关变量******************/
SOCKET sock_server,newsock; //监听套接字 已连接套接字
struct sockaddr_in addr; //服务器地址
struct sockaddr_in client_addr; //客户端地址
char msgbuffer[256]; //接收缓冲区
/************初始化winsock2.DLL************/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/*************创建监听套接字****************/
if((sock_server=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写绑定服务器地址**************/
int addr_len=sizeof(struct sockaddr_in);
memset((void *)&addr,0,addr_len); //把地址结构体清0
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr.s_addr=htonl(INADDR_ANY); //使用本机IP地址作为服务器地址
/**********监听套接字绑定服务器地址*********/
if(bind(sock_server,(struct sockaddr *)&addr,sizeof(addr))!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}
/**********将套接字设置为监听状态***********/
if(listen(sock_server,0)!=0){
cout<<"listen 函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}else
cout<<"listenning.....\n";
/********循环接受连接请求并收发数据*********/
int size;
if((newsock=accept(sock_server,(struct sockaddr *)&client_addr,&addr_len))==INVALID_SOCKET){
cout<<"accept函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
}else
cout<<"成功接收一个连接请求"<<endl;
while(1){
if((size=recv(newsock,msgbuffer,sizeof(msgbuffer),0))<0){
cout<<"接收信息失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(newsock);
break;
}else if(size==0){
cout<<"对方已关闭连接!\n";
closesocket(newsock);
break;
}else
cout<<"收到的信息为:"<<msgbuffer<<endl;
}
closesocket(sock_server);
WSACleanup();
system("pause");
}
客户端:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main(){
/************定义相关变量*************/
SOCKET sock_client; //客户端套接字
struct sockaddr_in server_addr; //存放服务器地址
int addr_len=sizeof(struct sockaddr_in);
/********初始化winsock2.DLL**********/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/************创建套接字**************/
if((sock_client=socket(AF_INET,SOCK_STREAM,0))<0){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写服务器地址************/
char IP[20];
cout<<"请输入服务器IP地址:";
cin>>IP;
memset((void *)&server_addr,0,addr_len); //把地址结构体清0
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=inet_addr(IP);
/*********套接字与服务器连接***********/
if(connect(sock_client,(struct sockaddr *)&server_addr,addr_len)!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_client);
WSACleanup();
return 0;
}
/******循环接受连接请求并收发数据*******/
while(true){
int size;
cout<<"从键盘输入一行文字发送给服务器!\n";
char msgbuffer[256]; //接收缓冲区
cin>>msgbuffer;
int c=0; //记录输入字符串的长度
while(msgbuffer[c]!=0){
c++;
}
char *buf=(char*)malloc((c+1)*sizeof(char)); //动态申请一个新的、大小适合的接收缓冲区
for(int i=0;i<c;i++)
buf[i]=msgbuffer[i]; //进行字符复制
buf[c]=0;
if((size=send(sock_client,buf,sizeof(msgbuffer),0))<0){
cout<<"发送信息失败!错误代码:"<<WSAGetLastError()<<endl;
}else if(size==0){
cout<<"对方已关闭连接!\n";
}else
cout<<"信息发送成功!\n";
free(buf); //释放申请的堆空间
}
closesocket(sock_client);
WSACleanup();
system("pause");
}
程序运行效果:
服务器端:
客户端:
数据传输格式
TCP将应用程序要传输的数据看成一个字节流,对数据的格式无任何要求,因此,使用TCP可以传送任何格式的数据。但是,由于通信两端的系统有时并不相同,而不同的系统对相同的数据的表示或存储格式有时是不相同的,所以,在不同的系统平台间交换数据时需要考虑数据的格式问题。
对于整型数据,由于存在使用大端和小端两种不同的字节顺序的计算机系统,为了保证不同系统间整型数据格式的兼容性,要求发送端在发送整型数据前必须先将整型数据转换为网络字节顺序,接收端接收到整型数据后必须将整型数据转换为主机字节顺序。同时,对其他数据还需要注意:
(1).设置或读取协议首部中以字或双字表示的字段时必须转换为它们的字节顺序,例如,TCP中的端口、序号等,但IP地址不用转换;
(2).由于目前最常使用的计算机系统PC大都是X86系统架构的,忘记字节顺序转换通常不会有问题产生,但是,现实也存在很多基于不同的系统平台上的计算机,因此,在编写网络通信程序时一定转换字节顺序是一种良好的编程习惯。
(3).对char类型数据来说,由于每个char类型的字符仅占用一个字节的存储空间,因此不存在字节顺序问题,所以在发送和接收char类型数据时不需要字节顺序转换。
(4).对于汉字来说,不论采用哪一种编码格式,汉字的编码长度都是大于1的,但是只要使用同一种汉字编码,无论在何种系统上,其字节的顺序是固定不变的,与硬件系统平台无关,因此在传输汉字时也无须进行字节转换。
(5).float和double类型的浮点数也是多字节的数值数据,但它们的字节顺序是由IEEE标准规定的,同样与系统无关,一般情况下,C/C++语言编译器会根据IEEE标准的规定,把float和double类型数据分别看作4个和8个char类型的数组进行解释转换,因此也不需要考虑字节顺序。
(6).结构数据中不同成员的数据类型往往是不同的,运行在不同的计算机上的两个进程在通过网络传输结构类型的数据时,一种自然的做法就是将结构中的每个成员都看作是一个单一的数据来分别进行处理和传输。但是如果结构数据成员较多,在编程时这种方法就会显得有些繁琐,而且编写出的程序在运行效率也不会很高。较好的做法是将结构数据作为一个整体来进行传输,也就是将存放结构数据的存储空间看作一个存放单字节数据的存储区,直接将存储区中的这些字节原样发给通信对端。但是,如果结构体数据中存在整数成员,则在发送端和接收端就需要单独对整型成员进行字节转换。
例子:客户端程序将一个学生的姓名、学号、成绩等信息发送给服务器。
服务器端:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
struct Student{ //学生信息结构
char sName[20];
char sTtID[10];
int nEnglist;
int nComputerNetwork;
int nCProg;
};
bool InitSocket(SOCKET &sock_server,int addr_len){
/************初始化winsock2.DLL************/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/*************创建监听套接字****************/
if((sock_server=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写绑定服务器地址**************/
struct sockaddr_in addr; //服务器地址
memset((void *)&addr,0,addr_len); //把地址结构体清0
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr.s_addr=htonl(INADDR_ANY); //使用本机IP地址作为服务器地址
/**********监听套接字绑定服务器地址*********/
if(bind(sock_server,(struct sockaddr *)&addr,sizeof(addr))!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}
/**********将套接字设置为监听状态***********/
if(listen(sock_server,0)!=0){
cout<<"listen 函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_server);
WSACleanup();
return 0;
}else
cout<<"listenning.....\n";
}
int main(){
/*************定义相关变量******************/
SOCKET sock_server,newsock; //监听套接字 已连接套接字
struct sockaddr_in client_addr; //客户端地址
int addr_len=sizeof(struct sockaddr_in);
InitSocket(sock_server,addr_len); //初始化socket
/********循环接受连接请求并收发数据*********/
int size;
if((newsock=accept(sock_server,(struct sockaddr *)&client_addr,&addr_len))==INVALID_SOCKET){
cout<<"accept函数调用失败!错误代码:"<<WSAGetLastError()<<endl;
}else
cout<<"成功接收一个连接请求"<<endl;
while(1){
struct Student stud;
if((size=recv(newsock,(char*)&stud,sizeof(struct Student),0))<0){
cout<<"接收信息失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(newsock);
break;
}else if(size==0){
cout<<"对方已关闭连接!\n";
closesocket(newsock);
break;
}else{
//将结构体中的整型数据字段的值由主机字节顺序转换为网络字节顺序
stud.nEnglist=htonl(stud.nEnglist);
stud.nComputerNetwork=htonl(stud.nComputerNetwork);
stud.nCProg=htonl(stud.nCProg);
cout<<stud.sName<<" "<<stud.sTtID<<" "<<stud.nEnglist<<" "
<<stud.nComputerNetwork<<" "<<stud.nCProg<<endl;
}
}
closesocket(sock_server);
WSACleanup();
system("pause");
}
客户端:
#include <iostream>
#include "winsock2.h"
#define PORT 65432
#pragma comment(lib,"ws2_32.lib")
using namespace std;
struct Student{ //学生信息结构
char sName[20];
char sTtID[10];
int nEnglist;
int nComputerNetwork;
int nCProg;
};
bool InitSocket(SOCKET &sock_client,int addr_len){
/********初始化winsock2.DLL**********/
WSADATA wsaData;
WORD wVerionRequested=MAKEWORD(2,2);
if(WSAStartup(wVerionRequested,&wsaData)!=0){
cout<<"加载winsock.dll失败!";
return 0;
}
/************创建套接字**************/
if((sock_client=socket(AF_INET,SOCK_STREAM,0))<0){
cout<<"创建套接字失败!错误代码:"<<WSAGetLastError()<<endl;
WSACleanup();
return 0;
}
/***********填写服务器地址************/
struct sockaddr_in server_addr; //存放服务器地址
char IP[20];
cout<<"请输入服务器IP地址:";
cin>>IP;
memset((void *)&server_addr,0,addr_len); //把地址结构体清0
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=inet_addr(IP);
/*********套接字与服务器连接***********/
if(connect(sock_client,(struct sockaddr *)&server_addr,addr_len)!=0){
cout<<"地址绑定失败!错误代码:"<<WSAGetLastError()<<endl;
closesocket(sock_client);
WSACleanup();
return 0;
}
return 1;
}
int main(){
/************定义相关变量*************/
SOCKET sock_client; //客户端套接字
int addr_len=sizeof(struct sockaddr_in);
InitSocket(sock_client,addr_len); //初始化socket
/******循环接受连接请求并收发数据*******/
while(true){
struct Student stud;
cin>>stud.sName;
cin>>stud.sTtID;
cin>>stud.nEnglist;
cin>>stud.nComputerNetwork;
cin>>stud.nCProg;
//将结构体中的整型数据字段的值由主机字节顺序转换为网络字节顺序
stud.nEnglist=htonl(stud.nEnglist);
stud.nComputerNetwork=htonl(stud.nComputerNetwork);
stud.nCProg=htonl(stud.nCProg);
//发送数据
send(sock_client,(char*)&stud,sizeof(struct Student),0);
}
closesocket(sock_client);
WSACleanup();
system("pause");
}
程序运行效果:
服务器端:
客户端: