孙鑫 第十四课Socket编程之一 简介

1 Socket编程分为基于TCP的和基于UDP的

TCP:面向连接的,可靠的传输,连接要经过三次握手,客户端在连接之前就得按照已知的服务器地址 端口进行连接,服务器端在等到客户端的连接后返回一个连接套接字,之后的信息发送接收就按这条连接进行,不需要再有地址信息。

UDP:面向无连接的,不可靠的,不会进行重发,由于没有连接,因此每次发送都要指明对方的IP 端口,每次接收都要信息的同时要接收对方地址 端口。 

在socket编程中,套接字有三种,流式套接字(SOCK_STREAM), 数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),基于TCP的采用的是流式套接字,基于UDP的采用的是数据报套接字,socket2.0版本不支持原是套接字。

 

基于TCP的套接字编程

★ 基于TCP的服务器端套接字编程步骤

①协商版本 / 初始化(WSAStartup)

②建立服务器套接字(socket)

③套接字绑定本机IP 和 端口(bind)

④设置为监听模式(listen)

⑤开始监听连接,如果有连接到来则返回一个连接套接字(同时取得客户端地址 端口),否则一直在此监听。(accept)

⑥使用连接套接字开始和客户端通信(发送 / 接收)。(send / recv)

⑦通信完毕,关闭套接字(连接套接字 /  服务器套接字)。(closesoket)

⑧释放资源,删除套接字库的使用。(WSACleanup)

 

★ 基于TCP的客户端套接字编程步骤

①协商版本 / 初始化 (WSAStartup)

②建立客户端套接字 (socket)

③指定服务器地址 / 端口。(SOCKADDR_IN addrServer)

④连接服务器(connect)

⑤开始通信(发送 / 接收)。(send / recv)

⑥通信完毕,关闭套接字。(closesoket)

⑦释放资源,删除套接字库的使用。(WSACleanup)

 

NOTE

基于TCP的socket编程中服务器端和客户端的区别:

①客户端不用绑定本地IP 和 端口。

②由于服务器是被动的等待连接,因此必须先由客户端发起连接请求(这样服务器才能知道客户端的地址 端口),而客户端在连接服务器之前就应该已经知道了服务器地址。

 

3 基于UDP的套接字编程

★ 基于UPD的服务器端套接字编程步骤

①协商版本 / 初始化(WSAStartup)

②建立套接字(socket)

③绑定本地IP 端口(bind)

④通信, 发送 / 接收 (sendto / recvfrom)

⑤通信完毕,关闭套接字(closesocket)

⑥释放资源,删除使用套接字库(WSACleanup)

 

★ 基于UDP的客户端套接字变成步骤

①协商版本 / 初始化(WSAStartup)

②建立套接字 (socket)

③通信,发送 / 接收(sendto / recvfrom)

④通信完毕,关闭套接字(closesoket)

⑤释放资源,删除使用套接字库(WSACleanup)

 

NOTE:

基于UDP的客户端编程中客户端不必发起连接请求,为了能达到服务器,需要在sendto函数中指定地址端口。 

服务器段也需要在sendto函数中指定客户端的地址端口。

而在基于TCP的服务器中,在返回连接套接字后,已经和对方建立了连接,也就是说早accept函数中取得了客户端的地址。

基于TCP的客户端中,在发起连接请求的时候要指定服务器的地址和端口,因此连接建立以后的send / recv 都不再需要地址端口。

  

 

4 TCP实例

下面是孙鑫老师视频中讲的一个基于TCP的socket编程例子(稍有改动),提供了socekt TCP编程的基本思路。

服务器端

#include <WinSock2.h>  //必须的头文件

#include <iostream>

#include "stdio.h"

#pragma comment(lib, "ws2_32.lib")  //指定连接的库文件,取代在VC工程设置里添加

using namespace std; 

int main()

{

    WORD wVersionRequested;//版本号 

    WSADATA wsaData; 

    int err;

    wVersionRequested = MAKEWORD( 1, 1 );//1.1版本的套接字, 协商版本 初始化

    err = WSAStartup( wVersionRequested, &wsaData ); 

    if ( err != 0 ) //加载套接字库,加裁失败则返回

    { 

        cerr<<"startup error!"<<endl;

        return -1;

    }

    if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ) //如果不是1.1的则退出

    { 

        WSACleanup( );

        cerr<<"version error!"<<endl;

        return -1; 

     }

    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0); //创建套接字(socket)。

    SOCKADDR_IN addrSrv; 

    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//转换Unsigned short为网络字节序的格式 ,也可以按下句写法

    //addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");   //同上句,假如只有一块网卡

    addrSrv.sin_family=AF_INET; 

    addrSrv.sin_port=htons(6000); 

    bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//将套接字绑定到一个本地地址和端口上(bind)

    listen(sockSrv,5);//将套接字设为监听模式,准备接收客户请求(listen)。

    SOCKADDR_IN addrClient;//定义地址族

    int len=sizeof(SOCKADDR);//初始化这个参数,这个参数必须被初始化

    while(1)

    {

        SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//accept的第三个参数一定要有初始值。

        //等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。

        //此时程序在此发生阻塞

        char sendBuf[100];

        sprintf(sendBuf,"Welcome %s tohttp://www.sunxin.org",inet_ntoa(addrClient.sin_addr));//用返回的套接字和客户端                                                                                                         // 进行通信(send / to)

        send(sockConn,sendBuf,strlen(sendBuf)+1,0); 

        char recvBuf[100]; 

        recv(sockConn,recvBuf,100,0);

        printf("%s\n",recvBuf);

        closesocket(sockConn);//关闭套接字。等待另一个用户请求

    } 

    closesocket(sockSrv); //关闭服务器套接字

    WSACleanup();  //释放资源

    return 0;

}

 

