基于TCP(UDP)的网络socket-编程_第1页
基于TCP(UDP)的网络socket-编程_第2页
基于TCP(UDP)的网络socket-编程_第3页
基于TCP(UDP)的网络socket-编程_第4页
基于TCP(UDP)的网络socket-编程_第5页
已阅读5页,还剩72页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于TCP(UDP)

网络安全(socket)编程1.套接字(socket)的引入 为了能够方便的开发网络应用软件,由美国伯克利大学在Unix上推出了一种应用程序访问通信协议的操作系统调用socket(套接字)。socket的出现,使程序员可以很方便地访问TCP/IP,从而开发各种网络应用的程序。 随着Unix的应用推广,套接字在编写网络软件中得到了极大的普及。后来,套接字又被引进了Windows等操作系统,成为开发网络应用程序的非常有效快捷的工具。

套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。套接字通常只与同一区域的套接字交换数据(也有可能跨区域通信,但这只在执行了某种转换进程后才能实现)。

WindowsSockets只支持一个通信区域:

网际域(AF_INET),这个域被使用网际协议簇通信的进程使用。2.网络字节顺序 不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。 基于Intel的CPU,即我们常用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要指定网络字节顺序。

TCP/IP协议使用16位整数和32位整数的高位先存格式。客户机/服务器模式

1)在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式(client/server),即客户向服务器提出请求,服务器接收到请求后,提供相应的服务。

2)客户机/服务器模式的建立基于以下两点:首先,建立网络的起因是网络中软硬件资源、运算能力和信息不均等,需要共享,从而造就拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。其次,网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP。客户机/服务器模式客户机/服务器模式在操作过程中采取的是主动请求的方式。

首先服务器方要先启动,并根据请求提供相应的服务:①打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。②等待客户请求到达该端口。③接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一个新的进程(或线程)来处理这个客户请求。新进程(或线程)处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。④返回第二步,等待另一客户请求。⑤关闭服务器。

客户方:①打开一个通信通道,并连接到服务器所在主机的特定端口。②向服务器发服务请求报文,等待并接收应答;继续提出请求。③请求结束后关闭通信通道并终止。WindowsSockets的实现 WindowsSockets是MicrosoftWindows的网络程序设计接口,它是从BerkeleySockets扩展而来的,以动态链接库的形式提供给我们使用。WindowsSockets在继承了BerkeleySockets主要特征的基础上,又对它进行了重要扩充。这些扩充主要是提供了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。 WindowsSockets1.1和BerkeleySockets都是基于TCP/IP协议的;WindowsSockets2从WindowsSockets1.1发展而来,与协议无关并向下兼容,可以使用任何底层传输协议提供的通信能力,来为上层应用程序完成网络数据通讯,而不关心底层网络链路的通讯情况,真正实现了底层网络通讯对应用程序的透明。套接字的类型1)流式套接字(SOCK_STREAM) 提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。2)数据报式套接字(SOCK_DGRAM) 提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。3)原始套接字(SOCK_RAW)网络安全编程基本函数1、SOCKET类型

SOCKET是socket套接字类型,在WINSOCK2.H中有如下定义: typedefunsignedintu_int;

typedefu_int

SOCKET;

可知套接字实际上就是一个无符号整型,它将被Socket环境管理和使用。

套接字的使用流程:

被创建、设置、用来发送和接收数据,最后会被关闭。

2WORD类型、MAKEWORD、LOBYTE和HIBYTE宏

WORD类型是一个16位的无符号整型,在WTYPES.H中被定义为: typedefunsignedshortWORD;

目的: 是提供两个字节的存储,在Socket中这两个字节可以表示主版本号和副版本号。使用MAKEWORD宏可以给一个WORD类型赋值。

例如: 要表示主版本号2,副版本号0,可以使用以下代码: WORDwVersionRequested; wVersionRequested=MAKEWORD(2,0); 注意低位内存存储主版本号2,高位内存存储副版本号0,其值为0x0002。 使用宏LOBYTE可以读取WORD的低位字节,HIBYTE可以读取高位字节。3WSAStartup()函数WSAStartup函数被用来初始化Socket环境,它的定义如下:intPASCALFARWSAStartup(WORDwVersionRequired,LPWSADATAlpWSAData);返回值:

