服务器端:
// Server.cpp : Defines the entry point for the console application.
//
#include "stdio.h"
#include <iostream>
using namespace std;
#include <sstream>
#include <string>
#include <WINSOCK2.H>
#pragma comment(lib, "wsock32.lib")
#define SERVER_EXIT_OK 0 //服务器正常退出
#define SERVER_DLL_REEOR 1 //调用Windows sockets DLL失败
#define SERVER_API_ERROR 2 //调用Windows sockets API失败
#define SERVERPORT 5555//服务器TCP端口
#define MAX_NUM_BUF 64 //缓冲区最大尺寸
//变量
char bufRecv[MAX_NUM_BUF]; //读缓冲区
char bufSend[MAX_NUM_BUF]; //写缓冲区
SOCKET sServer; //服务器监听套接字
SOCKET sClient; //接受客户端套接字
BOOL bConning; //与客户端的连接状态
//函数
void InitMember(void); //初始化成员变量
int ExitClient(int nExit); //客户端退出
BOOL RecvLine(SOCKET s, char* buf); //读取一行数据
BOOL SendLine(SOCKET s, char* buf); //发送一行数据
int HandleSocketError(char *str); //对Windows sockets API调用错误处理
void ShowSocketMsg(char* str); //显示错误信息
//主函数
int main(int argc, char* argv[])
{
InitMember(); //初始化变量
WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本
WSADATA wsaData; //Windows sockets DLL版本信息
int retVal; //调用Windows sockets API返回值
//初始化Windows Sockets DLL
wVersionRequested = MAKEWORD(1,1);//makeword是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)
retVal = WSAStartup(wVersionRequested, &wsaData);//Windows异步套接字的启动命令
//{1}一个WORD(双字节)型数值,指定了应用程序需要使用的Winsock规范的最高版本。
//{2}指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
if ( 0 != retVal )
{
ShowSocketMsg("Can not find a usable Windows Sockets dll!");
return SERVER_DLL_REEOR;
}
//确保WinSock DLL支持1.1
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1)
{
ShowSocketMsg("Can not find a usable Windows Sockets dll!");
WSACleanup( );//中止Windows Sockets在所有线程上的操作.
return SERVER_DLL_REEOR;
}
//创建套接字
sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/*SOCKET PASCAL FAR socket( int af, int type, int protocol);
af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。
protocol:套接口所用的协议。如调用者不想指定,可用0。
若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误*/
if(INVALID_SOCKET == sServer)
{
return HandleSocketError("Failed socket()!");
}
//服务器套接字地址
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(SERVERPORT);//将一个无符号短整型数值转换为网络字节序
addrServ.sin_addr.s_addr = INADDR_ANY;
/*sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构*/
//绑定套接字
retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
/*返回:成功返回0,失败返回-1.
参数列表中,sockfd 表示已经建立的)socket编号(描述符);
my_addr 是一个指向sockaddr结构体类型的指针;
参数addrlen表示my_addr结构的长度,可以用sizeof函数获得。*/
if(SOCKET_ERROR == retVal)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed bind()!"); //错误处理
}
int count=0;
while(1){
//开始监听
retVal = listen(sServer, 2);
/*S:用于标识一个已捆绑未连接套接口的描述字。
backlog:等待连接队列的最大长度*/
if(SOCKET_ERROR == retVal)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed listen()!");//错误处理
}
//等待客户端的连接
cout << "Server succeeded!" << endl;
cout << "Waiting for new clients..." << endl;
//接受客户端请求
sockaddr_in addrClient;
int addrClientlen = sizeof(addrClient);
sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);
/*s:套接口描述字,该套接口在listen()后监听连接。
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
*/
if(INVALID_SOCKET == sClient)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed accept()!"); //错误处理
}else{
bConning = TRUE; //客户端请求成功
}
//显示客户端的IP和端口
char *pClientIP = inet_ntoa(addrClient.sin_addr);//如果正确,返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区
u_short clientPort = ntohs(addrClient.sin_port); //将一个无符号短整形数从网络字节顺序转换为主机字节顺序
cout<<"Accept a client."<<endl;
cout<<"IP: "<<pClientIP<<endl;
cout<<"Port: "<<clientPort<<endl;
//接收客户端数据
if (!RecvLine(sClient, bufRecv))
{
cout<<"接收数据失败!\n";
return ExitClient(SERVER_API_ERROR);//退出
}
//显示客户端数据
cout << bufRecv<<endl;
char temp[MAX_NUM_BUF];
int i=0;
while(bufRecv[i]!='\n')
{
temp[i]=bufRecv[i];
i++;
}
temp[i]='\0';
//向客户端发送数据
stringstream ss;
string s(temp);
count++;
ss<<count;
string str="";
str+=s;
str+="你好!你是访问我服务的第";
str+=ss.str();
str+="个客户\n";
strcpy(bufSend, str.c_str());
if (!SendLine(sClient, bufSend))
{
cout<<"发送失败!\n";
//return ExitClient(SERVER_API_ERROR);
}
}
//显示退出信息
cout << "Server exiting..." << endl;
//退出
return ExitClient(SERVER_EXIT_OK);
}
/*
* 初始化成员变量
*/
void InitMember(void)
{
//初始化读和写缓冲区
memset(bufRecv, 0, MAX_NUM_BUF);
memset(bufSend, 0, MAX_NUM_BUF);
//初始化
sServer = INVALID_SOCKET;
sClient = INVALID_SOCKET;
//没有连接状态
bConning = FALSE;
}
/*
* 退出
*/
int ExitClient(int nExit)
{
closesocket(sServer); //关闭监听套接字
closesocket(sClient); //关闭连接客户端套接接
WSACleanup(); //卸载Windows sockets DLL 清理内存
return nExit; //退出
}
/*
* 读一行数据
*/
BOOL RecvLine(SOCKET s, char* buf)
{
BOOL retVal = TRUE; //返回值
BOOL bLineEnd = FALSE; //行结束
int nReadLen = 0; //读入字节数
int nDataLen = 0; //数据长度
while (!bLineEnd && bConning) //与客户端连接 没有换行
{
nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节
/*s:一个标识已连接套接口的描述字。
buf:用于接收数据的缓冲区。
len:缓冲区长度。
flags:指定调用方式。*/
//错误处理
if (SOCKET_ERROR == nReadLen)
{
int nErrCode = WSAGetLastError();//错误代码,获得上次失败操作的错误状态
if (WSAENOTCONN == nErrCode)
{
ShowSocketMsg("The socket is not connected!");
}else if(WSAESHUTDOWN == nErrCode)
{
ShowSocketMsg("The socket has been shut down!");
}else if (WSAETIMEDOUT == nErrCode)
{
ShowSocketMsg("The connection has been dropped!");
}else if (WSAECONNRESET == nErrCode)
{
ShowSocketMsg("The virtual circuit was reset by the remote side!");
}else{}
retVal = FALSE; //读数据失败
break; //跳出循环
}
if (0 == nReadLen)//客户端关闭
{
retVal = FALSE; //读数据失败
break ; //跳出循环
}
//读入数据
if ('\n' == *(buf + nDataLen)) //换行符
{
bLineEnd = TRUE; //接收数据结束
}else{
nDataLen += nReadLen; //增加数据长度
}
}
return retVal;
}
/*
* //发送一行数据
*/
BOOL SendLine(SOCKET s, char* str)
{
int retVal;//返回值
retVal = send(s, str, strlen(str), 0);//一次发送
/*s:一个用于标识已连接套接口的描述字。
buf:包含待发送数据的缓冲区。
len:缓冲区中数据的长度。
flags:调用执行方式。*/
//错误处理
if (SOCKET_ERROR == retVal)
{
int nErrCode = WSAGetLastError();//错误代码
if (WSAENOTCONN == nErrCode)
{
ShowSocketMsg("The socket is not connected!");
}else if(WSAESHUTDOWN == nErrCode)
{
ShowSocketMsg("The socket has been shut down!");
}else if (WSAETIMEDOUT == nErrCode)
{
ShowSocketMsg("The connection has been dropped!");
}else{}
return FALSE; //发送失败
}
return TRUE; //发送成功
}
/*
* 错误处理
*/
int HandleSocketError(char *str)
{
ShowSocketMsg(str); //显示错误消息
WSACleanup(); //卸载Windows socket DLL
return SERVER_API_ERROR;//退出应用程序
}
/*
* 显示错误
*/
void ShowSocketMsg(char* str)
{
cout<<str;
//MessageBox(NULL, str, "SERVER ERROR", MB_OK);
}
客户端:
// Client.cpp : Defines the entry point for the console application.
//
#include "stdio.h"
#include <windows.h>
#include <winsock.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "wsock32.lib")
#define CLIENT_EXIT_OK 0 //客户端正常退出
#define CLIENT_DLL_REEOR 1 //调用Windows socket dll失败
#define CLIENT_API_ERROR 2 //调用Windows socket api失败
#define MAX_NUM_BUF 64 //缓冲区的最大长度
#define SERVERPORT 5555//服务器TCP端口
//变量
char bufRecv[MAX_NUM_BUF]; //读缓冲区
char bufSend[MAX_NUM_BUF]; //写缓冲区
SOCKET sHost; //socket
BOOL bConning; //连接服务器状态
//函数
void InitMember(void); //初始化变量
int ExitClient(int nExit); //退出
BOOL RecvLine(SOCKET s, char* buf); //读取一行数据
void ShowErrorMsg(void); //显示错误信息
//主函数
int main()
{
//初始化变量
InitMember();
WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本
WSADATA wsaData; //Windows sockets DLL版本信息
int retVal; //调用Windows sockets API返回值
//初始化Windows Sockets DLL
wVersionRequested = MAKEWORD(1,1);
retVal = WSAStartup(wVersionRequested,(LPWSADATA)&wsaData);
if ( 0 != retVal )
{
cout<<"Error";
//MessageBox(NULL, "Can not find a usable Windows Sockets dll!", "ERROR", MB_OK);
return CLIENT_DLL_REEOR;
}
//while(1){
//创建Windows socket
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == sHost)
{
ShowErrorMsg(); //显示错误信息
WSACleanup(); //释放资源
return CLIENT_API_ERROR;//退出
}
//准备连接服务器
cout << "Client succeeded!" << endl;
cout<<"Be ready to connect to server..."<<endl;
//获取主机的信息
LPHOSTENT hostEntry;
char hostname[MAX_NUM_BUF];
gethostname(hostname,MAX_NUM_BUF); //获取主机名称
hostEntry = gethostbyname(hostname); //获取主机信息
if(!hostEntry)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//设置sockaddr_in
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
//addrServ.sin_addr.s_addr = inet_addr("10.20.255.183");
addrServ.sin_port = htons(SERVERPORT);
//连接服务器
retVal=connect(sHost,(LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
if(SOCKET_ERROR == retVal)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}else{
bConning = TRUE; //连接服务器成功
}
//连接服务器成功
cout<<"Connect successfully!"<<endl;
//向服务器发送数据
int i=0;
char c;
char s[MAX_NUM_BUF];
cout<<"输入您的姓名:\n";
while((c=getchar())!='\n')
{
s[i++]=c;
}
s[i++]='\n';
s[i]='\0';
strcpy(bufSend, s);
retVal = send(sHost, bufSend, strlen(bufSend), 0);
if (SOCKET_ERROR == retVal)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//从服务器接收数据
if (!RecvLine(sHost, bufRecv))
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//显示服务器的应答
cout<<bufRecv<<endl;
//Sleep(2000);
//退出
return ExitClient(CLIENT_EXIT_OK);
}
/*
* 显示错误信息
*/
void ShowErrorMsg(void)
{
int nErrCode = WSAGetLastError();//获取错误代码
HLOCAL hlocal = NULL;
//获取错误的文本字符串
BOOL fOk = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, nErrCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(PTSTR)&hlocal, 0, NULL);
//显示错误信息
if (hlocal != NULL)
{
cout<<LocalLock(hlocal);
//MessageBox(NULL, (char*)LocalLock(hlocal), "CLIENT ERROR", MB_OK);
LocalFree(hlocal);
}
}
/*
* 初始化成员变量
*/
void InitMember(void)
{
//初始化读和写缓冲区
memset(bufRecv, 0, MAX_NUM_BUF);
memset(bufSend, 0, MAX_NUM_BUF);
//初始化
sHost = INVALID_SOCKET;
//没有连接状态
bConning = FALSE;
}
/*
* 退出
*/
int ExitClient(int nExit)
{
closesocket(sHost); //关闭套接字
WSACleanup(); //卸载Windows sockets DLL 清理内存
//显示退出信息
cout << "Client exiting..." << endl;
Sleep(5000);
return nExit; //退出
}
/*
* 读取一行数据
*/
BOOL RecvLine(SOCKET s, char* buf)
{
BOOL retVal = TRUE; //返回值
BOOL bLineEnd = FALSE; //行结束
int nReadLen = 0; //读入字节数
int nDataLen = 0; //数据长度
while (!bLineEnd && bConning) //与客户端连接 没有换行
{
nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节
//错误处理
if (SOCKET_ERROR == nReadLen)
{
retVal= FALSE; //读数据失败
break; //跳出循环
}
if (0 == nReadLen)//客户端关闭
{
retVal = FALSE; //读数据失败
break ; //跳出循环
}
//读入数据
if ('\n' == *(buf + nDataLen)) //换行符
{
bLineEnd = TRUE; //接收数据结束
}else{
nDataLen += nReadLen; //增加数据长度
}
}
return retVal;
}
服务器端:
客户端: