搬砖:TCP协议的通讯流程

网络 专栏收录该内容
33 篇文章 0 订阅

 

https://blog.csdn.net/houjixin/article/details/50552970

1.  简单的示例伪程序

服务端伪代码:

  1. <span style="font-family:Microsoft YaHei;font-size:14px;">// 1. 建立一个监听socket

  2. listen_socket = socket(AF_INET, SOCK_STREAM, 0);

  3. // 2. 创建一个监听socket绑定的本地地址结构体

  4. serverAddr.sin_family = AF_INET;

  5. serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用通配地址

  6. serverAddr.sin_port = htons(5500); // 5500是服务器的监听端口号

  7. int ret = bind(listen_socket, (sockaddr*)&serverAddr, sizeof(serverAddr) );

  8. // 3. 开始监听客户端请求

  9. listen(listenfd, SOMAXCONN); // 注意这里的 SOMAXCONN 参数

  10. // 4. 循环接受客户端的请求

  11. while (true){

  12. // 5. accept以阻塞方式工作,当有一个连接进来时,为这个连接产生一个业务socket:business_socket

  13. business_socket = accept(listenfd, (sockaddr*)NULL, NULL);

  14. // 6. 向业务socket:business_socket中写入数据

  15. send(business_socket, buf, strlen(buf), 0 );

  16. // 7. 关闭业务socket

  17. close(business_socket);

  18. }

  19. </span>


客户端的伪代码

 

 

 
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">//1.创建一个客户端使用的socket

  2. client_socket = socket(AF_INET, SOCK_STREAM, 0)

  3. //2.创建一个地址结构体,用于存储服务端的地址信息

  4. servaddr.sin_family = AF_INET;

  5. servaddr.sin_port = htons(5500);

  6. servaddr.sin_addr.s_addr = inet_addr(server_ip);

  7. //3.连接服务器程序

  8. connect(client_socket, (struct sockaddr*) &servaddr, sizeof(servaddr))

  9. //4.从socket中读取数据到recvline

  10. while ( (n = read(client_socket, recvline, MAXLINE)) > 0) {

  11. //5.处理存储在recvline中的内容

  12. }

  13. </span>


2.TCP建立连接及其工作过程

 

 

 

通过上图可以看到TCP的工作过程如下:

[1] 服务器端先启动,创建一个监听socket,将这个socket绑定到一个公开端口,然后监听这个socket以等待客户端的连接请求发送过来;

[2] 客户端后启动,创建一个socket,然后用它连接到服务端的监听socket;

[3] 服务端在监听socket等着客户端连接进来之后,在服务端为这个连接产生一个业务socket,后续服务端就用此业务socket给这个客户端通信;

[4] 客户端在连接建立后,就可以向服务端发送请求数据;

[5] 客户端在请求满足之后,主动关闭连接;

 

(1)    连接建立时的socket五元组变化情况

假设上述的服务端程序运行在192.168.1.201上,客户端运行在192.168.1.202上,根据前面的表述,一个socket可以认为是由一个五元组标识:<本地地址,本地端口,远端地址,远端端口,通信协议>,用*表示一个未确定项(通配项),下面的过程将以这个五元组的变化来描述客户端和服务器的工作过程(图片中黄色代码本地的地址和端口,红色为远端地址和端口):

[1] 服务端创建一个socket,执行代码: listen_socket = socket(AF_INET, SOCK_STREAM,0)后这个socket的五元组的样子是:<*,*,*,*,TCP>,即这时只知道这个socket用TCP协议通信,但是还不知道TCP对应的源端口、IP和目的端口、IP;

[2] 服务端创建一个监听地址,并将监听socket绑定到这个监听地址上,经过该步骤之后,监听socket对应五元组的本地端口号已经被设置为5500:

 

[3] 服务端执行监听操作之后将在监听socket上监听客户端的连接请求;

[4] 客户端创建一个socket,与服务端创建socket时一样,这里创建时只指定了socket五元组的通信协议:TCP;

[5] 客户端执行connect操作,该操作中将指定要连接的服务端的IP地址和端口号,本地的IP地址和端口号由OS帮我们填写,然后与服务器端建立连接,即执行三次握手操作;

[6] 服务端的监听socket接到客户端连接请求之后将为之产生一个业务socket,业务socket的本地地址和端口都有监听socket一致,监听socket指定本地地址,业务socket里要指定,业务socket的远端地址和端口来自这个新连接中,上层业务通过accept获得这个新业务socket;

