Socket編程詳解

1  計算機網絡基礎編程

1.1 OSI參考模型(略)服務器

1.2 TCP/IP協議網絡

 傳輸控制協議/網際協議,TCP/IP將網絡分爲四層,從高往低一次爲應用層-》傳輸層-》網絡層-》數據鏈路層。其中,傳輸層協議包括TCP,UDP;網絡層協議包括IP、ARP等;socket

如圖所示:函數

注:圖片來源http://blog.csdn.net/lizhifeng2009/article/details/8820228測試

2  套接字編程基礎ui

2.1  套接字spa

套接字是網絡編程的基礎,最初由加利福尼亞大學爲UNIX開發的網絡通訊編程接口,爲了在WINDOWS 操做系統上使用,微軟與第三方廠商共同制定了一套標準,Winsock.。操作系統

套接字其實是一個指向傳輸提供者的句柄,Winsock中經過該句柄實現網絡通訊與管理。.net

能夠理解爲實現網絡通訊和管理,在應用層與傳輸層之間的操做接口。對於用戶而言,可經過調用SOCKET 相關接口函數,控制傳輸協議、傳輸內容等。

具體如圖所示:

 

2.1.1 套接字類型

 根據套接字做用不一樣,分爲三類:原始套接字、流式套接字和數據包套接字。

原始套接字(SOCK_RAW):可以使程序人員對底層的網絡機制進行控制,接收的數據包頭中含有IP頭。

流式套接字(SOCK_STREAM):提供了雙向、有序、可靠的數據傳輸服務,在通訊前須要雙方創建鏈接,TCP協議採用了該套接字。

數據包套接字(SOCK_DGRAM):與流式套接字相對應,提供雙向數據流,可是不能保證數據傳輸的可靠性、有序性和無重複性。UDP協議採用了該套接字。

//2.2 套接字I/O模型

(暫缺)
2.3 套接字函數

爲了使用套接字進行網絡程序開發,Windows操做系統提供了一組套接字函數,使用這些函數,能夠實現功能強大的網絡編程。經常使用可分爲三大類:初始化庫函數(WSAStartup()、WSACleanup())、通訊時使用函數(socket()、bind()、listen()、accept()、closesocket()、connect()、recv()、send()、select()、ioctlsocket())和地址轉換函數(htons()、htonl()、inet_addr())。其中,TCP的三次握手是經過connect 函數觸發。

其中,WSAStartup()用於初始化動態連接庫函數,WSACleanup()用於釋放動態連接庫初始化時分配的資源。

注:套接字函數一般封裝在Ws2_32.dll動態連接庫中,其頭文件Winsock2.h提供了套接字函數的原型,庫文件Ws2_32.lib提供了Ws2_32.dll文件的輸出節,在使用套接字函數前,需包含頭文件 ,並連接Ws2_32.lib。

#include <winsock2.h>

#pragma comment(lib,"ws2_32.lib")

 

3  網絡編程步驟

實現網絡間通訊,須要一個服務器端和一個客戶端。這二者之間的具體實現有點不一樣,大致步驟爲:

3.1 服務器端

(1)建立套接字:socket()建立套接字,指明傳輸協議  

例如:
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
//sListen表明返回的套接字

//AF_INET爲IPV4地址家族
//SOCK_STREAM爲套接字類型 
//
IPPROTO_TCP指明傳輸協議

(2)構建本地地址信息  

//構建本地地址信息  
    saServer.sin_family = AF_INET; //地址家族  
    saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡字節序  
    saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址  

(3)bind()函數把一個地址族中的特定地址賦給socket。

//綁定
ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));  
    if (ret == SOCKET_ERROR)  
    {  
        printf("bind() faild! code:%d\n", WSAGetLastError());  
        closesocket(sListen); //關閉套接字  
        WSACleanup();  
        //return 0;  
    }  

(4)監聽:listen()

 //偵聽鏈接請求  
    ret = listen(sListen, 5);  
    if (ret == SOCKET_ERROR)  
    {  
        printf("listen() faild! code:%d\n", WSAGetLastError());  
        closesocket(sListen); //關閉套接字  
        //return 0;  
    }  
    

(5)等待鏈接:accept()

accept()函數提取出所監聽套接字的等待鏈接隊列中第一個鏈接請求建立一個新的套接字,並返回指向該套接字的文件描述符;

函數原型爲:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

3.2 客戶端

(1)建立套接字

(2)構建服務器地址

(3)鏈接服務器connect()

4  實例

下面經過一個簡單的客戶端/服務器端程序,講解通訊編程的具體實現:

