您的位置:首页 > 理论基础 > 计算机网络

windows sock 网络编程基础知识--基本TCP套接字编程

2012-10-16 00:21 567 查看
#include "winsock2.h"//头文件

#pragma comment(lib, "wsock32.lib")//导入wsock32.lib库文件

TCP和UDP的区别

简单的说,就是有建立连接(TCP)和无建立连接(UDP)。

typedef unsigned int u_int; //u_int 为无符号整形

typedef u_int SOCKET; //SOCKET为无符号整形,称为套接字

在windows Sockets中除了INVALID_SOCKET不是一个有效的套接字外,在0-INVALID_SOCKET-1之间的数值

都是有效的套接字。

判断是否成功的创建了套接字的方法是将socket()函数的返回值与INVALID_SOCKET进行比较

eg:

 SOCKET s = socket(...);

 if (INVALID_SOCKET == s)

 {

  //失败

 }

INVALID_SOCKET 的声明如下:

#define INVALID_SOCKET (SOCKET)(~0)   //~为按位取反运算符,在32位中INVALID_SOCKET为-1

在32位系统中对于0全部取反就是 1111 1111 1111 1111  等于-1。 简单记忆:0+1取反等于-1

常量SOCKET_ERROR是被用来检查Windows API调用失败的。

SOCKET_ERROR的声明如下:

#define SOCKET_ERROR            (-1)

Windows Sockets API 提供了shutdown()和WSASendDisconnect()函数实现关闭连接的功能。

closesocket()函数实现关闭套接字的功能,但同时也隐含执行shutdown()函数的功能。

Windows Sockets 通过AF_INET地址家族为IP通信定址。其中“A”代表address,“F”代表family。

AF_INET的声明如下:

#define AF_INET    2;

Windows Sockets API提供了SOCKADDR_IN结构来指定IP地址和端口号

该结构声明如下:

struct sockaddr_in{

 short    sin_family;

 u_short  sin_port;

 struct   in_addr sin_addr;

 char     sin_zero[8];

};

sin_famliy: 地址家族

sin_port:   服务端口号

sin_addr:   in_add类型的IP地址

sin_zero:   填充该结构的大小,使之与SOCKADD结构大小相同

sin_family字段必须为AF_INET,以告知Windows Sockets应用程序使用IP地址家族。

sin_port字段指定了服务的端口号。1024-49151

sin_addr字段用于把一个IP地址保存为一个4字节的数值

sin_zero字段充当填充项的职责,以便使得该结构与SOCKADDR结构长度相同

字节顺序问题

在计算机中把IP地址和端口号指定为多字节数字时,这个数就按照“主机字节”(hose-byte)顺序表示

。但是在网络上指定IP地址和端口号,这个数必须按照“大头”形式来表示,也就是按照从最有意义的字

节到最无意义的字节来表示数据,这里称为“网络字节”顺序(network-byte)

htonl()函数和htons()函数实现从主机字节顺序转换为网络字节顺序的功能。其中“h”代表机“host”

;“n”代表“net”;“l”代表“long”;“s”代表“short”;“to”表示转换的含义。

htonl()函数和htons()函数声明如下。

u_long  htohl(u_long  hostlong);

u_short htohs(u_short hostshort);

ntohl()函数和ntohs()函数实现从网络字节顺序转换为主机字节顺序的功能。

ntohl()函数和ntohs()函数声明如下。

u_long ntohl(u_long  netlong);

ushort ntohs(u_short netshort);

客户端连接服务器时,必须先知道服务器的名称。

在TCP/IP中,服务器的名称 = 服务器的IP地址和端口号。

服务端--创建套接字--绑定--监听--接受连接--发送数据--关闭

bind()函数实现将服务器绑定到一个已知的名字上的功能。

接下来需要将服务器套接字设置为监听状态,以便监听客户端的连接,这是通过调用listen()函数完成的

在监听状态下,如果客户端向服务器发起连接请求,服务器通过调用accetp()函数来接受该连接请求

服务端与客户端完成连接后,借可以进行数据通信了。

客户端--创建套接字--连接--发送数据--关闭

调用connect()函数向服务器发出连接请求。

WSAStartup()函数

