现在的位置: 首页 > 编程语言 > 正文

socket 的一些函数

2019年08月08日 编程语言 ⁄ 共 10935字 ⁄ 字号 评论关闭
关于winsock服务器和客户端编程
在网络编程中,最常用和最基础的就是WINSOCK. 现在我们讨论WINDOWS下的SOCKET编程. 
     
        大凡在WIN32平台上的WINSOCK编程都要经过下列步骤: 
      定义变量->获得WINDOCK版本->加载WINSOCK库->初始化->创建套接字->设置套接字选项->关闭套接字->卸载WINSOCK库->释放资源 

     下面介绍WINSOCK C/S的建立过程: 

     服务器                         客户端 
________________________________________________ 
1   初始化WSA                      1   初始化WSA 
____________________________________________________ 
2   建立一个SOCKET                 2   建立一个SOCKET 
_____________________________________________________ 
3   绑定SOCKET                     3   连接到服务器 
_____________________________________________________ 
4   在指定的端口监听               4   发送和接受数据 
_____________________________________________________ 
5   接受一个连接                   5    断开连接 
______________________________________________________- 
6   发送和接受数据 
___________________________________________________ 
7   断开连接 
__________________________________________________ 
   
    大家注意,在VC中进行WINSOCK编程时,需要引入如下两个库文件:WINSOCK.H(这个是WINSOCK API的头文件,WIN2K以上支持WINSOCK2,所以 
可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API连接库文件). 
使用方式如下: 
               #include <winsock.h> 
       #pragma comment(lib,"ws2_32.lib") 
   
   下面我们通过具体的代码演示服务器和客户端的工作流程: 

首先,建立一个WSADATA结构,通常用wsaData 
WSADATA wsaData; 

然后,调用WSAStartup函数,这个函数是连接应用程序与winsock.dll的第一个调用.其中,第一个参数是WINSOCK 版本号,第二个参数是指向 
WSADATA的指针.该函数返回一个INT型值,通过检查这个值来确定初始化是否成功.调用格式如下:WSAStartup(MAKEWORD(2,2),&wsaData),其中 
MAKEWORD(2,2)表示使用WINSOCK2版本.wsaData用来存储系统传回的关于WINSOCK的资料. 

if(iResuit=WSAStartup(MAKEWORD(2,2),&wsaData)!=0) 

printf("WSAStartup failed:%d",GetLastError());   //返回值不等与0,说明初始化失败 
         ExitProcess();                                   //退出程序 

应用程序在完成对请求的SOCKET库使用后,要调用WSACleanup函数来接触SOCKET库的绑定,并且释放资源. 

注意WSAStartup初始化后,必须建立一个SOCKET结构来保存SOCKET句柄. 

下面我们建立一个SOCKET. 

首先我们建立一个m_socket的SOCKET句柄,接着调用socket()函数,函数返回值保存在m_socket中.我们使用AF_INFE,SOCK_STREAM,IPPROTO_TCP 
三个参数.第一个表示地址族,AF_INFE表示TCP/IP族,第二个表示服务类型,在WINSOCK2中,SOCKET支持以下三种类型; 

SOCK_STREAM 流式套接字 
SOCK_DGRAM   数据报套接字 
SOCK_RAW     原始套接字 

第三个参数表示协议: 

IPPROTO_UDP   UDP协议 用于无连接数据报套接字 
IPPROTO_TCP   TCP协议 用于流式套接字 
IPPROTO_ICMP ICMP协议用于原始套接字 

m_socket=socket(AF_INFE,SOCK_STREAM,IPPROTO_TCP);     //创建TCP协议 

以下代码用于检查返回值是否有错误: 

if(m_scoket==INVALID_SOCKET) 

prinrf("Error at socket():%d/n",GetLastError()); 
WSACleanup();                                    //释放资源 
return;                                    

说明,如果socket()调用失败,他将返回INVALID_SOCKET. 

     为了服务器能接受一个连接,他必须绑定一个网络地址,下面的代码展示如何绑定一个已经初始化的IP和端口的Socket.客户端程序用这个 
IP地址和端口来连接服务器. 

