Socket编程详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Socket编程详解相关的知识,希望对你有一定的参考价值。

1  计算机网络基础

1.1 OSI参考模型(略)

1.2 TCP/IP协议

 传输控制协议/网际协议,TCP/IP将网络分为四层,从高往低一次为应用层-》传输层-》网络层-》数据链路层。其中,传输层协议包括TCP,UDP;网络层协议包括IP、ARP等;

2  套接字编程基础

2.1  套接字

套接字是网络编程的基础,最初由加利福尼亚大学为UNIX开发的网络通信编程接口,为了在WINDOWS 操作系统上使用,微软与第三方厂商共同制定了一套标准,Winsock.。

套接字实际上是一个指向传输提供者的句柄,Winsock中通过该句柄实现网络通信与管理。

可以理解为实现网络通信和管理的操作接口。

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())。

其中,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。

(4)监听:lidten()

(5)等待连接:accept()

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;  
}  
  

 






以上是关于Socket编程详解的主要内容,如果未能解决你的问题,请参考以下文章

Java Socket网络编程Server端详解

网络编程socketserver的方法都有哪些

socket编程函数详解

socket技术详解(看清socket编程)

Java开发之Socket编程详解

java网络socket编程详解