溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

socket的一些函數(shù)分析

發(fā)布時(shí)間:2021-10-14 15:19:21 來(lái)源:億速云 閱讀:163 作者:柒染 欄目:編程語(yǔ)言

這篇文章給大家介紹socket的一些函數(shù)分析,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

在網(wǎng)絡(luò)編程中,最常用和最基礎(chǔ)的就是WINSOCK. 現(xiàn)在我們討論WINDOWS下的SOCKET編程. 
     
        大凡在WIN32平臺(tái)上的WINSOCK編程都要經(jīng)過(guò)下列步驟: 
      定義變量->獲得WINDOCK版本->加載WINSOCK庫(kù)->初始化->創(chuàng)建套接字->設(shè)置套接字選項(xiàng)->關(guān)閉套接字->卸載WINSOCK庫(kù)->釋放資源 

     下面介紹WINSOCK C/S的建立過(guò)程: 

     服務(wù)器                         客戶端 
________________________________________________ 
1   初始化WSA                      1   初始化WSA 
____________________________________________________ 
2   建立一個(gè)SOCKET                 2   建立一個(gè)SOCKET 
_____________________________________________________ 
3   綁定SOCKET                     3   連接到服務(wù)器 
_____________________________________________________ 
4   在指定的端口監(jiān)聽(tīng)               4   發(fā)送和接受數(shù)據(jù) 
_____________________________________________________ 
5   接受一個(gè)連接                   5    斷開(kāi)連接 
______________________________________________________- 
6   發(fā)送和接受數(shù)據(jù) 
___________________________________________________ 
7   斷開(kāi)連接 
__________________________________________________ 
   
    大家注意,在VC中進(jìn)行WINSOCK編程時(shí),需要引入如下兩個(gè)庫(kù)文件:WINSOCK.H(這個(gè)是WINSOCK API的頭文件,WIN2K以上支持WINSOCK2,所以 
可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API連接庫(kù)文件). 
使用方式如下: 
               #include <winsock.h> 
       #pragma comment(lib,"ws2_32.lib") 
   
   下面我們通過(guò)具體的代碼演示服務(wù)器和客戶端的工作流程: 

首先,建立一個(gè)WSADATA結(jié)構(gòu),通常用wsaData 
WSADATA wsaData; 

然后,調(diào)用WSAStartup函數(shù),這個(gè)函數(shù)是連接應(yīng)用程序與winsock.dll的第一個(gè)調(diào)用.其中,第一個(gè)參數(shù)是WINSOCK 版本號(hào),第二個(gè)參數(shù)是指向 
WSADATA的指針.該函數(shù)返回一個(gè)INT型值,通過(guò)檢查這個(gè)值來(lái)確定初始化是否成功.調(diào)用格式如下:WSAStartup(MAKEWORD(2,2),&wsaData),其中 
MAKEWORD(2,2)表示使用WINSOCK2版本.wsaData用來(lái)存儲(chǔ)系統(tǒng)傳回的關(guān)于WINSOCK的資料. 

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

printf("WSAStartup failed:%d",GetLastError());   //返回值不等與0,說(shuō)明初始化失敗 
         ExitProcess();                                   //退出程序 

應(yīng)用程序在完成對(duì)請(qǐng)求的SOCKET庫(kù)使用后,要調(diào)用WSACleanup函數(shù)來(lái)接觸SOCKET庫(kù)的綁定,并且釋放資源. 

注意WSAStartup初始化后,必須建立一個(gè)SOCKET結(jié)構(gòu)來(lái)保存SOCKET句柄. 

下面我們建立一個(gè)SOCKET. 

首先我們建立一個(gè)m_socket的SOCKET句柄,接著調(diào)用socket()函數(shù),函數(shù)返回值保存在m_socket中.我們使用AF_INFE,SOCK_STREAM,IPPROTO_TCP 
三個(gè)參數(shù).第一個(gè)表示地址族,AF_INFE表示TCP/IP族,第二個(gè)表示服務(wù)類型,在WINSOCK2中,SOCKET支持以下三種類型; 

SOCK_STREAM 流式套接字 
SOCK_DGRAM   數(shù)據(jù)報(bào)套接字 
SOCK_RAW     原始套接字 

第三個(gè)參數(shù)表示協(xié)議: 

IPPROTO_UDP   UDP協(xié)議 用于無(wú)連接數(shù)據(jù)報(bào)套接字 
IPPROTO_TCP   TCP協(xié)議 用于流式套接字 
IPPROTO_ICMP ICMP協(xié)議用于原始套接字 

