利用流式套接字传输数据文件
2015-12-18 21:39
281 查看
服务器端:
1>建立一个基于对话框的应用程序StreamSocketServer
2>设计界面,将“确定”按钮的标题改为“启动”
3>双击“启动按钮”,添加以下代码,初始化网络,启动服务器监听
4>注册自定义消息NETWORK_EVENT, 并添加其相应处理函数OnNetEvent
在StreamSocketServerDlg.h中添加成员函数:
在StreamSocketServerDlg.cpp中实现:
5>添加客户端的连接请求处理函数
6>添加发送数据文件函数
客户端:
1>新建一个基于对话框的程序StreamSocketClient
2>设计界面,添加编辑框m_CtrlPAddress输入IP地址,m_iPort用于输入端口,将“确定”按钮改为“连接”,“取消”改为“关闭”
3>双击“连接”,添加以下代码:
4>添加接收文件函数
界面:
1>建立一个基于对话框的应用程序StreamSocketServer
2>设计界面,将“确定”按钮的标题改为“启动”
3>双击“启动按钮”,添加以下代码,初始化网络,启动服务器监听
void CStreamSocketServerDlg::OnOK() { // TODO: Add extra validation here WSADATA wsaData; /* typedef struct WSAData { WORD wVersion; //为希望使用的Winsock版本 WORD wHeighVersion; //返回现有的Winsock库的最高版本 char szDescription[WSADESCRIPTION_LEN+1]; //通常不用 char szSystemStatus[WSASYS_STATUE_LEN+1]; //通常不用 unsigned short iMaxSockets; //可同时打开的套接字数 unsigend short iMaxUdpDg; //数据报最大长度 char FAR * lpVendorInfo; //为制定Winsock实施方案的厂商信息所预留,在任何一个Win32平 台上都没有使用这个字段] } */ //初始化TCP协议 BOOL ret = WSAStartup(MAKEWORD(2, 2), &wsaData); /* int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); wVersionRequested 用户制定准备加载的Winsock库的版本. 宏定义MAKEWOARD(X, Y) 获得wVersionRequested的正确值, [X为高位字节,Y为低位字节, lpWSAData 为指向LPWSDATA结构的指针,包含了加载库版本有关的信息 */ if(ret != 0) { MessageBox("初始化网络协议失败!"); return; } //创建服务器端套接字 ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* SOCKET socket(int af, int type, int protocol); af用于制定网络类型,一般取AF_INET,表示该套接字在Internet域中进行通信 type用于指定套接字类型,SOCK_STREAM表示流套接字, SOCK_DGRAM表示数据报套接字 protocol用于指定网络协议,默认为0,表示TCP/IP协议 */ if(ServerSock == INVALID_SOCKET) { MessageBox("创建失败!"); closesocket(ServerSock); } //绑定到本地一个端口上 sockaddr_in localaddr; /* struct sockaddr_in { short sin_family; //必须为AF_INET,表示该socket处于Internet域 u_short sin_port; //用于指定服务端口 struct in_addr sin_addr; //把一个IP地址保存为一个4字节的数,其数据类型为无符号长整数 类型 char sin_zero[8]; //只充当填充项角色,使得与sockaddr长度保持一致 } */ localaddr.sin_family = AF_INET; localaddr.sin_port = htons(6000); /* htons的功能:将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian) 参数u_short hostshort:16位无符号整数 返回值:TCP/IP网络字节顺序. */ localaddr.sin_addr.s_addr = 0; if(bind(ServerSock, (struct sockaddr *)&localaddr, sizeof(sockaddr)) == SOCKET_ERROR) { MessageBox("绑定地址失败!"); closesocket(ServerSock); WSACleanup(); return; } /* int bind(SOCKET s, const struct sockaddr FAR *name, int namelen); s标识一个未捆绑套接字的句柄,用来等待客户机的连接 name是赋予套接字的地址 */ //将ServerSock设置为异步非阻塞模式,并为它注册各种网络异步事件 //m_hWnd为应用程序的主对话框或主窗口的句柄 if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT, FD_ACCEPT|FD_CLOSE|FD_READ|FD_WRITE) == SOCKET_ERROR) { MessageBox("注册失败!"); WSACleanup(); return; } /* int WSAAsyncSelect( SOCKET s, HWND hWnd, //接收消息的窗口句柄 unsigned int wMsg; //网络事件发生时要接收的消息 long lEvent; //被注册的网络事件 ); lEvent参数值及意义: FD_READ 希望在套接字s收到数据时收到消息 FD_WRITE 希望在套接字s可以发送数据时收到消息 FD_ACCEPT 希望在套接字s收到连接请求是收到消息 FD_CONNECT 希望在套接字s连接成功是收到消息 FD_CLOSE 希望在套接字s连接关闭是收到消息 FD_OOB 希望在套接字s收到外带数据时收到消息 NETWORK_EVENT 为自定义消息,并添加其响应处理函数OnNetEvent(WPARAM wParam, LPRAM lParam) */ listen(ServerSock, 31); //设置为侦听模式,最多要31个连接 MessageBox("服务启动成功!"); CDialog::OnOK(); }
4>注册自定义消息NETWORK_EVENT, 并添加其相应处理函数OnNetEvent
在StreamSocketServerDlg.h中添加成员函数:
void OnNetEvent(WPARAM wParam, LPARAM lParam);
在StreamSocketServerDlg.cpp中实现:
void CStreamSocketServerDlg::OnNetEvent(WPARAM wParam, LPARAM lParam) { int iEvent = WSAGETSELECTEVENT(lParam); //调用Winsock API函数,得到网络事件类型 SOCKET CurSock = (SOCKET)wParam; //调用Winsock API函数,得到此事件的客户端套接字 switch(iEvent) { case FD_ACCEPT: //客户端连接请求事件 TRACE("Get client call!\n"); OnAccept(CurSock); break; case FD_CLOSE: //客户端断开事件 OnClose(CurSock); break; case FD_READ: //客户端网络包到达事件 OnReceive(CurSock); break; case FD_WRITE: //发送网络数据事件 break; default: break; } }
5>添加客户端的连接请求处理函数
void CStreamSocketServerDlg::OnAccept(SOCKET CurSock) { SOCKET TempSock; struct socaaddr_in ConSocketAddr; int addrlen; addrlen = sizeof(ConSocketAddr); TempSock = accept(CurSock, (struct sockaddr*)&ConSocketAddr, &addrlen); /* 用于接受连接请求 SCOKET accept( SCOKET s, //Socket的识别码 struct sockaddr FAR * addr, //存放连接的客户端地址 int FAR* addrlen //地址长度 ) */ if(ConnectSock == INVALID_SOCKET) MessageBox("INVALID_SOCKET"); TRACE("建立新连接,sock:%d\n", TempSock); //输出调试信息 inet_ntoa(ConSocketAddr.sin_addr); ConnectSock = TempSock; char *filename = "c:\\001.doc"; if(!SendFile(filename, ConnectSock)) MessageBox("发送失败!"); }
6>添加发送数据文件函数
BOOL CStreamSocketServerDlg::SendFile(char *name, SOCKET conn) { char *FileName = name; SOCKET TcpConn = conn; CFile file; if(!file.Open(FileName, CFile::modeRead)) { printf("打开%s失败!\n", FileName); return FALSE; } int NumBytes; //用来保存每次传送时数据块的大小 UINT Total = 0; //用来保存套接字已经传送的总的字节数 int BufSize = 1024; //发送缓冲区的大小 int Size = BufSize; //读取文件的大小 LPBYTE pBuf = new BYTE[BufSize]; //发送缓冲区 DWORD dwTemp = 0; UINT FileLength = file.GetLength(); //得到文件的大小并发送出去 send(TcpConn, (char *)&FileLength, 4, 0); /* int send( //返回发送的字符总数 SOCKET s, const char FAR *buf, //存放要传送的资料的暂存区 int len, //buf的长度 int flags //此函数被调用的方式 ) */ file.SeekToBegin(); while(Total < FileLength) { if((int)(FileLength - Total) < Size) Size = FileLength - Total; Size = file.Read(pBuf, Size); /* virtual UINT Read( void* lpBuf, //是把资源读入哪里 UINT nCount //是读入的字节数 ); */ NumBytes = send(TcpConn, (char *)pBuf, Size, 0); if(NumBytes == SOCKET_ERROR) { send(TcpConn, "ERROR", sizeof("ERROR")+1, 0); return FALSE; } Total += NumBytes; file.Seek(Total, CFile::begin); } delete[] pBuf; file.Close(); closesocket(TcpConn); return TRUE; }
客户端:
1>新建一个基于对话框的程序StreamSocketClient
2>设计界面,添加编辑框m_CtrlPAddress输入IP地址,m_iPort用于输入端口,将“确定”按钮改为“连接”,“取消”改为“关闭”
3>双击“连接”,添加以下代码:
void CStreamSocketClientDlg::OnOK() { // TODO: Add extra validation here UpdateData(FALSE); //初始化对话框中的数据 WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); //连接两个给定的无符号参数 err = WSAStartUp(wVersionReqested, &wsaData); if(err != 0) return; if(LOBYTE(wsaData.wVersion) != 1) || HIBYTE(wsaData.wVersion) != 1) //LOBYTE()得到一个16bit数 最低(最右边)那个字节 { WSACleanup(); return; } SOCKET TcpClient = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN SerAddr; SerAddr.sin_family AF_INET; SerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); /* struct in_addr{ union{ struct{ u_char s_b1, s_b2, s_b3, s_b4;} S_un_b; struct{u_short S_w1, S_w2} S_un_w; u_long S_addr; }S_un; }; */ SerAddr.sin_port = htos(m_iPort); connect(TcpClient, (SOCKADDR*)&SerAddr, sizeof(SerAddr)); //与服务器端建立连接 /* int connect( SOCKET s, //服务器端Socket的识别码 const struct sockaddr FAR* name, //Socket想要连接的对方地址 int namelen //地址长度 ) */ long FileLength; recv(TcpClient, (char *)&FileLength, sizeof(long), 0); /* int recv( SOCKET s, char FAR *buf, //存放接收到资料的暂存区 int len, //buf的长度 int flags //此函数的调用方式 ) */ if(RecvFile("c:\\003.doc", TcpClient, FileLength)) MessageBox("传输结束!\n"); else MessageBox("传输失败!\n"); CDialog::OnOK(); }
4>添加接收文件函数
BOOL CStreamSocketClientDlg::RecvFile(char *name, SOCKET conn, UINT filelen) { char *FileName = name; SOCKET client = conn; CFile file; if(!file.Open(FileName, CFile::modeWrite)) { printf("打开%s失败!\n", FileName); return FALSE; } int NumBytes; //用来保存每次接收时数据块的大小 UINT Total = 0; //用来保存套接字已经接收的总的字节数 int BufSize = 1024; //接收缓冲区的大小 int Size = BufSize; //写文件的大小 LPBYTE pBuf = new BYTE[BufSize]; //接收缓冲区 DWORD dwTemp = 0; file.SeekToBegin(); while(Total < filelen) { if((int)(filelen - Total) < Size) Size = filelen - Total; NumBytes = recv(client, (char *)pBuf, Size, 0); if(NumBytes == SOCKET_ERROR) { printf("接收失败!\n"); return FALSE; } file.Write(pBuf, NumBytes); /* virtual void Write( const void* lpBuf, //存储要写的东西的字符串或者字符数组 UINT nCount ); //要从这个字符串或者字符数组中写多少个字符到文件中 */ Total += NumBytes; file.Seek(Total, CFile::begin); } delete[] pBuf; file.Close(); closesocket(client); return TRUE; }
界面:
相关文章推荐
- Spring 之注解事务 @Transactional
- NowCoder猜想--牛客网题
- 7、Android多线程与线程池
- leetcode Count and Say
- 科普:手机5G WiFi 那些事
- 数组的注意点
- maven 学习
- 图像分析之结构张量
- Auto Layout(NSLayoutConstraint)
- hdu 3790 最短路径问题(距离和费用)
- Java多线程总结(6)— 线程池的基本使用和执行流程分析
- 多媒体开发之ftp---一个很现实的需求把ftp转换成rtmp协议做点播
- 博客栏目
- KNN算法--物以类聚,人以群分
- 听说
- tiny4412 ubuntudesktop更新源(old)
- [JZOJ3975] 串
- UML之用例图
- caffe
- xml学习笔记