关于C++ socket 附送图片的问题。客户端已写好暂时好像没问题。服务器端有错误

C++语言 码拜 8年前 (2017-04-29) 2249次浏览
C和S编译都没有错误,但是运行起来服务器接收客户端发送的图片会出问题。本人还是个小白,这个问题本人都纠结了半个月了。

// ChatRoomServerDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "ChatRoomServer.h"
#include "ChatRoomServerDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CChatRoomServerDlg 对话框
using namespace std;
#define BUF_SIZE 1024
//#define  IP_ADDRESS "10.11.163.113"  //表示服务器端的地址
#define  IP_ADDRESS "127.0.0.1"  //直接使用本机地址
#define MSGSIZE 1024
struct PerSocketData
{
	WSAOVERLAPPED overlap;//每一个socket连接需要关联一个WSAOVERLAPPED对象
	WSABUF buffer;//与WSAOVERLAPPED对象绑定的缓冲区
	char          szMessage[MSGSIZE];//初始化buffer的缓冲区
	DWORD          NumberOfBytesRecvd;//指定接收到的字符的数目
	DWORD          flags;
};
struct TransferData
{
		CChatRoomServerDlg * _chatRoom;//对话框指针
		HANDLE _completionPort;//完成端口
};
//使用这个工作线程来通过重叠IO的方式与客户端通信,多次利用IO的意思。
DWORD WINAPI workThread(LPVOID lpParam)
{
	TransferData * transData=(TransferData *)lpParam;
	HANDLE completionPort=transData->_completionPort;
	CChatRoomServerDlg * chatRoom=transData->_chatRoom;
	DWORD dwBytesTransfered;
	SOCKET clientSocket;
	PerSocketData * lpIOdata=NULL;
	while(true)
	{
		GetQueuedCompletionStatus(
			completionPort,
			&dwBytesTransfered,
			(LPDWORD)&clientSocket,
			(LPOVERLAPPED*)&lpIOdata,
			INFINITE);
		if (dwBytesTransfered==0xFFFFFFFF)
		{
			return 0;
		}
		if (dwBytesTransfered==0)
		{
			string message=chatRoom->socketMap[clientSocket];
			message=message+string(" 已退出!\n");
			chatRoom->appendString(CString(message.c_str()));
			closesocket(clientSocket);
			HeapFree(GetProcessHeap(),0,lpIOdata);
		}
		else
		{
			//假如这个socket连接已经被加入map中,此时传送的是信息
			if (chatRoom->socketMap.count(clientSocket))
			{
				string message=lpIOdata->szMessage;
				map<SOCKET,string>::iterator iter;
				string nickName=chatRoom->socketMap[clientSocket];
				string info=nickName+string(" 发送了一条消息:")+string(message);
				chatRoom->appendString(CString(info.c_str()));
				message=nickName+string(":")+string("\n")+message;
				//遍历每一个socket连接并将信息发送出去
				for (iter=chatRoom->socketMap.begin();iter!=chatRoom->socketMap.end();iter++)
				{
					send(iter->first,message.c_str(),message.size()+1,0);//多发送一个字符,将字符串结束符也发送过去
				}
			}
			else//第一次连接,此时收到的是用户昵称
			{
				//将这个新的socket连接和昵称放入集合中
					string message=lpIOdata->szMessage;//获取昵称
					chatRoom->socketMap[clientSocket]=message;//将昵称和socket绑定到一起
					//显示登陆信息在服务器端
					string login=chatRoom->socketMap[clientSocket];
					login=login+string(" 登陆成功!\n");
					chatRoom->appendString(CString(login.c_str()));
					//组装好发送给客户端的数据
					map<SOCKET,string>::iterator iter;
					string info=" 成功登陆!";
					message=message+info;
					//遍历每一个socket连接并将信息发送出去
					for (iter=chatRoom->socketMap.begin();iter!=chatRoom->socketMap.end();iter++)
					{
						send(iter->first,message.c_str(),message.size()+1,0);//多发送一个字符,将字符串结束符也发送过去
					}
			}
		//	cout<<lpIOdata->szMessage<<endl;
		//	send(clientSocket,lpIOdata->szMessage,dwBytesTransfered+1,0);//多发送一个字符,将字符串结束符也发送过去
			memset(lpIOdata,0,sizeof(PerSocketData));
			lpIOdata->buffer.len=MSGSIZE;
			lpIOdata->buffer.buf=lpIOdata->szMessage;
			WSARecv(clientSocket,
				&lpIOdata->buffer,
				1,
				&lpIOdata->NumberOfBytesRecvd,
				&lpIOdata->flags,
				&lpIOdata->overlap,
				NULL);
		}
	}
	return 0;
}
DWORD WINAPI startThread(LPVOID lpParam)
{
	//将对话框指针作为参数传递给这个成员变量
	CChatRoomServerDlg * chatRoom=(CChatRoomServerDlg*)lpParam;

	WSADATA wsaData;
	int err;
	CString str;
	//1.加载套接字库
	err=WSAStartup(MAKEWORD(1,1),&wsaData);
	if (err!=0)
	{
		str.Format( _T("Init Windows Socket Failed::%d\n", GetLastError()));
		chatRoom->appendString(str);
		return 0;
	}
	//下面执行一些使用完成端口需要进行的步骤
	//创建一个完成端口
	HANDLE completionPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
	PerSocketData * sockData;
	SYSTEM_INFO systeminfo;
	GetSystemInfo(&systeminfo);
	DWORD dwThreadId;
	TransferData trans;
	trans._chatRoom=chatRoom;
	trans._completionPort=completionPort;
	for (int i=0;i<systeminfo.dwNumberOfProcessors;i++)
	{
		CreateThread(NULL,0,workThread,&trans,0,&dwThreadId);
	}
	//2.创建socket
	//套接字描述符,SOCKET实际上是unsigned int,无符号整型数据。
	SOCKET serverSocket;
	serverSocket=socket(AF_INET,SOCK_STREAM,0);
	if (serverSocket==INVALID_SOCKET)
	{
		str.Format(_T("Create Socket Failed::%d\n", GetLastError()));
		chatRoom->appendString(str);
		return 0;
	}
	//服务器端的地址和端口号,使用本地ip地址和用户指定的端口号
	struct sockaddr_in serverAddr,clientAdd;
	DWORD dwip;
	chatRoom->m_ip.GetAddress(dwip);
	//serverAddr.sin_addr.s_addr=inet_addr(IP_ADDRESS);
	serverAddr.sin_addr.s_addr=htonl(dwip);
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(chatRoom->m_port);
	//3.绑定Socket,将Socket与某个协议的某个地址绑定
	err=bind(serverSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
	if (err!=0)
	{
		str.Format(_T("Bind Socket Failed::%d\n", GetLastError()));
		chatRoom->appendString(str);
		return 0;
	}
	//4.监听,将套接字由默认的主动套接字转换成被动套接字
	err=listen(serverSocket,10);
	if (err!=0)
	{
		str.Format( _T("listen Socket Failed::%d\n", GetLastError()));
		chatRoom->appendString(str);
		return 0;
	}
	chatRoom->appendString (_T("服务器端已启动......\n"));
	int addrLen=sizeof(clientAdd);
	SOCKET  sockConn;
	while(true)
	{
		//5.接收请求,当收到请求后,会将客户端的信息存入clientAdd这个结构体中,并返回描述这个TCP连接的Socket
		sockConn=accept(serverSocket,(struct sockaddr*)&clientAdd,&addrLen);
		if (sockConn==INVALID_SOCKET)
		{
			str.Format( _T("Accpet Failed::%d\n", GetLastError()));
			chatRoom->appendString(str);
			return 0;
		}
		inet_ntoa(clientAdd.sin_addr);
		clientAdd.sin_port;
		str.Format( _T("客户端连接:%S : %d\n"), inet_ntoa(clientAdd.sin_addr), clientAdd.sin_port);
		chatRoom->appendString(str);

		//第6步
		CreateIoCompletionPort((HANDLE)sockConn,completionPort,(DWORD)sockConn,0);
		sockData=(PerSocketData*)HeapAlloc(
			GetProcessHeap(),
			HEAP_ZERO_MEMORY,
			sizeof(PerSocketData));
		sockData->buffer.len=MSGSIZE;
		sockData->buffer.buf=sockData->szMessage;
		WSARecv(
			sockConn,
			&sockData->buffer,
			1,
			&sockData->NumberOfBytesRecvd,
			&sockData->flags,
			&sockData->overlap,
			NULL);
		/*******************接收文件*************************/
		FILE* fp;
		char buffer[BUF_SIZE] = { 0 }; printf("123!");
		while (1)
		{
			printf("456!");
			int OutCount = 0;
			OutCount = recv(sockConn, buffer, BUF_SIZE, 0);
			if (OutCount == 0)
				break;
			fwrite(buffer, 100, BUF_SIZE, fp);
		}
		printf("传输完成!");
		fclose(fp);
		closesocket(sockConn);
		WSACleanup();
		return 0;
		/*******************结束********************************/
	}
	PostQueuedCompletionStatus(completionPort,0xFFFFFFFF,0,NULL);
	CloseHandle(completionPort);
	closesocket(serverSocket);
	//7.清理Windows Socket库
	WSACleanup();
}
CChatRoomServerDlg::CChatRoomServerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CChatRoomServerDlg::IDD, pParent)
	, m_port(6000)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CChatRoomServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_PROT, m_port);
	DDX_Control(pDX, IDC_INFO, m_info);
	DDX_Control(pDX, IDC_IPADDRESS1, m_ip);
	DDX_Control(pDX, IDC_EDIT_RECVPICTURE, m_recvpicture);
}
BEGIN_MESSAGE_MAP(CChatRoomServerDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BEGIN, &CChatRoomServerDlg::OnBnClickedBegin)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BUTTON1, &CChatRoomServerDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
// CChatRoomServerDlg 消息处理程序
BOOL CChatRoomServerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标
	// TODO: 在此添加额外的初始化代码
	// 此段代码:独立的获取本机IP地址和计算机名  
	WORD wVersionRequested;   
	WSADATA wsaData;   
	char name[255];   
	CString ip;   
	PHOSTENT hostinfo;   
	wVersionRequested = MAKEWORD(2, 0);   
	if (WSAStartup(wVersionRequested, &wsaData) == 0)  
	{   
		if(gethostname(name, sizeof(name)) == 0)   
		{   
			if((hostinfo = gethostbyname(name)) != NULL)   
			{   
				ip = inet_ntoa(*(struct in_addr *)*hostinfo->h_addr_list);   
			}   
		}     
		WSACleanup( );   
	}      
	m_ip.SetWindowText(ip);
	UpdateData(false);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