m_socket=socket(AF_INFE,SOCK_STREAM,IPPROTO_TCP);     //創(chuàng)建TCP協(xié)議 

以下代碼用于檢查返回值是否有錯(cuò)誤: 

if(m_scoket==INVALID_SOCKET) 

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

說(shuō)明,如果socket()調(diào)用失敗,他將返回INVALID_SOCKET. 

     為了服務(wù)器能接受一個(gè)連接,他必須綁定一個(gè)網(wǎng)絡(luò)地址,下面的代碼展示如何綁定一個(gè)已經(jīng)初始化的IP和端口的Socket.客戶端程序用這個(gè) 
IP地址和端口來(lái)連接服務(wù)器. 

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將要綁定的端口 

下面我們調(diào)用BIND函數(shù),把SOCKET和SOCKADDR以參數(shù)的形式傳入,并檢查錯(cuò)誤. 

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

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

    當(dāng)綁定完成后,服務(wù)器必須建立一個(gè)監(jiān)聽(tīng)隊(duì)列,以接受客戶端的請(qǐng)求.listen()使服務(wù)器進(jìn)入監(jiān)聽(tīng)狀態(tài),該函數(shù)調(diào)用成功返回0,否則返回 
SOCKET_ERROR.代碼如下: 

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

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

服務(wù)器端調(diào)用完LISTEN()后,如果此時(shí)客戶端調(diào)用CONNECT()函數(shù),服務(wù)器端必須在調(diào)用ACCEPT().這樣服務(wù)器和客戶端才算正式完成通信程序的 
連接動(dòng)作. 

一旦服務(wù)器開(kāi)始監(jiān)聽(tīng),我們就要指定一個(gè)句柄來(lái)表示利用ACCEPT()函數(shù)接受的連接,這個(gè)句柄是用來(lái)發(fā)送和接受數(shù)據(jù)的表示.建立一個(gè)SOCKET句柄 
Socket AcceptSocket 然后利用無(wú)限循環(huán)來(lái)檢測(cè)是否有連接傳入.一但有連接請(qǐng)求,ACCEPT()函數(shù)就會(huì)被調(diào)用,并且返回這次連接的句柄. 

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將要綁定的端口 

下面調(diào)用CONNECT()函數(shù): 

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

         printf( "Failed to connect./n" ); 
         WSACleanup(); 
         return; 
}                                                                      //如果調(diào)用失敗清理退出 
                                                                       //調(diào)用成功繼續(xù)讀寫(xiě)數(shù)據(jù) 

_________________________________________________________________________________________________ 
到這里,服務(wù)器和客戶端的基本流程介紹完畢,下面我們介紹數(shù)據(jù)交換. 

send(): 
int send 

SOCKET s,                     //指定發(fā)送端套接字 
const char FAR?*buf,          //指明一個(gè)存放應(yīng)用程序要發(fā)送的數(shù)據(jù)的緩沖區(qū) 
int len,                      //實(shí)際要發(fā)送的數(shù)據(jù)字節(jié)數(shù) 
int flags                     //一般設(shè)置為0 
}; 
C/S都用SEND函數(shù)向TCP連接的另一端發(fā)送數(shù)據(jù). 

recv(): 
int recv 

SOCKET s,                     //指定發(fā)送端套接字 
char FAR?*buf,               //指明一個(gè)緩沖區(qū) 存放RECC受到的數(shù)據(jù) 
int len,                      //指明BUF的長(zhǎng)度 
int flags                     //一般設(shè)置為0 

}; 
C/S都使用RECV函數(shù)從TCP連接的另一端接受數(shù)據(jù) 

下面將完整的程序代碼提供如下,大家可直接編譯運(yùn)行 首先看客戶端的代碼: #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; } // 連接到服務(wù)器. 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; } // 發(fā)送并接收數(shù)據(jù). 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; } 下面是服務(wù)器端代碼: #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; } // 監(jiān)聽(tīng) 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; } // 發(fā)送接受數(shù)據(jù) 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:本文轉(zhuǎn)自百度貼吧新紅盟吧

 

轉(zhuǎn)自:

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

關(guān)于gethostname函數(shù)失敗的問(wèn)題

調(diào)用gethostname之前, 要先調(diào)用WSAStartup才可以, 否則gethostname會(huì)失敗!

下面是正確的代碼

 

view plaincopy to clipboardprint?
  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. }   

 

 

轉(zhuǎn)自:http://blog.csdn.net/leesphone/archive/2008/03/02/2138775.aspx