[7] 多个客户端的socket五元组中,目的地址、目的端口号、通信协议是一样的;同一主机上的客户端(下图中的client1和client2)所对应的socket五元组中只有本地端口号不一样,不同主机上客户端所对应的socket五元组中本地IP地址不一样,本地端口则可能一样;在服务端生成的业务socket中,目的地址和端口号中必须有一个不一样才能对它们进行区分;

[8] 连接建立之后,客户端与服务端之间使用业务socket进行通信

2.3 数据交付过程

问题描述:每台主机可能很多socket在使用,这些socket可能分属于不同的应用进程,但是在操作系统内,他们都是一样的,那么数据到来之后,操作系统是怎么判断数据是属于哪个socket的呢?

在UNIX操作系统内部,每个socket中都包含一个协议控制块,协议控制块中将有每个socket对应的五元组信息,这些协议控制块将被串联起来放在一个链表中,socket结构图和协议控制块中分别有指针指向彼此;如下图TCP的协议控制块链表:

协议控制块属于传输层的组成部分,如下图绿色框所示,TCP和UDP分别有自己独立的链表。 

当TCP收到来自IP层的数据报后,将查找协议控制块列表,通过对比数据报文中端口号等信息,找到通配匹配数最小的协议控制块,即是目标协议控制块中,由于协议控制块中有指针指向对应的socket,也就找到了数据应该交付到哪个socket中,然后将数据放在socket的接收buffer中即可;这里通配匹配数是TCP的本地地址、本地端口、远端地址、远端端口这四项中,在没有确定不匹配项的前提下,用通配符*所匹配的项数,通配匹配数越小,说明匹配度越高;

例如,假设当前server端主机只有一个监听socket和三个业务socket,如下:

本地地址

本地端口

远端地址

远端端口

TCP状态

*

5500

*

*

LISTEN

192.168.1.201

5500

192.168.1.202

3500

ESTABLISH

192.168.1.201

5500

192.168.1.202

3501

ESTABLISH

192.168.1.201

5500

192.168.1.203

3501

ESTABLISH

 

 

 

 

 

当前TCP收到一个来自端口5500的报文段,其源地址为192.168.1.202,源端口为3500,目的地址为192.168.1.201,目的端口为5500(这个报文来自客户端,因此这里要站在客户端的角度来看),此报文与server端主机的所有SOCKET对比如下:

本地地址

本地端口

远端地址

远端端口

通配匹配数

*

5500

*

*

3

192.168.1.201

5500

192.168.1.202

3500

0

192.168.1.201

5500

192.168.1.202

3501

端口不匹配

192.168.1.201

5500

192.168.1.203

3501

Ip和端口不匹配

 

 

 

 

 

第一个socket匹配当前报文段时,只有本地端口5500是完全匹配,本地地址、远端地址和远端端口都是使用通配符*匹配的,因此通配匹配数为3;

第二个socket匹配当前报文段时,全部精确匹配,使用通配符*匹配的项数为0;

第三个socket匹配当前报文段时,其远端端口3501与报文的源端口3500不匹配,因此排除此项;

第四个socket匹配当前报文段时,其远端端口3501和远端IP192.168.1.203与报文段的源端口3500和源IP192.168.1.202都不匹配,因此排除此项;

通过上述的比较,可以得到最小匹配数为0的第二个socket的协议控制块的结构体(PCB),通过此PCB就能知道它对应的socket结构体,接下来就可以将数据放到此socket结构体的接收缓存中。

 

 

 

 

 

https://blog.csdn.net/m0_38121874/article/details/81990457

基于TCP/IP协议的服务器和客户端程序的一般流程,如下图所示:
这里写图片描述
1、服务器初始化——LISTEN
(1)调用socket函数创建文件描述符。
(2)调用bind函数将当前的文件描述符和ip/port绑定在一起。如果这个端口已经被其他进程占用了,就会bind失败。
(3)调用listen函数声明当前这个文件描述符作为一个服务器的文件描述符,为accept做好准备。
(4)调用accept函数阻塞等待客户端连接起来。

2、建立连接的过程——三次握手(绿色部分)
第一次:调用connect函数发出SYN段向服务器发起连接请求,并阻塞等待服务器应答。
第二次:服务器收到客户端的SYN段后,会应答一个SYN-ACK段表示“同一建立连接”。
第三次:服务器端收到SYN-ACK后会从connect函数中返回,同时应答一个ACK段。