开发Windows Sockets应用程序时,必须首先加载Windows Sockets动态库(DLL)。WSAStartup()函数实

现此项功能。该函数是套接字应用程序必须调用的第一个函数。

该函数的声明如下:

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

wVersionRequested:指定准备加载Windows Sockets动态库的版本。

lpWSAData:指向LPWSADATA结构的指针,该参数返回被加载动态库的有关信息。

WSADATA结构声明如下:

#define WSADESCRIPTION_LEN 256

#define WSASYS_STATUS_LEN 128

typedef struct WSAData

{

 WORD           wVersion;

 WORD           wHigtVersion;

 char           szDescription{WSADESCRIPTION_LEN+1};

 char           szSystemStatus{WSASYS_STATUS_LEN+1};

 unsigned short iMaxSockets;

 unsigned short iMaxUdpDg;

 char   FAR*    lpVendorInfo;

}WSADATA, FAR * LPWSADATA;

wVersion:期望调用者使用的Windows Sockets版本

wHighVersion:DLL支持的最高版本

szDescription:DLL的描述信息

szSystemStatus:DLL的状态信息

iMaxSockets:一个进程可以打开的套接字最多数量(Windows Sockets2.0及以后版本中忽略此值)

iMaxUdpDg:一个进程发送或接收的最大的数据包的长度(Windows Sockets2.0及以后版本中忽略此值)

lpVendorInfo:厂商专有信息(Windows Sockets2.0及以后版本中忽略此值)

socket()函数

初始化Windows Sockets DLL之后,创建套接字。socket()函数和WSASocket()函数将实现此功能。

socket()函数声明如下:

SOCKET socket(int af, int type, int protocol);

af:协议的地址家族。创建TCP或者UDP套接字时,该参数为AF_INET。

type:协议的套接字类型。有SOCK_STREAM,SOCK_DGRAM和SOCK_RAM 3种类型

protocol:协议。指定的地址家族和套接字类型有多个数目时,使用该字段来限定一个特殊的传输。对于

SOCK_STREAM套接字类型,该字段为IPPROTO_TCP或者为0;对于SOCK_DGRAM套接字类型,该字段为

IPPROTO_UDP或者为0.

当该函数调用成功后,返回一个新建的套接字句柄,调用失败则返回INVALID_SOCKET.

SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(INVALID_SOCKET == s)

{

//调用失败

}

bind()函数

bind()函数将套接字绑定到一个已知的地址。

该函数声明如下:

int bind(SOCKET s, const struct sockaddr FAR * name, int namelen);

s:套接字

name:地址

namelen:sockaddr结构长度

如果该函数调用成功,则返回值为0,失败则返回值为SOCKET_ERROR

如果地址字段为INADDR_ANY,则可使用任意网络接口。

如果端口号设置为0,则Windows Sockets将给应用程序分配一个值在1024-5000之间的唯一端口

eg:

SOCKET             s;//套接字

struct sockaddr_in servAddr;//服务器套接字地址

int                nServPort = 5500;//服务器端口

int                nErrCode;//错误代码

//定义服务器地址

servAddr.sin_family = AF_INET;

servAddr.sin_addr.S_addr = htonl(INADDR_ANY);

servAddr.sin_family = htons(nServPort);

//绑定套接字

nErrcode = bind(s, (SOC
4000
KADDR*) &servAddr, sizeof(servAddr));

if(SOCKET_ERROR == nErrCode)

{

//绑定套接字失败

}

listen()函数

listen()函数将套接字设置为监听模式。

listen()函数声明如下:

int listen(SOCKET s, int backlog);

s:套接字

backlog:指定等待连接的最大队列长度

该函数调用成功时,返回值为0,失败则返回SOCKET_ERROR.

accept()函数

accept()函数实现接受一个连接请求的功能

accept()声明如下:

SOCKET accept(SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen);

s:监听套接字。

addr:该参数返回请求连接主机的地址

addrlen:该参数返回SOCKADDR_IN结构的长度。

当该函数成功返回时:

主机接受了等待队列中的第一个请求

addr结构返回发起请求的客户端的地址,addrlen参数指出了地址结构的长度。

