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
学会怎么使用抓包工具辅助调试通讯程序的错误比找到错误更重要。