sockaddr_in service; 
service.sin_family=AF_INET;                        //INTERNET地址族 
service.sin_addr.s_addr=inet_addr("127.0.0.1");    //将要绑定的本地IP地址 
service.sin_port=htons(27015);                      //27015将要绑定的端口 

下面我们调用BIND函数,把SOCKET和SOCKADDR以参数的形式传入,并检查错误. 

if(bind(m_socket,(SOCKADDR*)&SERVICE,sizeof(service))==SOCKET_ERROR) 

printf("bind() failed./n"); 
closesocket(m_socket); 
return; 

    当绑定完成后,服务器必须建立一个监听队列,以接受客户端的请求.listen()使服务器进入监听状态,该函数调用成功返回0,否则返回 
SOCKET_ERROR.代码如下: 

if(listen(m_socket,1)==SOCKET-ERROR) 

printf("error listening on socket./n"); 

服务器端调用完LISTEN()后,如果此时客户端调用CONNECT()函数,服务器端必须在调用ACCEPT().这样服务器和客户端才算正式完成通信程序的 
连接动作. 

一旦服务器开始监听,我们就要指定一个句柄来表示利用ACCEPT()函数接受的连接,这个句柄是用来发送和接受数据的表示.建立一个SOCKET句柄 
Socket AcceptSocket 然后利用无限循环来检测是否有连接传入.一但有连接请求,ACCEPT()函数就会被调用,并且返回这次连接的句柄. 

printf("waitong for a client to connect.../n"); 
while(1) 

AcceptSocket=SOCKET_ERROR; 
while(AcceptSocket==SOCKET_ERROR) 

AcceptSocket=accept(m_socket,NULL,NULL); 

下面看客户端端代码: 

sockaddr_in clientService; 
clientService.sin_family=AF_INET;                        //INTERNET地址族 
clientService.sin_addr.s_addr=inet_addr("127.0.0.1");    //将要绑定的本地IP地址 
clientService.sin_port=htons(27015);                      //27015将要绑定的端口 

下面调用CONNECT()函数: 

if ( connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) 

         printf( "Failed to connect./n" ); 
         WSACleanup(); 
         return; 
}                                                                      //如果调用失败清理退出 
                                                                       //调用成功继续读写数据 

_________________________________________________________________________________________________ 
到这里,服务器和客户端的基本流程介绍完毕,下面我们介绍数据交换. 

send(): 
int send 

SOCKET s,                     //指定发送端套接字 
const char FAR?*buf,          //指明一个存放应用程序要发送的数据的缓冲区 
int len,                      //实际要发送的数据字节数 
int flags                     //一般设置为0 
}; 
C/S都用SEND函数向TCP连接的另一端发送数据. 

recv(): 
int recv 

SOCKET s,                     //指定发送端套接字 
char FAR?*buf,               //指明一个缓冲区 存放RECC受到的数据 
int len,                      //指明BUF的长度 
int flags                     //一般设置为0 

}; 
C/S都使用RECV函数从TCP连接的另一端接受数据 

下面将完整的程序代码提供如下,大家可直接编译运行 

首先看客户端的代码: 

#include <stdio.h> 
#include <winsock2.h> 
#pragma comment(lib, "ws2_32.lib") 
void main() { 

// 初始化 Winsock. 
WSADATA wsaData; 
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); 
if ( iResult != NO_ERROR ) 
printf("Error at WSAStartup()/n"); 

// 建立socket socket. 
SOCKET client; 
client = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); 

if ( client == INVALID_SOCKET ) { 
printf( "Error at socket(): %ld/n", WSAGetLastError() ); 
WSACleanup(); 
return; 

// 连接到服务器. 
sockaddr_in clientService; 

clientService.sin_family = AF_INET; 
clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); 
clientService.sin_port = htons( 27015 ); 

if ( connect( client, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) { 
printf( "Failed to connect./n" ); 
WSACleanup(); 
return; 

// 发送并接收数据. 
int bytesSent; 
int bytesRecv = SOCKET_ERROR; 
char sendbuf[32] = "Client: Sending data."; 
char recvbuf[32] = ""; 

bytesSent = send( client, sendbuf, strlen(sendbuf), 0 ); 
printf( "Bytes Sent: %ld/n", bytesSent ); 

while( bytesRecv == SOCKET_ERROR ) { 
bytesRecv = recv( client, recvbuf, 32, 0 ); 
if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) { 
printf( "Connection Closed./n"); 
break; 

if (bytesRecv < 0) 
return; 
printf( "Bytes Recv: %ld/n", bytesRecv ); 

return; 

下面是服务器端代码: 

#include <stdio.h> 
#include <winsock2.h> 
#pragma comment(lib, "ws2_32.lib") 
void main() { 

// 初始化 
WSADATA wsaData; 
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); 
if ( iResult != NO_ERROR ) 
printf("Error at WSAStartup()/n"); 

// 建立socket 
SOCKET server; 
server = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); 

if ( server == INVALID_SOCKET ) { 
printf( "Error at socket(): %ld/n", WSAGetLastError() ); 
WSACleanup(); 
return; 

// 绑定socket 
sockaddr_in service; 

service.sin_family = AF_INET; 
service.sin_addr.s_addr = inet_addr( "127.0.0.1" ); 
service.sin_port = htons( 27015 ); 

if ( bind( server, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) { 
printf( "bind() failed./n" ); 
closesocket(server); 
return; 

// 监听 socket 
if ( listen( server, 1 ) == SOCKET_ERROR ) 
printf( "Error listening on socket./n"); 

// 接受连接 
SOCKET AcceptSocket; 

printf( "Waiting for a client to connect.../n" ); 
while (1) { 
AcceptSocket = SOCKET_ERROR; 
while ( AcceptSocket == SOCKET_ERROR ) { 
AcceptSocket = accept( server, NULL, NULL ); 

printf( "Client Connected./n"); 
server = AcceptSocket; 
break; 

// 发送接受数据 
int bytesSent; 
int bytesRecv = SOCKET_ERROR; 
char sendbuf[32] = "Server: Sending Data."; 
char recvbuf[32] = ""; 

bytesRecv = recv( server, recvbuf, 32, 0 ); 
printf( "Bytes Recv: %ld/n", bytesRecv ); 

bytesSent = send( server, sendbuf, strlen(sendbuf), 0 ); 
printf( "Bytes Sent: %ld/n", bytesSent ); 

return; 

本程序仅仅描述了同步的情况!

PS:本文转自百度贴吧新红盟吧

 

转自:

http://blog.csdn.net/LaoWu_/archive/2010/04/08/5461077.aspx

关于gethostname函数失败的问题

调用gethostname之前, 要先调用WSAStartup才可以, 否则gethostname会失败!

下面是正确的代码

 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <Winsock2.h>  
  5. #include <windows.h>  
  6. #pragma comment(lib, "Ws2_32")  
  7. int main()  
  8. {  
  9.     WSADATA wsData;  
  10.     ::WSAStartup(MAKEWORD(2,2), &wsData);  
  11.     char szIP[32] = {0};  
  12.     char szHostName[32] = {0};  
  13.     int iResult = ::gethostname(szHostName, sizeof(szHostName));  
  14.     if (iResult != 0)  
  15.     {  
  16.         printf("error/n");  
  17.         return -1;  
  18.     }  
  19.     printf("%s/n", szHostName);  
  20.     hostent *pHost = ::gethostbyname(szHostName);  
  21.     ::WSACleanup();  
  22.     return 0;  
  23. }   

 

 

转自:http://blog.csdn.net/leesphone/archive/2008/03/02/2138775.aspx

gethostbyname用法

使用这个东西,首先要包含2个头文件:
#include <netdb.h>
#include <sys/socket.h>
struct hostent *gethostbyname(const char *name);
这个函数的传入值是域名或者主机名,例如"www.google.com","wpc"等等。
传出值,是一个hostent的结构(如下)。如果函数调用失败,将返回NULL。
struct hostent {
     char *h_name;
     char **h_aliases;
     int h_addrtype;
     int h_length;
     char **h_addr_list;
};

解释一下这个结构, 其中:
char *h_name 表示的是主机的规范名。例如www.google.com的规范名其实是www.l.google.com
char **h_aliases 表示的是主机的别名。www.google.com就是google他自己的别名。有的时候,有的主机可能有好几个别名,这些,其实都是为了易于用户记忆而为自己的网站多取的名字。
int h_addrtype 表示的是主机ip地址的类型,到底是ipv4(AF_INET),还是ipv6(AF_INET6)
int h_length 表示的是主机ip地址的长度
int **h_addr_lisst 表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,会有问题的哇。所以到真正需要打印出这个IP的话,需要调用inet_ntop()。
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) :
这个函数,是将类型为af的网络地址结构src,转换成主机序的字符串形式,存放在长度为cnt的字符串中。
这个函数,其实就是返回指向dst的一个指针。如果函数调用错误,返回值是NULL。
下面是例程,有详细的注释。
#include <netdb.h>
#include <sys/socket.h>
int main(int argc, char **argv)
{
char *ptr,**pptr;
struct hostent *hptr;
char str[32];
/* 取得命令后第一个参数,即要解析的域名或主机名 */
ptr = argv[1];
/* 调用gethostbyname()。调用结果都存在hptr中 */
if( (hptr = gethostbyname(ptr) ) == NULL )
{
printf("gethostbyname error for host:%s/n", ptr);
return 0; /* 如果调用gethostbyname发生错误,返回1 */
}
/* 将主机的规范名打出来 */
printf("official hostname:%s/n",hptr->h_name);
/* 主机可能有多个别名,将所有别名分别打出来 */
for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
printf(" alias:%s/n",*pptr);
/* 根据地址类型,将地址打出来 */
switch(hptr->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr=hptr->h_addr_list;
/* 将刚才得到的所有地址都打出来。其中调用了inet_ntop()函数 */
for(;*pptr!=NULL;pptr++)
printf(" address:%s/n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
break;
default:
printf("unknown address type/n");
break;
}
return 0;

 

 

 

转自:http://blog.csdn.net/chollima/archive/2010/06/04/5648065.aspx

 gethostname和gethostbyname,inet_ntoa

  1. // socketTest.cpp : Defines the entry point for the console application.  
  2. //  
  3. #include "stdafx.h"  
  4. #include <WinSock2.h>  
  5. #include <iostream>  
  6. using namespace std;  
  7. int _tmain(int argc, _TCHAR* argv[])  
  8. {  
  9.     WSADATA wsData = { 0 };  
  10.     int nRet = ::WSAStartup(MAKEWORD(2, 2), &wsData);  
  11.     char szBuf[MAX_PATH] = { 0 };  
  12.     ::gethostname(szBuf, MAX_PATH);//获取计算机名  
  13.     struct hostent* pStHostent = NULL;  
  14.     strcpy(szBuf, "www.google.com");  
  15.     pStHostent = ::gethostbyname(szBuf);//传入域名或者主机名,得到指向host结构  
  16.     /* 
  17.     pStHostent->h_name; //规范名 
  18.     pStHostent->h_addrtype;//地址类型IPV4 or IPV6 
  19.     pStHostent->h_addr_list;//ip地址 
  20.     pStHostent->h_length; //ip地址长度 
  21.     pStHostent->h_aliases;//主机别名 
  22.     */  
  23.     cout << pStHostent->h_name << endl;  
  24.     char** pptr = NULL;  
  25.     struct in_addr addr;  
  26.     switch(pStHostent->h_addrtype)  
  27.     {  
  28.     case AF_INET:  
  29.     case AF_INET6:  
  30.         pptr = pStHostent->h_addr_list;  
  31.         for (; *pptr != NULL; pptr++)  
  32.         {  
  33.             //addr.S_un.S_addr = *(u_long*)*pptr;  
  34.             addr.s_addr = *(u_long*)*pptr;  
  35.             //打印出IP地址列表  
  36.             cout << inet_ntoa(addr) << endl;  
  37.         }  
  38.         break;  
  39.     default:  
  40.         cout << "UnKnow addr type!" << endl;  
  41.     }  
  42.     //打印出域名或者计算机名对应的别名列表  
  43.     pptr = pStHostent->h_aliases;  
  44.     for (; *pptr != NULL; pptr++)  
  45.     {  
  46.         cout << *pptr << endl;  
  47.     }  
  48.     ::WSACleanup();  
  49.     return 0;  
  50. }  

抱歉!评论已关闭.