3、数据传输的过程(蓝色部分)
建立连接后,TCP协议提供全双工的通信服务。所谓全双工,意思是:在同一条链路中的同一时刻,通信双方可以同时写数据。相对的概念叫做半双工,即:在同一条链路中的同一时刻,只能由一方来写数据。
(1)服务器从accept函数返回后立刻调用read函数读socket里的数据。读socket就像读管道一样,如果没有数据到达就阻塞等待。
(2)客户端调用write函数发送请求给服务器,服务器收到后就向客户端回复ACK,并从read函数中返回,对客户端的请求进行处理。在此期间客户端调用read函数阻塞等待服务器的应答。
(3)服务器调用write函数将处理结果发回客户端,客户端收到后就回复ACK。服务器再次调用read函数阻塞等待下一条请求,。
(4)客户端从read函数中返回,并发送下一条请求,如此循环下去。

4、断开连接的过程——四次挥手(红色部分)
第一次:如果客户端没有更多的请求就调用close函数关闭连接,客户端会向服务器端发送FIN端。
第二次:服务器收到FIN后会回应一个ACK,同时read返回0。
第三次:客户端收到FIN后,再返回一个ACK给服务器。

以上过程,基于TCP/IP 协议的套接字的工作流程,如下图所示:
这里写图片描述
5、问题
(1)为什么要三次握手而不是两次?
答:这主要是为了防止已失效的的连接请求报文段。
假设,客户端发出连接请求报文段,但是该连接请求报文段 在某个网络节点中长时间滞留,导致客户端因为超时又发送第二个连接请求报文段 。第二个连接请求与服务器建立连接,而第一个连接请求报文段 以至于延误到连接释放以后才到达服务器。本来这个连接早已经失效了,如果只采用两次握手的话,服务器会误认为客户端又发送了一次连接请求,从而统一建立连接。所以,采用三次握手可以很好的避免这个问题。

(2)为什么连接的时候是三次握手,断开的时候是四次挥手?
答:因为连接的时候,服务器收到客户端的SYN连接报文后可以直接发送SYN+ACK报文(其中SYN报文是用来同步的,ACK报文是用来应答的);但是,断开的时候,服务器收到客户端的FIN报文后并不会立即关闭SOCKET,而是先回复一个ACK报文,告诉客户端“我收到你发的FIN报文了”,直到服务器的所有报文都发送完了,才发送FIN报文,因此不会一起发送FIN+ACK。故需要四步握手。

(3)为什么有时候是三次挥手?
答:因为假设在服务器发送ack给客户端的时候,服务器端已经没有要发送的数据,则这时服务器会将ACK捎带在第二次挥手的FIN报文里面返回回去,也就是第二和第三合在一起发送了。

(4)为什么TIME_WAIT的时间是2MSL?
答:因为MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失,否则服务器立刻重启,可能会收到来自上一个迟到的数据而引起错误。同时,也是在理论上保证最后一个报文可靠到达。假设最后一个ACK丢失,那么服务器会重发一个FIN。这时,虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK。

(5)服务器关闭之后为什么不能立即重启?
答:这是由于套接字处于TIME_WAIT状态引起的,这个状态会持续2MSL时间。在TIME_WAIT退出后,套接字被删除,该地址才能被重新绑定而不出现问题。

6、注意:
(1)中断连接可以是客户端,也可以是服务端。
假设由客户端中断连接,即客户端发起FIN报文中断连接请求,告诉服务器“我没有数据发给你了”。这时,客户端进入FIN_WAIT_1状态。服务器收到FIN报文后,如果还有数据没有发送完成,则不会急着关闭SOCKET,会继续发送数据。这时,服务器会发送ACK告诉客户端“我收到你的断开请求了,但是我还没有准备好,请等我消息”。这时,客户端就进入FIN_WAIT_2状态,继续等待服务器的FIN报文。当服务器确认数据已经发送完成,则向客户端发送FIN报文,告诉客户端“好了,我这边的数据发完了,准备断开连接”。客户端收到FIN报文后就知道断开连接,但是它不相信网络,怕服务器不知道断开,所以发送ACK后进入TIME_WAIT状态。如果服务器没有收到ACK报文,则客户端会继续重传ACK,服务器收到ACK报文后就知道可以断开连接了。客户端等待2MSL后依然没有收到回复,则证明服务器已正常关闭。那好,我客户端也可以关闭连接了。OK,TCP连接就这样关闭了。

(2)在服务器的TCP连接没有完全断开之前不允许重新监听,某些情况下可能是不合理的。
【例】
服务器需要处理大量的客户端的连接,每个连接的生存时间可能很短,但是每秒都有很多客户端来请求。这个时候,如果由服务器主动关闭连接,比如某些客户端不活跃,就需要被服务器主动清理掉,这时会产生大量的TIME_WAIT连接。由于我们的请求量很大,就可能导致TIME_WAIT的连接数很多而导致服务器的端口不够用,无法处理新的连接。