gethostbyname用法使用這個(gè)東西,首先要包含2個(gè)頭文件:
#include <netdb.h>
#include <sys/socket.h>
struct hostent *gethostbyname(const char *name);
這個(gè)函數(shù)的傳入值是域名或者主機(jī)名,例如"www.google.com","wpc"等等。
傳出值,是一個(gè)hostent的結(jié)構(gòu)(如下)。如果函數(shù)調(diào)用失敗,將返回NULL。
struct hostent {
     char *h_name;
     char **h_aliases;
     int h_addrtype;
     int h_length;
     char **h_addr_list;
};

解釋一下這個(gè)結(jié)構(gòu), 其中:
char *h_name 表示的是主機(jī)的規(guī)范名。例如www.google.com的規(guī)范名其實(shí)是www.l.google.com。
char **h_aliases 表示的是主機(jī)的別名。www.google.com就是google他自己的別名。有的時(shí)候,有的主機(jī)可能有好幾個(gè)別名,這些,其實(shí)都是為了易于用戶記憶而為自己的網(wǎng)站多取的名字。
int h_addrtype 表示的是主機(jī)ip地址的類型,到底是ipv4(AF_INET),還是ipv6(AF_INET6)
int h_length 表示的是主機(jī)ip地址的長(zhǎng)度
int **h_addr_lisst 表示的是主機(jī)的ip地址,注意,這個(gè)是以網(wǎng)絡(luò)字節(jié)序存儲(chǔ)的。千萬(wàn)不要直接用printf帶%s參數(shù)來(lái)打這個(gè)東西,會(huì)有問(wèn)題的哇。所以到真正需要打印出這個(gè)IP的話,需要調(diào)用inet_ntop()。
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) :
這個(gè)函數(shù),是將類型為af的網(wǎng)絡(luò)地址結(jié)構(gòu)src,轉(zhuǎn)換成主機(jī)序的字符串形式,存放在長(zhǎng)度為cnt的字符串中。
這個(gè)函數(shù),其實(shí)就是返回指向dst的一個(gè)指針。如果函數(shù)調(diào)用錯(cuò)誤,返回值是NULL。
下面是例程,有詳細(xì)的注釋。
#include <netdb.h>
#include <sys/socket.h>
int main(int argc, char **argv)
{
char *ptr,**pptr;
struct hostent *hptr;
char str[32];
/* 取得命令后第一個(gè)參數(shù),即要解析的域名或主機(jī)名 */
ptr = argv[1];
/* 調(diào)用gethostbyname()。調(diào)用結(jié)果都存在hptr中 */
if( (hptr = gethostbyname(ptr) ) == NULL )
{
printf("gethostbyname error for host:%s/n", ptr);
return 0; /* 如果調(diào)用gethostbyname發(fā)生錯(cuò)誤,返回1 */
}
/* 將主機(jī)的規(guī)范名打出來(lái) */
printf("official hostname:%s/n",hptr->h_name);
/* 主機(jī)可能有多個(gè)別名,將所有別名分別打出來(lái) */
for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
printf(" alias:%s/n",*pptr);
/* 根據(jù)地址類型,將地址打出來(lái) */
switch(hptr->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr=hptr->h_addr_list;
/* 將剛才得到的所有地址都打出來(lái)。其中調(diào)用了inet_ntop()函數(shù) */
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;

 

 

 

轉(zhuǎn)自:http://blog.csdn.net/chollima/archive/2010/06/04/5648065.aspx

 gethostname和gethostbyname,inet_ntoaview plaincopy to clipboardprint?
  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);//獲取計(jì)算機(jī)名

  13.     struct hostent* pStHostent = NULL;  

  14.     strcpy(szBuf, "www.google.com");  

  15.     pStHostent = ::gethostbyname(szBuf);//傳入域名或者主機(jī)名,得到指向host結(jié)構(gòu)

  16.     /*

  17.     pStHostent->h_name; //規(guī)范名

  18.     pStHostent->h_addrtype;//地址類型IPV4 or IPV6

  19.     pStHostent->h_addr_list;//ip地址

  20.     pStHostent->h_length; //ip地址長(zhǎng)度

  21.     pStHostent->h_aliases;//主機(jī)別名

  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.     //打印出域名或者計(jì)算機(jī)名對(duì)應(yīng)的別名列表

  43.     pptr = pStHostent->h_aliases;  

  44.     for (; *pptr != NULL; pptr++)  

  45.     {  

  46.         cout << *pptr << endl;  

  47.     }  

  48.     ::WSACleanup();  

  49.     return 0;  

  50. }  

關(guān)于socket的一些函數(shù)分析就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI