|
又是一个回射程序!!!!
毕竟还是一只小菜鸟,多练练,多写写没有坏处滴!
这次的回射程序思路如下:

客户端和服务器都使用了新的函数recvvl来接收数据了!!这是一个自己编写的函数,主要作用是接收不定长的数据。
整个程序的设计思路是这样的:
客户端首先将消息体的长度发送给服务器,再将消息体发送给服务器。
在服务器端先接收到消息体的长度,紧接着根据该长度接收相应的数据。
虽然上面接收数据看上去要用两步,但是我们把它放在同一个函数中了!
该函数的实现需要调用前面编写的recvn函数。其实现如下:
int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen)
{
int iResult;//存储单次recvn操作的返回值
unsigned int reclen; //用于存储报文头部存储的长度信息
//获取接收报文长度信息
iResult = recvn(s, ( char * )&reclen, sizeof( unsigned int ));
//printf("reclen:%d\n",reclen);
if ( iResult !=sizeof ( unsigned int )) {
//如果长度字段在接收时没有返回一个整型数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
//转换网络字节顺序到主机字节顺序
reclen = ntohl( reclen );
if ( reclen > recvbuflen ) {
//如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误
while ( reclen > 0){
iResult = recvn( s, recvbuf, recvbuflen );
if ( iResult != recvbuflen ) {
//如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
reclen -= recvbuflen;
//处理最后一段数据长度
if ( reclen < recvbuflen )
recvbuflen = reclen;
}
printf("可变长度的消息超出预分配的接收缓存\r\n");
return -1;
}
//接收可变长消息
iResult = recvn( s, recvbuf, reclen );
if ( iResult != reclen ){
//如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
return iResult;
}
函数参数说明:SOCKET s 连接套接字
char * recvbuf 接收缓存
unsigned int recvbuflen 缓存区的大小
值得注意的是,在接收过程中,需要将接收到的数据长度信息进行字节序的转换!!
//转换网络字节顺序到主机字节顺序
reclen = ntohl( reclen );
在发送数据长度之前,需要进行htonl的转换!!
下面贴上客户端的代码:
// 回显客户端3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#pragma comment(lib,"wsock32.lib")
#define MAXSIZE 100
//函数声明
int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen);
int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen);
int _tmain(int argc, _TCHAR* argv[])
{
//初始化WinSock
WSADATA wsaData;
int ret = WSAStartup(MAKEWORD(2,2),&wsaData);
if(ret != 0)
{
printf("WSAStratup failed!code:%d\n",WSAGetLastError());
return -1;
}
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("Invaild WinSock Version\n");
return -1;
}
//建立客户端套接字
SOCKET sockclient;
sockclient = socket(AF_INET,SOCK_STREAM,0);
if(sockclient==INVALID_SOCKET)
{
WSACleanup();
printf("socket() failed!code:%d\n",WSAGetLastError());
return -1;
}
//服务器地址信息
struct sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_port=htons(6000);
saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//连接服务器
ret = connect(sockclient,(sockaddr*)&saServer,sizeof(saServer));
if(ret == SOCKET_ERROR)
{
printf("connect() failed!code:%d\n",WSAGetLastError());
closesocket(sockclient);
WSACleanup();
return -1;
}
else
printf("connect successed\n");
//向服务器发送数据
//首先发送定长的消息声明本次传输的消息长度
//再发送变长的消息体
unsigned int slen = 0;
unsigned int buflen = MAXSIZE;
char buff[MAXSIZE]; //发送缓存
char recvbuff[MAXSIZE]; //接收缓存
//memset(buff, 0 , MAXSIZE);
while (TRUE)
{
memset(recvbuff,0,MAXSIZE);
printf("Please input a string to send:\n");
scanf("%s",buff);
if(strcmp(buff,"q")==0)
break;
slen = (unsigned int)strlen(buff);
slen = htonl(slen);
if(send(sockclient, (char*)&slen, sizeof(unsigned int), 0) <= 0)
{
printf("send failed !code:%d\n",WSAGetLastError());
closesocket(sockclient);
WSACleanup();
return -1;
}
if(send(sockclient, buff, strlen(buff), 0) <= 0)
{
printf("send failed !code:%d\n",WSAGetLastError());
closesocket(sockclient);
WSACleanup();
return -1;
}
ret = recvvl(sockclient,recvbuff,buflen);
if(ret==SOCKET_ERROR)
{
int nErrCode = WSAGetLastError();//错误代码
if (WSAENOTCONN == nErrCode)
{
printf("The socket is not connected!\n");
}else if(WSAESHUTDOWN == nErrCode)
{
printf("The socket has been shut down!\n");
}else if (WSAETIMEDOUT == nErrCode)
{
printf("The connection has been dropped!\n");
}else if (WSAECONNRESET == nErrCode)
{
printf("The virtual circuit was reset by the remote side!\n");
}else{}
closesocket(sockclient);
WSACleanup();
return -1;
}
//打印收到的回显字符串
printf("%s\n",recvbuff);
}
closesocket(sockclient);
WSACleanup();
return 0;
}
//指定长度接收
int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
{
int iResult; //存储单次recv操作的返回值
int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收
cnt = fixedlen;
while ( cnt > 0 ) {
iResult = recv(s, recvbuf, cnt, 0);
if ( iResult < 0 ){
//数据接收出现错误,返回失败
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
if ( iResult == 0 ){
//对方关闭连接,返回已接收到的小于fixedlen的字节数
printf("连接关闭\n");
return fixedlen - cnt;
}
//printf("接收到的字节数: %d\n", iResult);
//接收缓存指针向后移动
recvbuf +=iResult;
//更新cnt值
cnt -=iResult;
}
return fixedlen;
}
int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen)
{
int iResult;//存储单次recvn操作的返回值
unsigned int reclen; //用于存储报文头部存储的长度信息
//获取接收报文长度信息
iResult = recvn(s, ( char * )&reclen, sizeof( unsigned int ));
//printf("reclen:%d\n",reclen);
if ( iResult !=sizeof ( unsigned int )) {
//如果长度字段在接收时没有返回一个整型数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
//转换网络字节顺序到主机字节顺序
reclen = ntohl( reclen );
if ( reclen > recvbuflen ) {
//如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误
while ( reclen > 0){
iResult = recvn( s, recvbuf, recvbuflen );
if ( iResult != recvbuflen ) {
//如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
reclen -= recvbuflen;
//处理最后一段数据长度
if ( reclen < recvbuflen )
recvbuflen = reclen;
}
printf("可变长度的消息超出预分配的接收缓存\r\n");
return -1;
}
//接收可变长消息
iResult = recvn( s, recvbuf, reclen );
if ( iResult != reclen ){
//如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
if ( iResult == -1 ) {
printf("接收发生错误: %d\n", WSAGetLastError());
return -1;
}
else {
printf("连接关闭\n");
return 0;
}
}
return iResult;
}
|