//服務器端
// Server.cpp : 定義控制檯應用程序的入口點。
//TCP測試程序的服務器端程序
#include "stdafx.h"  
#include <stdio.h>  
#include <winsock2.h>  
#pragma comment(lib,"ws2_32.lib") 
#define SERVER_PORT 5208 //偵聽端口  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    WORD wVersionRequested;  
    WSADATA wsaData;  
    int ret, nLeft, length;  
    SOCKET sListen, sServer; //偵聽套接字,鏈接套接字  
    struct sockaddr_in saServer, saClient; //地址信息     
    char *ptr;//用於遍歷信息的指針     
    //WinSock初始化  
    wVersionRequested=MAKEWORD(2, 2); //但願使用的WinSock DLL 的版本  
    ret=WSAStartup(wVersionRequested, &wsaData);  
    if(ret!=0)  
    {  
        printf("WSAStartup() failed!\n");  
        //return 0;  
    }  
    //建立Socket,使用TCP協議  
    sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
    if (sListen == INVALID_SOCKET)  
    {  
        WSACleanup();  
        printf("socket() faild!\n");  
        //return 0;  
    }  
    //構建本地地址信息  
    saServer.sin_family = AF_INET; //地址家族  
    saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡字節序  
    saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址  
    
    //綁定  
    ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));  
    if (ret == SOCKET_ERROR)  
    {  
        printf("bind() faild! code:%d\n", WSAGetLastError());  
        closesocket(sListen); //關閉套接字  
        WSACleanup();  
        //return 0;  
    }  
    
    //偵聽鏈接請求  
    ret = listen(sListen, 5);  
    if (ret == SOCKET_ERROR)  
    {  
        printf("listen() faild! code:%d\n", WSAGetLastError());  
        closesocket(sListen); //關閉套接字  
        //return 0;  
    }  
    
    printf("Waiting for client connecting!\n");  
    printf("Tips: Ctrl+c to quit!\n");  

    //阻塞等待接受客戶端鏈接  
    while(1)//循環監聽客戶端,永遠不中止  
    {  
        length = sizeof(saClient);  
        sServer = accept(sListen, (struct sockaddr *)&saClient, &length);  
        if (sServer == INVALID_SOCKET)  
        {  
            printf("accept() faild! code:%d\n", WSAGetLastError());  
            closesocket(sListen); //關閉套接字  
            WSACleanup();  
            return 0;  
        }    
  
        char sendMessage[]="\nhello client";  //發送信息給客戶端  
        send(sServer,sendMessage,strlen(sendMessage)+1,0);  
  
        char receiveMessage[5000];  
        nLeft = sizeof(receiveMessage);  
        ptr = (char *)&receiveMessage;  
        while(nLeft>0)  
        {  
            //接收數據  
            ret = recv(sServer, ptr, 5000, 0);    //非負,成功;-1,失敗
            if (ret == SOCKET_ERROR)  
            {  
                printf("recv() failed!\n");  
                return 0;  
            }  
            if (ret == 0) //客戶端已經關閉鏈接  
            {  
                printf("Client has closed the connection\n");  
                break;  
            }  
            nLeft -= ret;  
            ptr += ret;  
        }    
        printf("receive message:%s\n", receiveMessage);//打印咱們接收到的消息。  
          
  
    }   
  //  closesocket(sListen);  
  //  closesocket(sServer);  
  //  WSACleanup();  
    return 0;  
}  
//TCP測試程序的客戶端程序
#include "stdafx.h"  
#include <stdio.h>  
#include <stdlib.h>  
#include <winsock2.h>  
#pragma comment(lib,"ws2_32.lib")
#define SERVER_PORT 5208 //偵聽端口  
  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    WORD wVersionRequested;  
    WSADATA wsaData;            //WSADATA爲Windows套接字結構體
    int ret;  
    SOCKET sClient; //鏈接套接字  
    struct sockaddr_in saServer; //服務器地址信息  
    char *ptr;  

    BOOL fSuccess = TRUE;  
    
    //---------------------------------------------------------------
    //WinSock初始化  
    //---------------------------------------------------------------
    wVersionRequested = MAKEWORD(2, 2); //但願使用的WinSock DLL的版本  
    ret = WSAStartup(wVersionRequested, &wsaData);  //加載套接字庫  
    if(ret!=0)  
    {  
        printf("WSAStartup() failed!\n");  
        //return 0;  
    }  
    //確認WinSock DLL支持版本2.2  
    if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)  
    {  
        WSACleanup();   //釋放爲該程序分配的資源,終止對winsock動態庫的使用  
        printf("Invalid WinSock version!\n");  
        //return 0;  
    }  
    
    //建立Socket,使用TCP協議  
    sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
    if (sClient == INVALID_SOCKET)  
    {  
        WSACleanup();  
        printf("socket() failed!\n");  
        //return 0;  
    }  
  
    //構建服務器地址信息  
    saServer.sin_family = AF_INET; //地址家族  
    saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡節序  
    saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  
  
    //鏈接服務器  
    ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));  
    if (ret == SOCKET_ERROR)  
    {  
        printf("connect() failed!\n");  
        closesocket(sClient); //關閉套接字  
        WSACleanup();  
        //return 0;  
    }  
  
  
    char sendMessage[]="hahaha!!!";   
    ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0);  
    if (ret == SOCKET_ERROR)  
    {  
        printf("send() failed!\n");  
    }  
    else  
        printf("client info has been sent!");  
    char recvBuf[100];  
    recv(sClient,recvBuf,100,0);  
    printf("%s\n",recvBuf);  
    
    closesocket(sClient); //關閉套接字  
    WSACleanup();  
    //getchar();  沒啥用,讓你最後在顯示終端能夠輸入一串字符,可是不能發送
    //return 0;  
}  
  
相關文章
相關標籤/搜索