整型;若返回值为0,则初始化成功,若不为0则失败。调用方式:

PASCAL(即标准类型,PASCAL等于__stdcall)参数:

第一个参数为WORD类型,指明了Socket的版本号 第二个参数为WSADATA类型的指针。4WSADATA类型和LPWSADATA类型

WSADATA类型是一个结构,描述了Socket库的一些相关信息,其结构定义如下:typedefstructWSAData{WORDwVersion;WORDwHighVersion;charszDescription[WSADESCRIPTION_LEN+1];charszSystemStatus[WSASYS_STATUS_LEN+1];unsignedshortiMaxSockets;unsignedshortiMaxUdpDg;charFAR*lpVendorInfo;}WSADATA;typedefWSADATAFAR*LPWSADATA;wVersion: 存储了Socket的版本类型。LPWSADATA: 是WSADATA的指针类型。 它们不用程序员手动填写,而是通过Socket的初始化函数WSAStartup读取出来。5socket()函数socket的创建函数,其定义为:SOCKETPASCALFARsocket(intaf,inttype,intprotocol);intaf: 代表网络地址族,目前只有一种取值是有效的,即AF_INET,代表internet地址族;inttype: 代表网络协议类型,SOCK_DGRAM代表UDP协议,SOCK_STREAM代表TCP协议;intprotocol: 指定网络地址族的特殊协议,目前无用,赋值0即可。

创建成功则返回值为SOCKET,若返回INVALID_SOCKET则失败。6

bind()函数intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数:

s:

指定要绑定的套接字

conststructsockaddrFAR*name:

了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同

Namelen: 指定该地址结构的长度。6

bind()函数intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);sockaddr结构定义如下:structsockaddr{u_shortsa_family;charsa_data[14];};sa_family: 指定该地址家族,在这里必须设为AF_INET。sa_data: 仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。除了sa_family外,sockaddr是按网络字节顺序表示的。在TCP/IP中,我们可以用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。

续:sockaddr_in的定义如下:

structsockaddr_in{

shortsin_family;

unsignedshortsin_port;

struct

in_addrsin_addr;

charsin_zero[8];};

其中,sin_family表示地址族,对于IP地址,sin_family成员将一直是AF_INET。成员sin_port指定的是将要分配给套接字的端口。成员sin_addr给出的是套接字的主机IP地址。而成员sin_zero只是一个填充数,以使sockaddr_in结构和sockaddr结构的长度一样。如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。多数情况下,每个机器只有一个IP地址,但有的机器可能会有多个网卡,每个网卡都可以有自己的IP地址,用INADDR_ANY可以简化应用程序的编写。将地址指定为INADDR_ANY,允许一个独立应用接受发自多个接口的回应。如果我们只想让套接字使用多个IP中的一个地址,就必须指定实际地址,要做到这一点,可以用inet_addr()函数,这个函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址(如192.168.0.16)。而且inet_addr()函数会返回一个适合分配给S_addr的u_long类型的数值。inet_ntoa()函数会完成相反的转换,它接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串。7setsockopt()函数 这个函数用来设置Socket的属性,若不能正确设置socket属性,则数据的发送和接收会失败。定义如下:intPASCALFARsetsockopt(SOCKETs,intlevel,intoptname,constcharFAR*optval,intoptlen);返回值:int类型,0代表成功,SOCKET_ERROR代表有错误发生。SOCKETs: 代表要设置的套接字;intlevel: 代表要设置的属性所处的层次,层次包含以下取值:SOL_SOCKET代表套接字层次;IPPROTO_TCP代表TCP协议层次,IPPROTO_IP代表IP协议层次(后面两个我都没有用过);intoptname: 代表设置参数的名称,SO_BROADCAST代表允许发送广播数据的属性,其它属性可参考MSDN;constcharFAR*optval: 代表指向存储参数数值的指针,注意这里可能要使用reinterpret_cast类型转换;intoptlen: 代表存储参数数值变量的长度。8sockaddr_in、in_addr类型,inet_addr、inet_ntoa函数sockaddr_in定义了socket发送和接收数据包的地址,定义如下:structsockaddr_in{shortsin_family;u_shortsin_port;

structin_addr

sin_addr;charsin_zero[8];};其中in_addr的定义如下:structin_addr{union{struct{u_chars_b1,s_b2,s_b3,s_b4;}S_un_b;struct{u_shorts_w1,s_w2;}S_un_w;u_longS_addr;}S_un;structin_addr{union{struct{u_chars_b1,s_b2,s_b3,s_b4;}S_un_b;struct{u_shorts_w1,s_w2;}S_un_w;

u_long

S_addr;}S_un; 首先阐述in_addr的含义,很显然它是一个存储ip地址的联合体,有三种表达方式:第一种:用四个字节来表示IP地址的四个数字;第二种:用两个双字节来表示IP地址;第三种:用一个长整型来表示IP地址。 给in_addr赋值的一种最简单方法是使用inet_addr函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型,如addrto.sin_addr.s_addr=inet_addr("192.168.0.2");其反函数是inet_ntoa():networktoascii

将网络地址转换成“.”点隔的字符串格式,即把一个in_addr类型转换为一个字符串。structsockaddr_in{shortsin_family;u_shortsin_port;structin_addrsin_addr;charsin_zero[8];};sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下:shortsin_family: 代表网络地址族,如前所述,只能取值AF_INET;u_shortsin_port: 代表IP地址端口,由程序员指定;structin_addrsin_addr: 代表IP地址;charsin_zero[8]: 是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。程序示例:以下代表指明了广播地址,端口号为7861的一个地址:sockaddr_inaddrto; //发往的地址memset(&addrto,0,sizeof(addrto));addrto.sin_family=AF_INET; //地址类型为internetworkaddrto.sin_addr.s_addr=INADDR_BROADCAST; //设置ip为广播地址addrto.sin_port=htons(7861); //端口号为78619sockaddr()类型 sockaddr类型是用来表示Socket地址的类型,同上面的sockaddr_in类型相比,sockaddr的适用范围更广,因为sockaddr_in只适用于TCP/IP地址。Sockaddr的定义如下: structsockaddr{ u_shortsa_family; charsa_data[14]; }; 可知sockaddr有16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockaddr的。事实上也往往使用这种方法。10Sleep()函数 线程挂起函数,表示线程挂起一段时间。Sleep(1000)表示挂起一秒。定义于WINBASE.H头文件中。WINBASE.H又被包含于WINDOWS.H中,然后WINDOWS.H被WINSOCK2.H包含。 所以使用Sleep函数不需要包含其它头文件。11sendto()函数在Socket中有两套发送和接收函数,一是sendto和recvfrom;二是send和recv。前一套在函数参数中要指明地址;而后一套需要先将套接字和一个地址绑定,然后直接发送和接收,不需绑定地址。sendto的定义如下:intPASCALFARsendto(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,inttolen);第一个参数就是套接字;第二个参数是要传送的数据指针;第三个参数是要传送的数据长度(字节数);第四个参数是传送方式的标识,如果不需要特殊要求则可以设置为0,其它值请参考MSDN;第五个参数是目标地址,注意这里使用的是sockaddr的指针;第六个参数是地址的长度;返回值为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。12WSAGetLastError()函数 该函数用来在Socket相关API失败后读取错误码,根据这些错误码可以对照查出错误原因。13closesocket()函数 关闭套接字,其参数为SOCKET类型。成功返回0,失败返回SOCKET_ERROR。

14WSACleanup()函数 卸载Socket库,释放系统资源。返回值为0表示成功,SOCKET_ERROR表示失败。下面是一个用UDP发送广播报文的程序示例:#include<winsock2.h>#include<iostream.h>voidmain(){SOCKETsock;//socket套接字charszMsg[]="thisisaUDPtestpackage";//被发送的字段

//1.启动SOCKET库,版本为2.0WORDwVersionRequested;WSADATAwsaData;interr;wVersionRequested=MAKEWORD(2,0);err=WSAStartup(wVersionRequested,&wsaData);if(0!=err)//检查Socket初始化是否成功{cout<<"Socket2.0初始化失败,Exit!";return;}//检查Socket库的版本是否为2.0if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=0){WSACleanup();return;}

//2.创建socket,sock=socket(AF_INET,//internetwork:UDP,TCP,etcSOCK_DGRAM,//SOCK_DGRAM说明是UDP类型0//protocol);if(INVALID_SOCKET==sock){cout<<"Socket创建失败,Exit!";return;}//3.设置该套接字为广播类型,boolopt=true;setsockopt(sock,SOL_SOCKET,SO_BROADCAST,reinterpret_cast<charFAR*>(&opt),sizeof(opt));//4.设置发往的地址sockaddr_inaddrto;//发往的地址memset(&addrto,0,sizeof(addrto));addrto.sin_family=AF_INET;//地址类型为internetworkaddrto.sin_addr.s_addr=INADDR_BROADCAST;//设置ip为广播地址addrto.sin_port=htons(7861);//端口号为7861intnlen=sizeof(addrto);unsignedintuIndex=1;

while(true){Sleep(1000);//程序休眠一秒//向广播地址发送消息if(sendto(sock,szMsg,strlen(szMsg),0,(sockaddr*)&addrto,nlen)==SOCKET_ERROR)cout<<WSAGetLastError()<<endl;elsecout<<uIndex++<<":anUDPpackageissended."<<endl;}if(!closesocket(sock))//关闭套接字{WSAGetLastError();return;}if(!WSACleanup())//关闭Socket库{WSAGetLastError();return;}}一、基于TCP(面向连接)的socket编程服务器端程序:1、加载套接字库,创建套接字(WSAStartup()/socket()); 2、将套接字绑定到一个本地地址和端口上(bind())。3、将套接字设置为监听模式等待连接请求(listen());4、当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept())。5、用返回的套接字和客户端进行通信(send()/recv());6、返回,等待另一客户请求。7、关闭套接字,卸载的套接字库(closesocket()/WSACleanup())。客户端程序:1、加载套接字库,创建套接字(WSAStartup()/socket()); 2、向服务器发出连接请求(connect());3、和服务器端进行通信(send()/recv());4、关闭套接字,卸载的套接字库(closesocket()/WSACleanup())。 在VC中进行WINSOCK的API编程开发的时候,需要在项目中使用下面的三个文件,否则会出现编译错误:1.WINSOCK2.H: WINSOCKAPI的头文件,需要包含在项目中。2.ws2_32.lib:(WSOCK32.LIB)

WINSOCKAPI链接库文件。在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。

设置方法:工程->设置->链接->对象/库模块3.WINSOCK.DLL: WINSOCK的动态连接库,位于WINDOWS的安装目录下。服务器端操作socket(套接字)

1)在初始化阶段调用WSAStartup()

此函数在应用程序中初始化WindowsSocketsDLL,只有此函数调用成功后,应用程序才可以再调用其他WindowsSocketsDLL中的API函数。 在程式中调用该函数的形式如下:intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);wVersionRequested: 用于指定准备加载的Winsock库的版本。 高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。 可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值。lpWSAData: 是指向WSADATA结构的指针,用来存储系统传回的关于加载的库版本的资料,是一个结构体。2)建立Socket

