TCP(面向连接)的socket编程

服务器端程序:
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、将套接字设为监听模式,准备接收客户请求(listen)。
4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
5、用返回的套接字和客户端进行通信(send/recv)。
6、返回,等待另一客户请求。
7、关闭套接字。

客户端程序:
1、创建套接字(socket)。
2、向服务器发出连接请求(connect)。
3、和服务器端进行通信(send/recv)。
4、关闭套接字。

1)WSAStartup
The WSAStartup function initiates use of Ws2_32.dll by a process.

int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);

wVersionRequested参数用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值。
lpWSAData参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。
Requirements
Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winsock2.h.
Library: Use Ws2_32.lib.//需加载Ws2_32.lib

2)WSADATA
The members of the WSADATA structure are:

typedef struct WSAData {
WORD                  wVersion;
WORD                  wHighVersion;
char                  szDescription[WSADESCRIPTION_LEN+1];
char                  szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short        iMaxSockets;
unsigned short        iMaxUdpDg;
char FAR *            lpVendorInfo;
} WSADATA, *LPWSADATA; 
     WSAStartup把第一个字段wVersion设成打算使用的Winsock版本。wHighVersion 参数容纳的是现有的Winsock库的最高版本。记住,这两个字段中,高位字节代表的是Winsock副版本,而低位字节代表的则是Winsock主版本。szDescription和szSystemStatus这两个字段由特定的Winsock实施方案设定,事实上没有用。不要使用下面这两个字段:iMaxSockets和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过WSAEnumProtocols来查询协议信息。同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。最后,lpVendorInfo字段是为Winsock实施方案有关的指定厂商信息预留的。任何一个Win32平台上都没有使用这个字段。
       如果WinSock.dll或底层网络子系统没有被正确初始化或没有被找到,WSAStartup将返回WSASYSNOTREADY。此外这个函数允许你的应用程序协商使用某种版本的WinSock规范,如果请求的版本等于或高于DLL所支持的最低版本,WSAData的wVersion成员中将包含你的应用程序应该使用的版本,它是DLL所支持的最高版本与请求版本中较小的那个。反之,如果请求的版本低于DLL所支持的最低版本,WSAStartup将返回WSAVERNOTSUPPORTED。
      对于每一个WSAStartup的成功调用(成功加载WinSock DLL后),在最后都对应一个WSACleanUp调用,以便释放为该应用程序分配的资源。 

3)socket
The socket function creates a socket that is bound to a specific service provider.

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

该函数接收三个参数。第一个参数af指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可写成PF_INET)。第二个参数指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字。第三个参数是与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动为你选择一个合适的协议。这是推荐使用的一种选择协议的方法。
如果这个函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

4)bind
The bind function associates a local address with a socket.

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

Parameters
s
[in] Descriptor identifying an unbound socket. 第一个参数s指定要绑定的套接字
name
[in] Address to assign to the socket from the SOCKADDR structure. 指定了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同,所以,要用第三个参数指定该地址结构的长度。
namelen
[in] Length of the value in the name parameter.

5)sockaddr
The sockaddr structure varies depending on the protocol selected. Except for the sa_family parameter, sockaddr contents are expressed in network byte order.

struct sockaddr {
u_short    sa_family;
char       sa_data[14];
};

sockaddr的第一个字段sa_family指定该地址家族,在这里必须设为AF_INET。sa_data仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。除了sa_family外,sockaddr是按网络字节顺序表示的。在TCP/IP中,我们可以用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。

6)SOCKADDR_IN
In the Internet address family, this structure is used by Windows Sockets to specify a local or remote endpoint address to which to connect a socket. This is the form of the SOCKADDR structure specific to the Internet address family and can be cast to SOCKADDR.

struct sockaddr_in{
short sin_family;
unsigned short sin_port;
IN_ADDR sin_addr;
char sin_zero[8];};

Members
sin_family
Address family; must be AF_INET. 表示地址族,对于IP地址,sin_family成员将一直是AF_INET。
sin_port
Internet Protocol (IP) port. 指定的是将要分配给套接字的端口.
sin_addr
IP address in network byte order. 给出的是套接字的主机IP地址.
sin_zero
Padding to make structure the same size as SOCKADDR. 只是一个填充数,以使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地址字符串。

7)accept
The accept function permits an incoming connection attempt on a socket.

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

Parameters
s
[in] Descriptor identifying a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.
addr
[out] Optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket was created.
addrlen
[out] Optional pointer to an integer that contains the length of addr.

8)send
The send function sends data on a connected socket.

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

Parameters
s
[in] Descriptor identifying a connected socket.
buf
[in] Buffer containing the data to be transmitted.
len
[in] Length of the data in buf.
flags
[in] Indicator specifying the way in which the call is made.

9)recv
The recv function receives data from a connected or bound socket.

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

Parameters
s
[in] Descriptor identifying a connected socket.
buf
[out] Buffer for the incoming data.
len
[in] Length of buf.
flags
[in] Flag specifying the way in which the call is made.

eg:
头文件:Winsock2.h、stdio.h
服务器:
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );//ver1.1

err = WSAStartup( wVersionRequested, &wsaData );//加载winsocket库
if ( err != 0 ) {                               //版本协商
    return;
}


if ( LOBYTE( wsaData.wVersion ) != 1 ||       //low ver
    HIBYTE( wsaData.wVersion ) != 1 )         //high ver
{       
    WSACleanup( );//释放winsocket库
    return; 
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建流式监听socket

SOCKADDR_IN addrSrv;//定义地址结构体变量
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//给出的是套接字的主机IP地址
addrSrv.sin_family=AF_INET;//地址族
addrSrv.sin_port=htons(10002);//端口

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//绑定

listen(sockSrv,10);//监听

SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);

while(1)
{
    SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//创建连接套接字
    char sendBuf[100];
    sprintf(sendBuf,"Welcome %s to C++",
        inet_ntoa(addrClient.sin_addr));
    send(sockConn,sendBuf,strlen(sendBuf)+1,0);
    char recvBuf[100];
    recv(sockConn,recvBuf,100,0);
    printf("%s\n",recvBuf);
    closesocket(sockConn);//关闭连接套接字
}

}

客户机:
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );//ver1.1

err = WSAStartup( wVersionRequested, &wsaData );//加载winsocket库
if ( err != 0 ) {                               //版本协商
    return;
}


if ( LOBYTE( wsaData.wVersion ) != 1 ||       //low ver
    HIBYTE( wsaData.wVersion ) != 1 )         //high ver
{       
    WSACleanup( );//释放winsocket库
    return; 
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);//创建流式socket

SOCKADDR_IN addrSrv;//定义地址结构体变量
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//
//addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.0.3");//给出的是套接字的主机IP地址
addrSrv.sin_family=AF_INET;//地址族
addrSrv.sin_port=htons(10002);//端口
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

char recvBuf[100];
recv(sockClient,recvBuf,100,0);
printf("%s\n",recvBuf);
send(sockClient,"Hello",strlen("Hello")+1,0);

closesocket(sockClient);//关闭连接套接字
WSACleanup();//释放winsocket库

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值