socket api(socket api文档)

      最后更新:2022-11-09 11:10:40 手机定位技术交流文章

      socket select用法,哪些socket api会阻塞

      默认的socket操作基本都是阻塞的,比如客户端的connect(),服务器端的accept(),基本的套接字读写read()和write()都是阻塞的
      socket select用法,哪些socket api会阻塞

      Winsocket API 使用

      一,WinSock简介 Socket(套接字)最初是由加利福尼亚大学Berkeley(伯克利)分校为UNIX操作系统开发的网络通信接口,随着UNIX的广泛使用,Socket成为当前最流行的网络通信应用程序接口之一。20世纪90年代初,由Sun Microsystems,JSB,FTP software,Microdyne和Microsoft等几家公司共同定制了一套标准,即Windows Socket规范,简称WinSock。VB编写网络程序主要有两种方式:1.winsock控件 2.winsockAPI二,WinSock控件的使用1.WinSock控件的主要属性a.Protocol属性通过Protocol属性可以设置WinSock控件连接远程计算机使用的协议。可选的协议是TCP和UDP对应的VB的常量分别是sckTCPProtocol和sckUDPProtocol,Winsock控件默认协议是TCP。注意:虽然可以在运行时设置协议,但必须在连接未建立或断开连接后。b.SocketHandle属性SocketHandle返回当前socket连接的句柄,这是只读属性。c.RemoteHostIP属性RemoteHostIP属性返回远程计算机的IP地址。在客户端,当使用了控件的Connect方法后,远程计算机的IP地址就赋给了RemoteHostIP属性,而在服务器端,当ConnectRequest事件后,远程计算机(客户端)的IP地址就赋给了这个属性。如果使用的是UDP协议那么当DataArrival事件后,发送UDP报文的计算机的IP才赋给了这个属性。d.ByteReceived属性返回当前接收缓冲区中的字节数e.State属性返回WinSock控件当前的状态常数值描述sckClosed0 缺省值,关闭。SckOpen 1 打开。SckListening 2 侦听sckConnectionPending 3 连接挂起sckResolvingHost4 识别主机。sckHostResolved 5 已识别主机sckConnecting6 正在连接。sckConnected7 已连接。sckClosing8 同级人员正在关闭连接。sckError 9 错误2.WinSock主要方法a.Bind方法用Bind方法可以把一个端口号固定为本控件使用,使得别的应用程序不能再使用这个端口。b.Listen方法Listen方法只在使用TCP协议时有用。它将应用程序置于监听检测状态。c.Connect方法当本地计算机希望和远程计算机建立连接时,就可以调用Connect方法。Connect方法调用的规范为:Connect RemoteHost,RemotePortd.Accept方法当服务器接收到客户端的连接请求后,服务器有权决定是否接受客户端的请求。e.SendData方法当连接建立后,要发送数据就可以调用SendData方法,该方法只有一个参数,就是要发送的数据。f.GetData方法当本地计算机接收到远程计算机的数据时,数据存放在缓冲区中,要从缓冲区中取出数据,可以使用GetData方法。GetData方法调用规范如下:GetData data,[type,][maxLen]它从缓冲区中取得最长为maxLen的数据,并以type类型存放在data中,GetData取得数据后,就把相应的缓冲区清空。g.PeekData方法和GetData方法类似,但PeekData在取得数据后并不把缓冲区清空。3.Winsock控件主要事件a.ConnectRequest事件当本地计算机接收到远程计算机发送的连接请求时,控件的ConnectRequest事件将会被触发。b.SendProgress事件当一端的计算机正在向另一端的计算机发送数据时,SendProgress事件将被触发。SendProgress事件记录了当前状态下已发送的字节数和剩余字节数。c.SendComplete事件当所有数据发送完成时,被触发。d.DataArrival事件当建立连接后,接受到了新数据就会触发这个事件。注意:如果在接受到新数据前,缓冲区中非空,就不会触发这个事件。e.Error事件当在工作中发生任何错误都会触发这个事件。例子见附件三,WinSockAPI的使用1.WSAStartup 函数为了在你的应用程序当中调用任何一个Winsock API 函数,首先第一件事情你就是必须通过WSAStartup函数完成对Winsock 服务的初始化,因此需要调用WSAStartup函数。Declare Function WSAStartup Lib "ws2_32.dll" _(ByVal wVersionRequired As Long, lpWSAData As WSAData) As Long这个函数有两个参数: wVersionRequired 和 lpWSAData。wVersionRequired 参数定义Windows Sockets 提供能使用的最高版本,它的高位字节定义的是次版本号,低位字节定义的是主版本号。下面的2个Winsock版本在VB中使用的例子:初始化1.1版本lngRetVal = WSAStartup(&H101, udtWinsockData)初始化2.2版本lngRetVal = WSAStartup(&H202, udtWinsockData)第二个参数是WSADATA 的数据结构 ,它是接收Windows Sockets 执行时的数据。Type WSADatawVersion As IntegerwHighVersion As IntegerszDescriptionAs String * WSADESCRIPTION_LENszSystemStatus As String * WSASYS_STATUS_LENiMaxSocketsAs IntegeriMaxUdpDgAs IntegerlpVendorInfo As LongEnd Type数据成员的描述在下表中:Field 描述wVersionWindows Sockets 版本信息。wHighVersion 通过加载库文件得到的最高的支持Winsock 的版本,它通常和wVersion值相同。szDescription Windows Sockets 执行时的详细描述szSystemStatus包含了相关的状态和配置的信息iMaxSockets 表示同时打开的socket最大数,为0表示没有限制。iMaxUdpDg 表示同时打开的数据报最大数,为0表示没有限制。lpVendorInfo厂商指定信息预留在Winsock的1.1和2.2版本中没有lpVendorInfo的返回值。因为winsock 2支持多个传输协议,所以iMaxSockets 和iMaxUdpDg只能在仅支持TCP/TP的winsock1.1中使用。为了在Winsock 2中获得这些值,你可以使用WSAEnumProtocols 函数。如果成功或者返回一个错误代码,则函数返回 0。错误代码含义WSASYSNOTREADY指出网络没有为传输准备好。WSAVERNOTSUPPORTED 当前的WinSock实现不支持应用程序指定的Windows Sockets规范版本WSAEINPROGRESS一个阻塞WinSock调用正在进行WSAEPROCLIM 请求的协议没有在系统中配置或没有支持它的实现存在。WSAEFAULT lpWSAData 不是有效的指针2.WSACleanup 函数每次调用了WSAStartup函数,你都需要调用WSACleanup函数,通知系统来卸载库文件及清除已分配的资源,这个函数十分简单,没有任何参数:Declare Function WSACleanup Lib "ws2_32.dll" () As Long3.建立Socket函数Declare Function socket Lib "ws2_32.dll" (ByVal af As Long, _ByVal s_type As Long,ByVal Protocol As Long) As Long函数有3个参数定义建立何种socket,三个参数分别是:ArgumentDescription Enum Typeaf Address family specification. AddressFamilys_typeType specification for the new socket.SocketTypeProtocolProtocol to be used with the socket SocketProtocolthat is specific to the indicated addressfamily.AddressFamily:AF_UNSPEC = 0'/* unspecified */AF_UNIX = 1'/* local to host (pipes, portals) */AF_INET = 2'/* internetwork: UDP, TCP, etc. */AF_IMPLINK = 3'/* arpanet imp addresses */AF_PUP = 4'/* pup protocols: e.g. BSP */AF_CHAOS = 5'/* mit CHAOS protocols */AF_NS = 6'/* XEROX NS protocols */AF_IPX = AF_NS'/* IPX protocols: IPX, SPX, etc. */AF_ISO = 7 '/* ISO protocols */AF_OSI = AF_ISO '/* OSI is ISO */AF_ECMA = 8 '/* european computer manufacturers */AF_DATAKIT = 9'/* datakit protocols */AF_CCITT = 10 '/* CCITT protocols, X.25 etc */AF_SNA = 11'/* IBM SNA */AF_DECnet = 12 '/* DECnet */AF_DLI = 13 '/* Direct data link interface */AF_LAT = 14'/* LAT */AF_HYLINK = 15 '/* NSC Hyperchannel */AF_APPLETALK = 16 '/* AppleTalk */AF_NETBIOS = 17'/* NetBios-style addresses */AF_VOICEVIEW = 18 '/* VoiceView */AF_FIREFOX = 19'/* Protocols from Firefox */AF_UNKNOWN1 = 20'/* Somebody is using this! */AF_BAN = 21 '/* Banyan */AF_ATM = 22 '/* Native ATM Services */AF_INET6 = 23 '/* Internetwork Version 6 */AF_CLUSTER = 24 '/* Microsoft Wolfpack */AF_12844 = 25 '/* IEEE 1284.4 WG AF */AF_MAX = 26Socket types:SOCK_STREAM = 1 ' /* stream socket */SOCK_DGRAM = 2 ' /* datagram socket */SOCK_RAW = 3' /* raw-protocol interface */SOCK_RDM = 4 ' /* reliably-delivered message */SOCK_SEQPACKET = 5' /* sequenced packet stream */Protocols:IPPROTO_IP = 0'/* dummy for IP */IPPROTO_ICMP = 1'/* control message protocol */IPPROTO_IGMP = 2'/* internet group management protocol */IPPROTO_GGP = 3 '/* gateway^2 (deprecated) */IPPROTO_TCP = 6 '/* tcp */IPPROTO_PUP = 12 '/* pup */IPPROTO_UDP = 17 '/* user datagram protocol */IPPROTO_IDP = 22'/* xns idp */IPPROTO_ND = 77 '/* UNOFFICIAL net disk proto */IPPROTO_RAW = 255'/* raw IP packet */IPPROTO_MAX = 256该函数可以建立使用特定协议的网络套接字,例如对于UDP协议可以这样写:s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)4.关闭Socket函数Declare Function closesocket Lib "ws2_32.dll" (ByVal s As Long) As Long函数有一个参数为建立socket时的Handle5.连接函数Declare Function connect Lib "ws2_32.dll" (ByVal s As Long, _ByRef name As sockaddr_in, _ByVal namelen As Long) As Long参数s 连接的socket句柄。name 建立连接的地址。namelen连接地址的长度。返回值成功时返回0。否则返回SOCKET_ERROR以及一个对应的错误号 Err.LastDllError。显然在调用这个函数时我们需要知道socket句柄,将连接的电脑的端口号和主机名称(或主机IP地址)。我们知道Winsock 控件的Connect方法依靠两个变量:RemoteHost 和RemotePort。此方法不需要socket句柄,因其已经被封装在COM对象中。你也许认为connect函数应该也接受相同的变量设置,然而,事实并非如此。connect函数的主机地址和端口号的传送是依靠 sockaddr_in 结构。Public Type sockaddr_insin_family As Integersin_port As Integersin_addr As Longsin_zero(1 To 8) As ByteEnd Type6.套接字帮定函数Declare Function bind Lib "ws2_32.dll" (ByVal s As Long, _ByRef name As sockaddr_in, _ByRef namelen As Long) As Longs是使用Socket函数创建好的套接字,name指向描述通信对象的结构体的指针,namelen是该结构的长度。该结构体中的分量包括:IP地址:对应name.sin_addr.s_addr端口号:对应name.sin_port端口号用于表示同一台计算机上不同的进程(即应用程序),其分配方法有两种:第一种分配方法是,进程让系统为套接字自动分配一端口号,这只要在调用bind前将端口号指定为0即可。由系统自动分配的端口号位于1024~5000之间,而1~1023之间的任一TCP或UDP端口都是保留的,系统不允许任一进程使用保留端口,除非其有效用户ID是零(即超级用户)。第二种分配方法是,进程为套接字指定一特定端口。这对于需要给套接字分配一众所周知的端口的服务器是很有用的。指定范围在1024~65536之间。地址类型:对应name.sin_family,一般都赋成AF_INET,表示是internet地址(即IP 地址)。IP地址通常使用点分表示法表示,但它事实上一个32位的长整数,这两者之间可通过inet_addr()函数转换。7.套接字监听函数Declare Function listen Lib "ws2_32.dll" (ByVal s As Long, ByVal backlog As Long) As Longlisten函数用来设定Socket为监听状态,这种状态表明Socket准备被连接了。注意,此函数一般在服务程序上使用,其中s是使用Socket函数创建好的套接字,backlog参数用于设定等待连接的客户端数。8.接受连接请求Declare Function accept Lib "ws2_32.dll" (ByVal s As Long, ByRef addr As sockaddr_in, _ByRef addrlen As Long) As Long服务端应用程序调用此函数来接受客户端Socket连接请求,accept()函数的返回值为一新的Socket,新Socket就可用来完成服务端和客户端之间的信息传递与接收,而原来Socket仍可以接受其他可户端的连接请求。9.接收信息Declare Function recv Lib "ws2_32.dll" (ByVal s As Long, _ByRef buf As Any, _ByVal buflen As Long, _ByVal flags As Long) As Longs一个已连接的socket的识别符buf 接受到的数据的缓冲区len 缓冲区长度flags 指定从哪调用的标识第一个参数是socket的句柄-为socket函数返回值。那就是说:我们需要告诉recv函数,哪一个socket正访问函数。第二个参数是:函数执行之后能装载一些数据的缓冲区。但它不是必须要有足够的长度接收Winsock缓冲区的所有数据,缓冲区的大小限制为8192 字节 (8 Kbytes)。因此如果Winsock缓冲区的数据的大小大于recv函数的缓冲区,你必需多次调用此函数,直到获取所有的数据。如果应用程序定义缓冲区的长度,则recv函数必须知道缓冲区可以存放多少字节。第三个参数就是为了这个目的。最后一个参数是可选的,今天我们不使用。该参数有两个选择标志: MSG_PEEK 和 MSG_OOB,用于改变函数的行为。MSG_PEEK 从输入数据中取数。数据拷入缓冲区,但不从输入队列中移走。函数返回当前准备接收的字节数。MSG_OOB 处理OOB(Out-of-band带外)数据。在网络上有两种类型的数据包,正常包和带外包。带外包可以通过检验一个TCP/IP包头的一个特定标志来决定。10.发送信息Declare Function send Lib "ws2_32.dll" (ByVal s As Long, _ByRef buf As Any, _ByVal buflen As Long, _ByVal flags As Long) As Long参数参看接收信息四,服务器与客户机交互目前最常用的方法是:服务程序在一个众所周知的地址(其中包括端口信息)监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。这个时刻,服务程序被唤醒并对客户的请求作出适当的反应。注意,服务器与客户机之间的交互可以是面向连接的(基于流套接字),也可以是无连接的(基于数据报套接字)。服务器socket()|bind()|listen() 客户机|| socket()| 建立连接 |accept() <-------------------------connect()| 请求数据 |recv() <----------------------------- send()||处理服务请求||应答数据|send()------------------------------> recv()||close()close()五,其他比较:WinSock控件优点:使用简单,工作量小。缺点:功能少仅支持TCP,UDP协议,需要WinSock控件(系统默认安装不带MSWINSCK.OCX文件)适合于初学者WinSockAPI优点:功能强大,支持多种协议,使用灵.活,WinSockAPI调用的wsock32.dll(28K)或ws2_32.dll(69K)为Windows系统自带函数库不必担心缺少文件。缺点:使用复杂,编程量大,需要一定基础 适合于要求较高的网络程序.
      调用socket类
      Winsocket API 使用

      socket API:connect 出错

      你的局域网连接外网,要有特殊的设置的,因为局域网对外是用的一个ip地址,当然你就连不上了,如实在一个局域网内,每个ip都是唯一的,能成功 一个外网,一个局域网,局域网内有好多ip,但是从外边看是一个ip 192...只能在局域网中使用
      socket API:connect 出错

      socket select用法,哪些socket api会阻塞

      默认的socket操作基本都是阻塞的,比如客户端的connect(),服务器端的accept(),基本的套接字读写read()和write()都是阻塞的
      socket select用法,哪些socket api会阻塞

      试用socket api借口设计一个文件传送软件,并编程实现,可自定义文件传输的通信。

      我用C++写的,可以调试的!要加分啊啊!记得啊!呵呵... 代码如下://功能:利用SOCKET传送文件的代码/*server.h*/#pragma comment(lib, "WS2_32")#include#include//#include#include#ifndef COMMONDEF_H#define COMMONDEF_H#define MAX_PACKET_SIZE 10240 // 数据包的最大长度,单位是sizeof(char)#define MAXFILEDIRLENGTH 256 // 存放文件路径的最大长度#define PORT 4096 // 端口号#define SERVER_IP "127.0.0.1" // server端的IP地址// 各种消息的宏定义#define INVALID_MSG -1 // 无效的消息标识#define MSG_FILENAME 1 // 文件的名称#define MSG_FILELENGTH 2 // 传送文件的长度#define MSG_CLIENT_READY 3 // 客户端准备接收文件#define MSG_FILE 4 // 传送文件#define MSG_SENDFILESUCCESS 5 // 传送文件成功#define MSG_OPENFILE_ERROR 10 // 打开文件失败,可能是文件路径错误找不到文件等原因#define MSG_FILEALREADYEXIT_ERROR 11 // 要保存的文件已经存在了class CCSDef{public:#pragma pack(1) // 使结构体的数据按照1字节来对齐,省空间// 消息头struct TMSG_HEADER{char cMsgID; // 消息标识TMSG_HEADER(char MsgID = INVALID_MSG): cMsgID(MsgID){}};// 请求传送的文件名// 客户端传给服务器端的是全路径名称// 服务器传回给客户端的是文件名struct TMSG_FILENAME : public TMSG_HEADER{char szFileName[256]; // 保存文件名的字符数组TMSG_FILENAME(): TMSG_HEADER(MSG_FILENAME){}};// 传送文件长度struct TMSG_FILELENGTH : public TMSG_HEADER{long lLength;TMSG_FILELENGTH(long length): TMSG_HEADER(MSG_FILELENGTH), lLength(length){}};// Client端已经准备好了,要求Server端开始传送文件struct TMSG_CLIENT_READY : public TMSG_HEADER{TMSG_CLIENT_READY(): TMSG_HEADER(MSG_CLIENT_READY){}};// 传送文件struct TMSG_FILE : public TMSG_HEADER{union // 采用union保证了数据包的大小不大于MAX_PACKET_SIZE * sizeof(char){char szBuff[MAX_PACKET_SIZE];struct{int nStart;int nSize;char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)];}tFile;};TMSG_FILE(): TMSG_HEADER(MSG_FILE){}};// 传送文件成功struct TMSG_SENDFILESUCCESS : public TMSG_HEADER{TMSG_SENDFILESUCCESS(): TMSG_HEADER(MSG_SENDFILESUCCESS){}};// 传送出错信息,包括:// MSG_OPENFILE_ERROR:打开文件失败// MSG_FILEALREADYEXIT_ERROR:要保存的文件已经存在了struct TMSG_ERROR_MSG : public TMSG_HEADER{TMSG_ERROR_MSG(char cErrorMsg): TMSG_HEADER(cErrorMsg){}};#pragma pack()};#endif/*server.h*/#pragma comment(lib, "WS2_32")#include#include//#include#include#ifndef COMMONDEF_H#define COMMONDEF_H#define MAX_PACKET_SIZE 10240 // 数据包的最大长度,单位是sizeof(char)#define MAXFILEDIRLENGTH 256 // 存放文件路径的最大长度#define PORT 4096 // 端口号#define SERVER_IP "127.0.0.1" // server端的IP地址// 各种消息的宏定义#define INVALID_MSG -1 // 无效的消息标识#define MSG_FILENAME 1 // 文件的名称#define MSG_FILELENGTH 2 // 传送文件的长度#define MSG_CLIENT_READY 3 // 客户端准备接收文件#define MSG_FILE 4 // 传送文件#define MSG_SENDFILESUCCESS 5 // 传送文件成功#define MSG_OPENFILE_ERROR 10 // 打开文件失败,可能是文件路径错误找不到文件等原因#define MSG_FILEALREADYEXIT_ERROR 11 // 要保存的文件已经存在了class CCSDef{public:#pragma pack(1) // 使结构体的数据按照1字节来对齐,省空间// 消息头struct TMSG_HEADER{char cMsgID; // 消息标识TMSG_HEADER(char MsgID = INVALID_MSG): cMsgID(MsgID){}};// 请求传送的文件名// 客户端传给服务器端的是全路径名称// 服务器传回给客户端的是文件名struct TMSG_FILENAME : public TMSG_HEADER{char szFileName[256]; // 保存文件名的字符数组TMSG_FILENAME(): TMSG_HEADER(MSG_FILENAME){}};// 传送文件长度struct TMSG_FILELENGTH : public TMSG_HEADER{long lLength;TMSG_FILELENGTH(long length): TMSG_HEADER(MSG_FILELENGTH), lLength(length){}};// Client端已经准备好了,要求Server端开始传送文件struct TMSG_CLIENT_READY : public TMSG_HEADER{TMSG_CLIENT_READY(): TMSG_HEADER(MSG_CLIENT_READY){}};// 传送文件struct TMSG_FILE : public TMSG_HEADER{union // 采用union保证了数据包的大小不大于MAX_PACKET_SIZE * sizeof(char){char szBuff[MAX_PACKET_SIZE];struct{int nStart;int nSize;char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)];}tFile;};TMSG_FILE(): TMSG_HEADER(MSG_FILE){}};// 传送文件成功struct TMSG_SENDFILESUCCESS : public TMSG_HEADER{TMSG_SENDFILESUCCESS(): TMSG_HEADER(MSG_SENDFILESUCCESS){}};// 传送出错信息,包括:// MSG_OPENFILE_ERROR:打开文件失败// MSG_FILEALREADYEXIT_ERROR:要保存的文件已经存在了struct TMSG_ERROR_MSG : public TMSG_HEADER{TMSG_ERROR_MSG(char cErrorMsg): TMSG_HEADER(cErrorMsg){}};#pragma pack()};#endif/*server.cpp*/#include "server.h"char g_szNewFileName[MAXFILEDIRLENGTH];char g_szBuff[MAX_PACKET_SIZE + 1];long g_lLength;char* g_pBuff = NULL;// 初始化socket库bool InitSocket();// 关闭socket库bool CloseSocket();// 解析消息进行相应的处理bool ProcessMsg(SOCKET sClient);// 监听Client的消息void ListenToClient();// 打开文件bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient);// 传送文件bool SendFile(SOCKET sClient);// 读取文件进入缓冲区bool ReadFile(SOCKET sClient);int main(){InitSocket();ListenToClient();CloseSocket();return 0;}void ListenToClient(){// 创建socket套接字SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (SOCKET_ERROR == sListen){printf("Init Socket Error!n");return;}// 绑定socket到一个本地地址sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.S_un.S_addr = INADDR_ANY;if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR){printf("Bind Error!n");return;}// 设置socket进入监听状态if (::listen(sListen, 10) == SOCKET_ERROR){printf("Listen Error!n");return;}printf("Listening To Client...n");// 循环接收client端的连接请求sockaddr_in ClientAddr;int nAddrLen = sizeof(sockaddr_in);SOCKET sClient;while (INVALID_SOCKET == (sClient = ::accept(sListen, (sockaddr*)&ClientAddr, &nAddrLen))){}while (true == ProcessMsg(sClient)){}// 关闭同客户端的连接::closesocket(sClient);::closesocket(sListen);}bool InitSocket(){// 初始化socket dllWSADATA wsaData;WORD socketVersion = MAKEWORD(2, 2);if (::WSAStartup(socketVersion, &wsaData) != 0){printf("Init socket dll errorn");return false;}return true;}bool CloseSocket(){// 释放winsock库::WSACleanup();if (NULL != g_pBuff){delete [] g_pBuff;g_pBuff = NULL;}return true;}bool ProcessMsg(SOCKET sClient){int nRecv = ::recv(sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);if (nRecv > 0){g_szBuff[nRecv] = '';}// 解析命令CCSDef::TMSG_HEADER* pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;switch (pMsgHeader->cMsgID){case MSG_FILENAME: // 文件名{OpenFile(pMsgHeader, sClient);}break;case MSG_CLIENT_READY: // 客户端准备好了,开始传送文件{SendFile(sClient);}break;case MSG_SENDFILESUCCESS: // 传送文件成功{printf("Send File Success!n");return false;}break;case MSG_FILEALREADYEXIT_ERROR: // 要保存的文件已经存在了{printf("The file reay to send already exit!n");return false;}break;}return true;}bool ReadFile(SOCKET sClient){if (NULL != g_pBuff){return true;}// 打开文件FILE *pFile;if (NULL == (pFile = fopen(g_szNewFileName, "rb"))) // 打开文件失败{printf("Cannot find the file, request the client input file name againn");CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_OPENFILE_ERROR);::send(sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);return false;}// 把文件的长度传回到client去fseek(pFile, 0, SEEK_END);g_lLength = ftell(pFile);printf("File Length = %dn", g_lLength);CCSDef::TMSG_FILELENGTH tMsgFileLength(g_lLength);::send(sClient, (char*)(&tMsgFileLength), sizeof(CCSDef::TMSG_FILELENGTH), 0);// 处理文件全路径名,把文件名分解出来char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFname[_MAX_FNAME], szExt[_MAX_EXT];_splitpath(g_szNewFileName, szDrive, szDir, szFname, szExt);strcat(szFname,szExt);CCSDef::TMSG_FILENAME tMsgFileName;strcpy(tMsgFileName.szFileName, szFname);printf("Send File Name: %sn", tMsgFileName.szFileName);::send(sClient, (char*)(&tMsgFileName), sizeof(CCSDef::TMSG_FILENAME), 0);// 分配缓冲区读取文件内容g_pBuff = new char[g_lLength + 1];if (NULL == g_pBuff){return false;}fseek(pFile, 0, SEEK_SET);fread(g_pBuff, sizeof(char), g_lLength, pFile);g_pBuff[g_lLength] = '';fclose(pFile);return true;}// 打开文件bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient){CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;// 对文件路径名进行一些处理char *p1, *p2;for (p1 = pRequestFilenameMsg->szFileName, p2 = g_szNewFileName;'' != *p1;++p1, ++p2){if ('n' != *p1){*p2 = *p1;}if ('\' == *p2){*(++p2) = '\';}}*p2 = '';ReadFile(sClient);return true;}// 传送文件bool SendFile(SOCKET sClient){if (NULL == g_pBuff){ReadFile(sClient);}int nPacketBufferSize = MAX_PACKET_SIZE - 2 * sizeof(int); // 每个数据包存放文件的buffer大小// 如果文件的长度大于每个数据包所能传送的buffer长度那么就分块传送for (int i = 0; i < g_lLength; i += nPacketBufferSize){CCSDef::TMSG_FILE tMsgFile;tMsgFile.tFile.nStart = i;if (i + nPacketBufferSize + 1> g_lLength){tMsgFile.tFile.nSize = g_lLength - i;}else{tMsgFile.tFile.nSize = nPacketBufferSize;}//printf("start = %d, size = %dn", tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);memcpy(tMsgFile.tFile.szBuff, g_pBuff + tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);::send(sClient, (char*)(&tMsgFile), sizeof(CCSDef::TMSG_FILE), 0);Sleep(0.5);}delete [] g_pBuff;g_pBuff = NULL;return true;}/*client.h同server.h*//*client.cpp*/#include "client.h"long g_lLength = 0;char* g_pBuff = NULL;char g_szFileName[MAXFILEDIRLENGTH];char g_szBuff[MAX_PACKET_SIZE + 1];SOCKET g_sClient;// 初始化socket库bool InitSocket();// 关闭socket库bool CloseSocket();// 把用户输入的文件路径传送到server端bool SendFileNameToServer();// 与server端连接bool ConectToServer();// 打开文件失败bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader);// 分配空间以便写入文件bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader);// 写入文件bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader);// 处理server端传送过来的消息bool ProcessMsg();int main(){InitSocket();ConectToServer();CloseSocket();return 0;}// 初始化socket库bool InitSocket(){// 初始化socket dllWSADATA wsaData;WORD socketVersion = MAKEWORD(2, 2);if (::WSAStartup(socketVersion, &wsaData) != 0){printf("Init socket dll errorn");exit(-1);}return true;}// 关闭socket库bool CloseSocket(){// 关闭套接字::closesocket(g_sClient);// 释放winsock库::WSACleanup();return true;}// 与server端连接进行文件的传输bool ConectToServer(){// 初始化socket套接字if (SOCKET_ERROR == (g_sClient = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))){printf("Init Socket Error!n");exit(-1);}sockaddr_in servAddr;servAddr.sin_family = AF_INET;servAddr.sin_port = htons(PORT);servAddr.sin_addr.S_un.S_addr = ::inet_addr(SERVER_IP);if (INVALID_SOCKET == (::connect(g_sClient, (sockaddr*)&servAddr, sizeof(sockaddr_in)))){printf("Connect to Server Error!n");exit(-1);}// 输入文件路径传输到server端SendFileNameToServer();// 接收server端传过来的信息,直到保存文件成功为止while (true == ProcessMsg()){}return true;}// 把用户输入的文件路径传送到server端bool SendFileNameToServer(){char szFileName[MAXFILEDIRLENGTH];printf("Input the File Directory: ");fgets(szFileName, MAXFILEDIRLENGTH, stdin);// 把文件路径发到server端CCSDef::TMSG_FILENAME tMsgRequestFileName;strcpy(tMsgRequestFileName.szFileName, szFileName);if (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgRequestFileName), sizeof(CCSDef::TMSG_FILENAME), 0)){printf("Send File Name Error!n");exit(-1);}return true;}// 处理server端传送过来的消息bool ProcessMsg(){CCSDef::TMSG_HEADER *pMsgHeader;int nRecv = ::recv(g_sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;switch (pMsgHeader->cMsgID){case MSG_OPENFILE_ERROR: // 打开文件错误{OpenFileError(pMsgHeader);}break;case MSG_FILELENGTH: // 文件的长度{if (0 == g_lLength){g_lLength = ((CCSDef::TMSG_FILELENGTH*)pMsgHeader)->lLength;printf("File Length: %dn", g_lLength);}}break;case MSG_FILENAME: // 文件名{return AllocateMemoryForFile(pMsgHeader);}break;case MSG_FILE: // 传送文件,写入文件成功之后退出这个函数{if (WriteToFile(pMsgHeader)){return false;}}break;}return true;}// 打开文件失败bool OpenFileError(CCSDef::TMSG_HEADER *pMsgHeader){if (NULL != g_pBuff)return true;assert(NULL != pMsgHeader);printf("Cannot find file!Please input again!n");// 重新输入文件名称SendFileNameToServer();return true;}// 查找是否已经存在了要保存的文件,同时分配缓冲区保存文件bool AllocateMemoryForFile(CCSDef::TMSG_HEADER *pMsgHeader){assert(NULL != pMsgHeader);if (NULL != g_pBuff){return true;}CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;printf("File Name: %sn", pRequestFilenameMsg->szFileName);// 把文件的路径设置为C盘根目录下strcpy(g_szFileName, "c:\");strcat(g_szFileName, pRequestFilenameMsg->szFileName);// 查找相同文件名的文件是否已经存在,如果存在报错退出FILE* pFile;if (NULL != (pFile = fopen(g_szFileName, "r"))){// 文件已经存在,要求重新输入一个文件printf("The file already exist!n");CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_FILEALREADYEXIT_ERROR);::send(g_sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);fclose(pFile);return false;}// 分配缓冲区开始接收文件,如果分配成功就给server端发送开始传送文件的要求g_pBuff = new char[g_lLength + 1];if (NULL != g_pBuff){memset(g_pBuff, '', g_lLength + 1);printf("Now ready to get the file %s!n", pRequestFilenameMsg->szFileName);CCSDef::TMSG_CLIENT_READY tMsgClientReady;if (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgClientReady), sizeof(CCSDef::TMSG_CLIENT_READY), 0)){printf("Send Error!n");exit(-1);}}else{printf("Alloc memory for file error!n");exit(-1);}return true;}// 写入文件bool WriteToFile(CCSDef::TMSG_HEADER *pMsgHeader){assert(NULL != pMsgHeader);CCSDef::TMSG_FILE* pMsgFile = (CCSDef::TMSG_FILE*)pMsgHeader;int nStart = pMsgFile->tFile.nStart;int nSize = pMsgFile->tFile.nSize;memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);if (0 == nStart){printf("Saving file into buffer...n");}memcpy(g_pBuff + nStart, pMsgFile->tFile.szBuff, nSize);//printf("start = %d, size = %dn", nStart, nSize);// 如果已经保存到缓冲区完毕就写入文件if (nStart + nSize >= g_lLength){printf("Writing to disk....n");// 写入文件FILE* pFile;pFile = fopen(g_szFileName, "w+b");fwrite(g_pBuff, sizeof(char), g_lLength, pFile);delete [] g_pBuff;g_pBuff = NULL;fclose(pFile);// 保存文件成功传送消息给server退出serverCCSDef::TMSG_SENDFILESUCCESS tMsgSendFileSuccess;while (SOCKET_ERROR == ::send(g_sClient, (char*)(&tMsgSendFileSuccess), sizeof(CCSDef::TMSG_SENDFILESUCCESS), 0)){}printf("Save the file %s success!n", g_szFileName);return true;}else{return false;} }
      试用socket api借口设计一个文件传送软件,并编程实现,可自定义文件传输的通信。

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/44187.html

          热门文章

          文章分类