(3)如何解决TIME_WAIT状态引起的bind失败?
答:使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同,但是IP地址不同的多个socket描述符。即,在server代码的socket函数和bind函数调用之间插入如下代码:
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

 

 

 

 

 

 

     TCP和UDP属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它事先为要发送的数据开辟好连接通道(三次握手),然后再进行数据发送;而UDP则不为IP提供可靠性,一般用于实时的视频流传输,像rtp、rtsp就是建立在udp的基础上的。

     首先谈谈tcp socket

    tcp简单的三次握手过程如图,

    SYN(Synchronize Sequence Numbers):同步标志

    ACK(Acknowledgement Number)        :确认标志

    图中可以看出,三次握手的过程是在c的connect()和s的bind()、listen()、accept()函数中完成的,这样开辟了相对可靠的连接通道,来传输数据。

UDP的socket编程过程如下图所示:

下面翠花上代码啦!大笑

服务端:

    #include <stdio.h>
    #include <Winsock2.h> //windows socket的头文件
     
    #pragma comment( lib, "ws2_32.lib" )// 链接Winsock2.h的静态库文件
     
    void main()
    {
        //初始化winsocket
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
     
        wVersionRequested = MAKEWORD( 1, 1 );//第一个参数为低位字节;第二个参数为高位字节
     
        err = WSAStartup( wVersionRequested, &wsaData );//对winsock DLL(动态链接库文件)进行初始化,协商Winsock的版本支持,并分配必要的资源。
        if ( err != 0 )
        {
            return;
        }
     
        if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16进制数最低位;HIBYTE()取得16进制数最高(最左边)那个字节的内容        
        {
            WSACleanup( );
            return;
        }
     
        SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建socket。AF_INET表示在Internet中通信;SOCK_STREAM表示socket是流套接字,对应tcp;0指定网络协议为TCP/IP
     
        SOCKADDR_IN addrSrv;
        addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);    //htonl用来将主机字节顺序转换为网络字节顺序(to network long)
        //INADDR_ANY就是指定地址为0.0.0.0的地址,
        //表示不确定地址,或“任意地址”。”
        addrSrv.sin_family=AF_INET;
        addrSrv.sin_port=htons(4000);//htons用来将主机字节顺序转换为网络字节顺序(to network short)
     
        bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//将本地地址绑定到所创建的socket上,以使在网络上标识该socket
     
        listen(sockSrv,5);//socket监听,准备接受连接请求。
     
        SOCKADDR_IN addrClient;
        int len=sizeof(SOCKADDR);
     
        while(1)
        {
            SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//为一个连接请求提供服务。addrClient包含了发出连接请求的客户机IP地址信息;返回的新socket描述服务器与该客户机的连接
     
            char sendBuf[50];
            sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));//inet_ntoa网络地址转换转点分十进制的字符串指针
            send(sockConn,sendBuf,strlen(sendBuf)+1,0);
     
            char recvBuf[50];
            recv(sockConn,recvBuf,50,0);
            printf("%s\n",recvBuf);
     
            closesocket(sockConn);
            Sleep(2000);//2000毫秒
        }
        WSACleanup();
    }


客户端:

 

    #include <stdio.h>
    #include <Winsock2.h>
     
    #pragma comment( lib, "ws2_32.lib" )
     
     
    void main()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
     
        wVersionRequested = MAKEWORD( 1, 1 );//第一个参数为低位字节;第二个参数为高位字节
     
        err = WSAStartup( wVersionRequested, &wsaData );//对winsock DLL(动态链接库文件)进行初始化,协商Winsock的版本支持,并分配必要的资源。
        if ( err != 0 )
        {
            return;
        }
     
        if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16进制数最低位;HIBYTE()取得16进制数最高(最左边)那个字节的内容        
        {
            WSACleanup( );
            return;
        }
        for(int index=0;;index++)
        {
            SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
     
            SOCKADDR_IN addrClt;//需要包含服务端IP信息
            addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");// inet_addr将IP地址从点数格式转换成网络字节格式整型。
            addrClt.sin_family=AF_INET;
            addrClt.sin_port=htons(4000);
     
            connect(sockClient,(SOCKADDR*)&addrClt,sizeof(SOCKADDR));//客户机向服务器发出连接请求
            char recvBuf[50];
            recv(sockClient,recvBuf,50,0);
            printf("my reply is : %s\n",recvBuf);
     
            char sendBuf[50];
            sprintf(sendBuf,"%3d,",index);
            strcat(sendBuf,"server node of: yaopeng");
            send(sockClient,sendBuf,strlen(sendBuf)+1,0);
     
            closesocket(sockClient);
            Sleep(2000);
        }
        WSACleanup();
    }

 

