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
不知道有多少前人掉在TCP Socket
send(人多)send(病少)send(财富)
recv(人多病)recv(少财富)
陷阱里面啊!
http://bbs.csdn.net/topics/380167545
20
学会怎么使用抓包工具辅助调试通讯程序的错误比找到错误更重要。