返回一个新的套接字句柄。服务器使用该套接字与客户端进行数据传输。而监听套接字仍然用于接受客户

端的连接

调用失败时返回INVALID_SOCKET错误

SOCKET sListen;//监听套接字

SOCKET sAccept;//接受套接字

sockaddr_in addrClient;//客户端地址

int addrClientlen = sizeof(addrClient);//长度

//接受客户请求

sAccept = accept(sListen, (SOCKADDR*) &addrClient, &addrClientlen);

if(INVALID_SOCKET == sAccept)

{

//失败处理

}

recv()函数

recv()和WSARecv()函数用于接受数据

recv()函数声明如下:

int recv(SOCKET s, char FAR * buf, int len, int flags);

s:套接字

buf:接受数据缓冲区

len:缓冲区长度

flags:该参数影响该函数的行为。该参数可以是0,MSG_PEEK和MSG_OOB.0表示无特殊行为;MSG_PEEK会

使有用的数据被复制到接收缓冲区内,但没有从系统缓冲区中将其删除,MSG——OOB表示处理带外数据。

该函数成功返回时返回值为接收的字节数。失败时则返回SOCKET_ERROR.

SOCKET s;//套接字

char   buf[128];//接收数据缓冲区

int    nReadlen;//接收字节数

//接收数据

nReadlen = recv(s, buf, 128, 0);

if(SOCKET_ERROR == nReadlen)

{

//失败处理

}

send()函数

send()和WSASend()函数用于发送数据

send()函数声明如下:

int send(SOCKET s, const char FAR * buf, int len, int flags);

s:套接字

buf:发送数据缓冲区

len:发送数据长度

flags:该参数影响该函数的行为,该参数可以为0,MSG_DONTROUTE或者MSG_OOB。0表示无函数无特殊行

为,MSG_DONTROUTE标志要求传输层不要将数据路由出去,MSG_OOB标志表示该数据应该被带外发送。

该函数成功返回时,返回值为实际发送的字节数。失败时返回SOCKET_ERROR。

SOCKET   s;//套接字

char     buf[128];//接受数据缓冲区

int      retVal;//返回值

strcpy(buf, "send data");

//发送数据

retVal = send(s, buf, strlen(buf),0);

if(SOCKET_ERROR == retVal)

{

//失败处理

}

closesocket()函数

closesocket()函数关闭套接字,释放所占资源。

该函数输声明如下:

int closesocket(SOCKET s);

当调用该函数释放套接字后,如果在使用该套接字执行函数调用,则会失败并返回WSAENOTSOCK错误。

shutdown()函数

shutdown()函数用于通知对方不再发送数据,或者不再接收数据,或者既不发送也不接收数据

该函数声明如下:

int shutdown(SOCKET s, int how);

s:套接字

how:如果该参数为SD_RECEIVE,则表示不允许再调用接收数据函数;如果该参数为SE_SEND,则表示不允

许再调用发送数据函数;如果该参数为SD_BOTH,则表示既不允许调用发送数据函数也不允许调用接收数

据函数

connect()函数

connect()函数实现连接服务器功能。

该函数声明如下:

int connect(SOCKET s, const struct cockaddr FAR * name, int namelen);

s:套接字

name:服务器地址

namelen:sockaddr结构的长度

当该函数调用成功时,函数返回0,调用失败时返回SOCKET_ERROR.

SOCKET  s;//套接字

u_long  ulServIP;//服务器IP

u_short usServPort;//服务器端口

int     setVal;//返回值

//服务器地址

SOCKADDR_IN servAddr;

servAddr.sin_family = AF_INET;

servAddr.sin_addr.S_un.S_addr = htonl(ulServIP);

servAddr.sin_Port = htos(usServPort);

int nServLen = sizeof(servAddr);

//连接服务器

retVal = connect(s, (LPSOCKADDR)&servAddr, sizeof(servAddr));

if(SOCKET_ERROR == retVal)

{

//失败处理

}

开发过程:

Server:

WSAStartup()--socket()--bind()--listen()--accept()--recv()--closesocket--WSACleanup()

Client:

WSAStartup()--socket()--connect()--send()--closesocket()--WSACleanup()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