https://blog.csdn.net/yaopeng_2005/article/details/6696105

对于tcp socket,有几点需要注意:

一、TCP的TIME_WAIT状态(等待客户端的相应)    

注*  TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟(MSL是最大分段生存期,指明TCP报文在Internet上最长生存时间)

    当服务器端socket绑定本地地址并占用了端口,此时如果匆忙结束;或者连接的服务器异常退出,这个时候被占用的端口不能马上释放,需要TIME_WAIT。即便调用closesocket()一般也不会立即关闭socket,仍可继续重用该socket。所以重新启动服务器时可能会出现问题。例如MFC中在子窗口中实现socket通信,那么关闭子窗口再打开就会出问题了。

     解决方法是在bind()之前添加setsockopt()函数,解除端口绑定。

介绍setsockopt()之前我们再来回顾一下三次握手协议的具体流程:

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

setsockopt()使用方法如下:

    1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

    2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程:
BOOL  bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));

更多setsockopt()函数用例可参考百度百科:http://baike.baidu.com/view/569217.htm

 

 二、对于大型文件,一般需要将其剁碎了一部分一部分的传。TCP不能保证接收方顺序的收到包,对于需要实时显示的文件可以在发送方发出包后设置来自接收方的响应,即对方收到前一个包后再发送下一个包。

目前就这么多,各位看官有其他的注意事项拜托请留言补充,小弟感激啊。

 

下面简单说下UDP socket

    UDP不能保证双方的可靠连接,容易出现丢包现象。

    UDP的socket编程过程如下图所示:

 

上代码了,哈哈。

服务端:

    #include <stdio.h>
    #include <Winsock2.h>
     
    #pragma comment( lib, "ws2_32.lib" )
     
    void main()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
     
        wVersionRequested = MAKEWORD( 1, 1 );
     
        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) {
            return;
        }
     
        if ( LOBYTE( wsaData.wVersion ) != 1 ||
            HIBYTE( wsaData.wVersion ) != 1 ) {
                WSACleanup( );
                return;
        }
        SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
        
        int len=sizeof(SOCKADDR);
        
        SOCKADDR_IN from;    
        SOCKADDR_IN local;
        local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
        local.sin_family=AF_INET;
        local.sin_port=htons(27015);
     
        int a = bind(sockSrv,(SOCKADDR*)&local,len);
     
         
     
        while(1)
        {
            char recvBuf[50];
            recvfrom(sockSrv,recvBuf,50,0,(SOCKADDR*)&from,&len);//from收到客户端的IP信息
            printf("%s\n",recvBuf);
            printf("%s\n",inet_ntoa(local.sin_addr));
            char sendBuf[50];
            sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(from.sin_addr));  
            sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&from,len);
            
            Sleep(2000);
        }
        closesocket(sockSrv);
        WSACleanup();
    }


客户端:

    #include <stdio.h>
    #include <Winsock2.h>
     
    #pragma comment( lib, "ws2_32.lib" )
     
     
    void main()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
     
        wVersionRequested = MAKEWORD( 1, 1 );
     
        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) {
            return;
        }
     
        if ( LOBYTE( wsaData.wVersion ) != 1 ||
            HIBYTE( wsaData.wVersion ) != 1 ) {
                WSACleanup( );
                return;
        }
        
     
        for(int index=0;;index++)
        {
            SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
     
            int len = sizeof(SOCKADDR);
     
             SOCKADDR_IN local;
            local.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");
            local.sin_family=AF_INET;
            local.sin_port=htons(27015);
     
            char sendBuf[30];
            sprintf(sendBuf,"%3d,",index);
            strcat(sendBuf,"server node of: yaopeng");
            sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&local,len);
     
            char recvBuf[50];
            recvfrom(sockClient,recvBuf,50,0,(SOCKADDR*)&local,&len);
            printf("my reply is : %s\n",recvBuf);
            printf("%s\n",inet_ntoa(local.sin_addr));
     
            closesocket(sockClient);
            Sleep(2000);
            WSACleanup();
        }
    }

 
   完了。关于socket编程还有很多函数没有涉及,急待跟进完善。欢迎大家给我留言!大笑
---------------------  
作者:小鹏_加油  
来源:CSDN  
原文:https://blog.csdn.net/yaopeng_2005/article/details/6696105  
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值