// 假如向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。
void CChatRoomServerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文
		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CChatRoomServerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}
void CChatRoomServerDlg::appendString(CString str)
{
		UpdateData(true);
		int len=m_info.SendMessage(WM_GETTEXTLENGTH);
		m_info.SetSel(len,len);
		m_info.ReplaceSel(str);
		UpdateData(false);
}
//点击连接服务器的响应函数,启动一个新的线程
void CChatRoomServerDlg::OnBnClickedBegin()
{
		UpdateData(true);
		HANDLE hThread=CreateThread(NULL,0,startThread,this,0,NULL);
		if (hThread==NULL)
		{
			appendString(CString(_T("创建启动线程失败!\n")));
		}
		else
		{
			appendString(CString(_T("创建启动线程成功!\n")));
		}
		CloseHandle(hThread);
}
void CChatRoomServerDlg::OnClose()
{
     CDialog::OnClose();
}
void CChatRoomServerDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	CDialog::OnCancel();
}
解决方案

20

建议题主先学会使用抓包软件例如wireshark
不知道有多少前人掉在TCP Socket
send(人多)send(病少)send(财富)
recv(人多病)recv(少财富)
陷阱里面啊!
http://bbs.csdn.net/topics/380167545

20

学会怎么使用抓包工具辅助调试通讯程序的错误比找到错误更重要。

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明关于C++ socket 附送图片的问题。客户端已写好暂时好像没问题。服务器端有错误
喜欢 (0)
[1034331897@qq.com]
分享 (0)