客户端

#include <Winsock2.h>

#include <stdio.h> 

#include <iostream>

#pragma comment(lib, "ws2_32.lib")  //加载库

int main() 

    WORD wVersionRequested; 

    WSADATA wsaData; 

    int err;

    wVersionRequested = MAKEWORD( 1, 1 ); 

    err = WSAStartup( wVersionRequested, &wsaData ); //初始化, 协商版本

    if ( err != 0 ) 

    { 

        cerr<<"startup error!"<<endl;

        return -1; 

    } 

    if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )

    { 

        WSACleanup( );

        cerr<<"version error!"<<endl;

        return -1;

     } 

    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_addr = inet_addr("127.0.0.1"); 

    addrSrv.sin_family=AF_INET; 

    addrSrv.sin_port=htons(6000); 

    connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //向服务器发出连接请求(connect)。 

    char recvBuf[100];    //和服务器端进行通信(send/recv)。

    recv(sockClient,recvBuf,100,0);

    printf("%s\n",recvBuf);send(sockClient,"This is lisi",strlen("This is lisi")+1,0);

    closesocket(sockClient); //关闭套接字。 

    WSACleanup();//必须调用这个函数清除参数

    }

    return 0;

}

 

5 基于UDP的套接字编程实例

服务器端//

#include <iostream>

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

using namespace std;

#define LOW_VERSION 2

#define HIGH_VERSION 0

#define PORT 6000

 

int main()

{

    WORD wVersionRequested;

    WSADATA wsaData;

    wVersionRequsted = MAKEWORD(LOW_VERSION, HIGH_VERSION);

    if( 0 != WSAStartup(wVersionRequsted, &wsaData);

    {

        cerr<<"startup error!"<<endl;

        WSACleanup();

        return -1;

    }

    if(LOBYTE(wsaData.wVersion) != LOW_VERSION || HIBYTE(wsaData.wVersion) != HI_VERSION)

    {

        cerr<<"version error!"<<endl;

        WSACleanup();

        return -1;

    }

    SOCKET sockServer = socket(AF_INET, SOCK_DGRAM, 0);  // 建立服务器套接字

    SOCKADDR_IN   addr_server; //服务器地址端口结构体

    char* ip = "127.0.0.1";

    addr_server.sin_addr.s_addr = inet_addr(ip);

    addr_server.sin_family = AF_INET;

    addr_server.sin_port = htons(PORT);

    bind(sockServer, (SOCKADDR*) &addr_server, sizeof(SOCKADDR));

 

    char recvBuffer[100];

    memset(recvBuffer, '\0', sizeof(recvBuffer) );

    char sendBuffer[100] = "hello client!";

 

    SOCKADDR_IN addr_client;

    int len = sizeof(SOCKADDR);

    while(1)

    {

        //服务器执行到recvfrom发生阻塞,一直在此监听信息到达,接收信息的同时接收到对方IP 和 端口

        recvfrom(sockServer, recvBuffer, sizeof(recvbuffer), 0, (SOCKADDR*) &addr_client, &len); //最后一个参数一定要初始                                                                                                                                            //化

        sendto(sockServer, sendBuffer, strlen(sendBuffer) + 1, 0, (SOCKADDR*) &addr_client, sizeof(SOCKADDR));

        //codes

    }

    closesocket(sockServer);

    WSACleanup();

    return 0;

}

 

//客户端/

#include <iostream>

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

using namespace std;

#define LOW_VERSION 2

#define HIGH_VERSION 0

#define PORT 6000

 

int main()

{

    WORD wVersionRequested;

    WSADATA wsaData;

    wVersionRequsted = MAKEWORD(LOW_VERSION, HIGH_VERSION);

    if( 0 != WSAStartup(wVersionRequsted, &wsaData);

    {

        cerr<<"startup error!"<<endl;

        WSACleanup();

        return -1;

    }

    if(LOBYTE(wsaData.wVersion) != LOW_VERSION || HIBYTE(wsaData.wVersion) != HI_VERSION)

    {

        cerr<<"version error!"<<endl;

        WSACleanup();

        return -1;

    }

    SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);

    SOCKADDR_IN   addr_server; //服务器地址端口结构体

    char* ip = "127.0.0.1";

    addr_server.sin_addr.s_addr = inet_addr(ip);

    addr_server.sin_family = AF_INET;

    addr_server.sin_port = htons(PORT);

    int len = sizeof(SOCKADDR);

 

    char recvBuffer[100];

    memset(recvBuffer, '\0', sizeof(recvBuffer) );

    char sendBuffer[100] = "hello server!";

    while(1)

    {

        sendto(sockClient, sendBuffer, strlen(sendBuffer) + 1, 0, (SOCKADDR*) &addr_server, sizeof(SOCKADDR));

        recvfrom(sockClient, recvBuffer, sizeof(recvBuffer), 0, (SOCKADDR*) &addr_server, &len);

        //codes

    }

    closesocket(sockClient);

    WSACleanup();

    return 0;

}

 

 

孙鑫 第十四课Socket编程之一 简介 - 大灰狼 - 大灰狼 的博客

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值