初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,为此可以调用Socket()函数用来建立这个监听的Socket,并定义此Socket所使用的通信协议。定义方式:SOCKETPASCALFARsocket(intaf,inttype,intprotocol)参数:

af: addressfamily,目前只提供AF_INET(PF_INET);

type: Socket的类型(SOCK_STREAM(流格式)、SOCK_DGRAM(数据报格式));

protocol: 特殊的通讯协定(不指定则设为0); 此函数调用成功返回Socket对象,失败则返回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket的函数都可以使用这个函数来获取失败的原因)。3)绑定端口

为监听的Socket指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此需调用bind()函数:intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数: s:Socket对象名; name:Socket的地址值,这个地址必须是执行这个程式所在机器的IP地址; namelen:name的长度; 该函数调用成功返回0,否则返回SOCKET_ERROR。 如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY及Port为0,WindowsSockets会自动将其设定适当之地址及Port(1024到5000之间的值)。此后可以调用getsockname()函数来获知其被设定的值。4)监听

当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket进入监听状态,并设定可以建立的最大连接数(目前最大值限制为5,最小值为1)。该函数调用成功返回0,否则返回SOCKET_ERROR。

intPASCALFARlisten(SOCKETs,intbacklog);参数:s:

需要建立监听的Socket;backlog:

最大连接个数;服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,Server端必须再调用accept()函数,这样服务器端和客户端才算正式完成通信程序的连接动作。4)监听 为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了。该函数调用成功返回0,否则返回SOCKET_ERROR。intPASCALFARWSAAsyncSelect(SOCKETs,HWNDhWnd,unsignedintwMsg,longlEvent);参数:s:

Socket对象;hWnd:

接收消息的窗口句柄;wMsg:

传给窗口的消息;lEvent: 被注册的网络事件,也即是应用程序向窗口发送消息的网路事件,该值为下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组;FD_READ: 希望在套接字S收到数据时收到消息;FD_WRITE: 希望在套接字S上可以发送数据时收到消息;FD_ACCEPT: 希望在套接字S上收到连接请求时收到消息;FD_CONNECT: 希望在套接字S上连接成功时收到消息;FD_CLOSE: 希望在套接字S上连接关闭时收到消息;FD_OOB: 希望在套接字S上收到带外数据时收到消息。 具体应用时,wMsg应是在应用程序中定义的消息名称,而消息结构中的lParam则为以上各种网络事件名称。 即:

可以在窗口处理自定义消息函数中使用以下结构来响应,Socket的不同事件:switch(lParam){caseFD_READ:… break; caseFD_WRITE、… break;… }

5)服务器端接受客户端的连接请求

当Client提出连接请求时,Server端hwnd视窗会收到WinsockStack送来我们自定义的一个消息,这时,我们可以分析lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一Socket与客户端的Socket相通,原先监听之Socket继续进入监听状态,等待他人的连接要求。该函数调用成功返回一个新产生的Socket对象,否则返回INVALID_SOCKET。SOCKETPASCALFARaccept(SCOKETs,structsockaddrFAR*addr,intFAR*addrlen);s:

Socket的识别码;addr: 存放来连接的客户端的地址;addrlen:

addr的长度6)结束socket连接

结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭Server端监听状态的socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()憨数相对应,程式结束前,需要调用WSACleanup()来通知WinsockStack释放Socket所占用的资源。这两个函数都是调用成功返回0,否则返回SOCKET_ERROR。intPASCALFARclosesocket(SOCKETs); s:Socket的识别码;intPASCALFARWSACleanup(void); 参数:无二、客户端Socket的操作

1)建立客户端的Socket

客户端应用程序首先也是调用WSAStartup()函数来与Winsock的动态连接库建立关系,然后同样调用socket()来建立一个TCP或UDPsocket(相同协定的sockets才能相通,TCP对TCP,UDP对UDP)。 与服务器端的socket同的是:客户端的socket可以调用bind()函数,由自己来指定IP地址及port号码;但是也可以不调用bind(),而由Winsock来自动设定IP地址及port号码。

2)提出连接申请

客户端的Socket使用connect()函数来提出与服务器端的Socket建立连接的申请,函数调用成功返回0,否则返回SOCKET_ERROR。intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);s: Socket的识别码;name: Socket想要连接的对方地址;namelen: name的长度三、数据的传送

一般情况下TCPSocket的数据发送和接收是调用send()及recv()这两个函数来达成,而UDPSocket则是用sendto()及recvfrom()这两个函数,这两个函数调用成功发挥发送或接收的资料的长度,否则返回SOCKET_ERROR。数据的发送:intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);s: Socket的识别码buf: 存放要传送的资料的暂存区lenbuf: 长度flags: 此函数被调用的方式

数据的接收:intPASCALFARrecv(SOCKETs,charFAR*buf,intlen,intflags);参数:s: Socket的识别码buf: 存放接收到的资料的暂存区lenbuf: 长度flags: 此函数被调用的方式对StreamSocket言,我们可以接收到目前inputbuffer内有效的资料,但其数量不超过len的大小。服务器端编程的步骤:1、加载套接字库,创建套接字(WSAStartup()/socket())

2、将套接字绑定到一个本地地址和端口上(bind())。3、将套接字设置为监听模式等待连接请求(listen());4、当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept())。5、用返回的套接字和客户端进行通信(send()/recv());6、返回,等待另一客户请求。7、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。一、基于TCP(面向连接)的socket编程服务器端编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}1.加载套接字库SOCKET

sockSrv=socket(AF_INET,SOCK_STREAM,0);SOCKADDR_IN

addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));listen(sockSrv,5);SOCKADDR_IN

addrClient;intlen=sizeof(SOCKADDR);while(1){ SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); charsendBuf[50]; sprintf(sendBuf,"欢迎%s来到美丽的闽南科技学院!!!",inet_ntoa(addrClient.sin_addr));

send(sockConn,sendBuf,strlen(sendBuf)+1,0); charrecvBuf[50];

recv(sockConn,recvBuf,50,0); printf("%s\n",recvBuf);

closesocket(sockConn);}}htonl():

将32位的主机字节顺序转化为32位的网络字节顺序

htons():

将16位的主机字节顺序转化为16位的网络字节顺序(ip地址是32位的端口号是16位的)2.创建套接字htons()--"HosttoNetworkShort"htonl()--"HosttoNetworkLong"ntohs()--"NetworktoHostShort"ntohl()--"NetworktoHostLong“3.绑定套接字4.监听等待请求5.数据通信(发送)6.数据通信(接收)7.关闭套接字客户端编程的步骤:1、加载套接字库,创建套接字(WSAStartup()/socket());2、向服务器发出连接请求(connect());3、和服务器端进行通信(send()/recv());4、关闭套接字,卸载加载的套接字库(closesocket()/WSACleanup()),释放系统资源。客户端编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));charrecvBuf[50];recv(sockClient,recvBuf,50,0);printf("%s\n",recvBuf);send(sockClient,"嗨,你好,我是某某某",strlen("嗨,你好,我是某某某")+1,0);closesocket(sockClient);WSACleanup();}6.数据通信(接收)3.转换为u_long操作步骤:1.新建工程TCP;2.新建C++源程序:TCPserv,加载ws2_32.lib;3.切换到FileView,新建工程TCPclient,新建C++源程序:TCPclient,加载ws2_32.lib;4.分别运行Serv和client;可能用到的方式或快捷键:1.组建,编译微型条2.Ctrl+Tab(切换程序)思考: 编写基于UDP(面向无连接)的socket程序二、基于UDP(面向无连接)的socket编程服务器端(接收端)程序:1、创建套接字(socket)。 2、将套接字绑定到一个本地地址和端口上(bind)。3、等待接收数据(recvfrom)4、关闭套接字,释放系统资源。客户端(发送端)程序:1、创建套接字(socket)。

2、向服务器发送数据(sendto)。3、关闭套接字,释放系统资源。服务器端编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}1.加载套接字库SOCKET

sockSrv=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_IN

addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//基于UDP面向无连接,因此无需listen()和accept()SOCKADDR_IN

addrClient;intlen=sizeof(SOCKADDR); charrecvBuf[50];

recvfrom(sockSrv,recvBuf,50,(SOCKADDR*)&addrSrv,&len); printf("%s\n",recvBuf);

closesocket(sockConn);}}客户端的编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");sendto(sockClient,“嗨,你好,我是某某某”,strlen(“嗨,你好,我是某某某”)+1,0,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));closesocket(sockClient);WSACleanup();}三、基于UDP的聊天程序优点: 速度快,占用资源少,丢失可根据上下文理解或选择重发消息。服务器端编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}SOCKET

sockSrv=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_IN

addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//基于UDP面向无连接,因此无需listen()和accept()charrecvBuf[100];charsendBuf[100];chartempBuf[100];SOCKADDR_IN

addrClient;intlen=sizeof(SOCKADDR);while(1){ recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);If(recvBuf[0]==‘q’){sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len); printf("Chatend!!!\n"); break;} sprintf(tempBuf,"%s说:%s",inet_ntoa(addrClient.sin_addr),recvBuf); printf("%s\n",tempBuf); printf("Pleaseinputdata:\n");

gets(sendBuf);

sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);}

closesocket(sockSrv);WSACleanup();}客户端编程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");charrecvBuf[300];charsendBuf[300];chartempBuf[300];intlen=sizeof(SOCKADDR);while(1){ printf("请输入数据:\n");

gets(sendBuf);

sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,len);

recvfrom(sockClient,rec

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论