当前位置: 代码迷 >> 综合 >> SOCKET编程 send recv API函数。。。。
  详细解决方案

SOCKET编程 send recv API函数。。。。

热度:2   发布时间:2024-01-15 23:34:22.0

最近一直在搞一个SOKCET编程。。其实网络的步骤跟平时通信差不多,比如首先选择什么通信方式,如果是写信,那么首先知道邮编相当于主机地址的IP。具体地址,比如美国,纽约,具体到街道,楼。可以看成是www.usmc.com,那么门牌号可以看成是port。然后你选择信纸,选择语言,写信封装到信封里,这个就是你自己的数据包,然后写上地址。姓名。这个就是可以看成TCP & IP封装。然后扔到楼下的邮筒里,这个相当于给系统了,具体邮递员这么投递,怎么到分局,这么送到美帝国主义那里,就不清楚了。对方拆信也一样。


套接字在网络中式用来发送和传送数据包的。IP协议在定义了Internet通信中使用的基本单元,称为数据报,长度一般小于1000字节。IP数据报对于源计算机和目标计算机来说都包含了32为的地址,TCP协议在两台机器之间建立了全双工,点对点的连接,IP地址和端口号的组合称为一个套接字(以上摘自VC6.0技术内幕,没有找到英文语句)

从MSDN获得一些帮助,也从网络,主要是那些研究UNIX/Linux的coder那里获得了很多的帮助。毕竟UNIX/Linux的发展历程,比indows长很多,

而且很多的技术,windows就是copyUNIX/Linux的技术。

thanks those coder,and love,supply and give me help ..........
for no wife and loverv. so hope some girl see it and Flying with me.......

Any if. SAVE YOU TIME SEE THE SECTION Fire...ALL FIRE.....Here is the thchnical detail...

感谢,那些研究UNIX & Linux的CODER们,和支持爱我的人,从我接受的美帝国主义的PUSSY的文化,首先的感谢妻子,因为没有遇到合适的女人所以没有妻子,自认为男人的性格比低三下四的去获得女人的肉体重要,所以没有情人。想要个二奶子吧,党没有给我机会,这个待遇只能是党的中高层干部的待遇,而且二奶子也是买的,总感觉不爽。


Stand by......

SOCKET主要伴随着网络的兴起,慢慢的发展起来。当时主要是在一些大型的机器,这些机器运行的是UNIX,而编写UNIX的语言是C语言。现在是C++语言,C++语言是C语言的超集。当时的网络拓扑(说白了就是网路上计算机的连接方式)有很多种,比较常见的就是星形(最常见,可以想象现在比较简单的电话,从电话局总机分出很多的线,连接到每个终端电话)。令牌环网。等等,这些联网方式的物理模型仍旧存在于现在的网络。网络通信就得伴随着物理实现和软件实现。这种情况和生活中的实际时间也是一样的,比如, 写信,首先的确定是用纸还是用其他的物理媒体,贺卡,陶瓷杯子,等等,相对于现在网络通信的物理底层,比如用无线,光纤,电话线,然后就得选择语言,中文,英语,日语,这个对应软件,就是通信协议, TCP/UDP/IP,等等,这么选择,主要看什么方便。刚开始的时候,就是普通的电话线,因为当时电话比较普及了,至少在美国是这样。多说点,中国供馋裆一直说改革开放30年,电话从固定电话到走着打,问题这个和你中国供馋裆有啥关系?这些都是美国人发明的,你他妈妈的就是一个使用者,当代的任何改变生活的进步基因,卫星,网络,电视,电话,计算机,汽车,光纤。都他妈妈的和你中国供馋裆没有任何关系。女表子买比,伴随着网络的发展。在更高要求上提出互联要求的想法出现了,推动这个进步就是美国的国防部,花美国纳税人的钱给全世界人民带来了消除贫富差距的一种方式,彻底改变了70、80年代的人。但是因为物理连接。通信协议不同这个时候,出现了一些强者,比较牛逼的公司,他们几乎垄断了技术,这样他们推行,网络的7层协议。实现不同网络的相互连接。最后常见的就是TCP/UDP/IP协议,这些协议也仅仅是实现了7层协议的几层。不是全部。。。。

 


Check you weapon.....................


Socket有同步阻塞方式和异步非阻塞方式两种使用,事实上同步和异步在我们编程的生涯中可能遇到了很多,而Socket也没什么特别。虽然同

步好用,不费劲,但不能满足一些应用场合,其效率也很低。

也许初涉编程的人不能理解"同步(或阻塞)"和"异步(或非阻塞)",其实简单两句话就能讲清楚,同步和异步往往都是针对一个函数来说的,"同

步"就是函数直到其要执行的功能全部完成时才返回,而"异步"则是,函数仅仅做一些简单的工作,然后马上返回,而它所要实现的功能留给别

的线程或者函数去完成。
例如,
SendMessage就是"同步"函数,它不但发送消息到消息队列,还需要等待消息被执行完才返回;相反PostMessage就是个异步函数,它只管发送

一个消息,而不管这个消息是否被处理,就马上返回。

具体到实际生活中,就是电话和短信,电话就是必须同步才能通信。实时的,短信就是异步或者同步。短信你收到了,如果比较忙,可以一会

回。也不耽误事情。如果收到短信就回复,那么就是同步,如果网络出现赌赛的情况,就非常符合异步了。。

 

 

Fire...ALL FIRE....

面向连接(TCP)的套接字的系统调用时序图。

         
             Service                                                       Client

AS: socket()建立流式套接字,返回套接字号SS。                  
               ↓        
               ↓
BS: bind(),套接字SS与本地地址相连。
               ↓
               ↓
CS: listen(),通知TCP服务器准备好接受连接。
               ↓
             C to D
               ↓
DS: accpet(),接受连接等待客户端的连接。                         AC: socket()建立流式套接字号SC。
               ↓                                                          ↓
               ↓                                                          ↓
ES: 建立连接,accpet()返回得到新的套接字,如NS。                 BC: connection(),将套接字S与远地主机连接。
               ↓                                                          ↓
              ES_FS                             <------------------ Connect to ES_FS
               ↓                                                          ↓
FS: recv() & send(),在套接字NS上读写数据直到完成交换。          CC: send() & recv(),在套接字上读写数据直到数据交换完成。
               ↓                                                          ↓
               ↓                                                          ↓
GS: closesocket(), 关闭套接字NS。                               DC: closesocket(),关闭套接字SC,结束TCP对话。
               ↓
         Goto: C to D
               ↓
HS: closesocket(),关闭最初套接字SS,服务结束。


注意的几个技术关键点是:
A: Service需要两个套接字,第一个是用来监听的不能用于数据通信,或者使用close函数关闭侦听后在用于数据通信,本人没有试验。该套接

字绑定的是本地机器的地址。主要告诉客户机用这个地址和端口跟Service通信。
B: 用accpet返回的套接字是用来和Client交换数据的,用于send和recv函数绑定。使用send和recv函数发送和接受数据。
C: Client用一个套接字就可以了。
D: 其中服务器的accpet函数和客户端的connect函数建立直接的联系,这个是我们这层所关心的,通过本人的验证了,在客户端执行到connect

函数的时候,服务器端在accpet函数那里设置的断点有效的断到那位置。
E: 其中执行listen函数的时候,在DOS里边执行netstat -an命令,可以查看本地机器的网络状况,其中设置的本地机器的端口和想建立连接的

地方会返现状态位置,标志为,LISTENING表示正在侦听。

SYN_SENT表示,连接请求,当你要访问恰的计算机的服务时要先发一个同步信号给该端口,此时状态为SYN_SENT.ruguo连接成功就变成了

ESTABLISHED。SYN_SENT不是一个稳定的状态,个人测试(反复用netstat -an命令查看)感觉大概在5s左右(可能和socket设置有关系)。

 


建立无连接的(DUP)的套接字的系统调用时序图。

            Service                                                      Client

AS: socket()建立流式套接字返回套接字号。                     AC: socket()建立流式套接字。返回套接字号(句柄?)。
               ↓                                                          ↓
               ↓                                                          ↓
BS: bind()套接字SS与本地地址相连。                           BC: bind(),将套接字CS与远地主机相连。
               ↓                                                          ↓                                                
               ↓                                                          ↓
CS:      recv() & send()                                     CC:     send() & recv()
    在套接字NS上读 & 写数据,直到完成交换。<----------->         在套接字上读写数据,直到数据交换完成。
               ↓                                                          ↓
               ↓                                                          ↓
DS: closesocket(), 关闭套接字NS。                           DC: closesocket(),关闭套接字CS,结束TCP对话。

 


注意的是send和recv函数用于TCP连接的。用于UDP连接的是

关于send函数的细节。

函数原型int send( SOCKET s, const char FAR *buf, int len, int flags );

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

该函数的

第一个参数指定发送端套接字描述符, acccept()返回的套接字,或者用close()关闭侦听的socket(没测试),构造一个CClass类就没有问题

(没测试),源于MSND提供的代码;

第二个参数指明一个存放应用程序要发送数据的缓冲区;

第三个参数指明实际要发送的数据的字节数,可以使用strlen函数。;

第四个参数一般置0。(可在MSDN上查看其具体含义)

这里只描述同步Socket的send函数的执行流程。
当调用该函数时,
A: send先比较待发送数据的长度len和套接字s的发送缓冲的长度,
B: 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR(在winsock.h中定义为-1);
C: 如果len小于或者等于s的发送缓冲区的长度,
D: 那么send先检查协议 是否正在发送s的发送缓冲中的数据,
E: 如果是就等待协议把数据发送完,
F: 如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,
G: 那么send就比较s的发送缓冲区的剩余空间和len,
H: 如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,
I: 如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而 

 是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。
J: 如果send函数copy数据成功,就返回实际copy的字节数,
K: 如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;
L: 如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间(相当于扔到楼地下的邮筒,至于投递信件就是邮递员的事情了,邮递员就

是网络下一个层次做的事情,比如加TCP头,IP头)里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的

传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套

接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)

注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是

进程终止。

以上来自于网络,而且本人没有机会接触到UNIX & Linux的源代码。所以感觉这个就是UNIX & Linux的通信模型,想必微软那些老头也是研究

UNIX & Linux起家的,所以应用到windows里边也不是不可能的事情。

 

 

recv函数:


函数原型int recv( SOCKET s,    char FAR *buf,     int len,    int flags    );  

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

该函数的第一个参数指定接收端套接字描述符;

第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

第三个参数指明buf的长度;

第四个参数一般置0。(可在MSDN上查看其具体含义)

这里只描述同步Socket的recv函数的执行流程。
当应用程序调用recv函数时,
A: recv先等待s的发送缓冲中的数据被协议传送完毕,
B: 如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,
C: 如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,D: recv先检查套接字s的接收缓冲区,
E: 如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。
F: 当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下 

   要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其 

   实际copy的字节数。
G: 如果recv在copy时出错,那么它返回SOCKET_ERROR;
H: 如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

PS:注意:在Unix等非Windows系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程

对该信号的默认处理是进程终止,这将导致程序退出。

 

 

 

P.S:

 

需要注意的是port在定义是一个unsigned short类型。范围是从0-65536.随便写端口号的时候不要溢出了,本人就多敲一个然后溢出了,当时还纳闷这么端口号不对。。

 

主要参考

 MSDN。网络。VC6.0技术内幕。

 

付 MSDN里边的代码。

#include <stdio.h>
#include "winsock2.h"

void main() {
  //----------------------
  // Initialize Winsock
  WSADATA wsaData;
  int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  if (iResult != NO_ERROR)
    printf("Error at WSAStartup()/n");

  //----------------------
  // Create a SOCKET for connecting to server
  SOCKET ConnectSocket;
  ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (ConnectSocket == INVALID_SOCKET) {
    printf("Error at socket(): %ld/n", WSAGetLastError());
    WSACleanup();
    return;
  }

  //----------------------
  // The sockaddr_in structure specifies the address family,
  // IP address, and port of the server to be connected to.
  sockaddr_in clientService;
  clientService.sin_family = AF_INET;
  clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  clientService.sin_port = htons( 27015 );

  //----------------------
  // Connect to server.
  if ( connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) {
    printf( "Failed to connect./n" );
    WSACleanup();
    return;
  }

  //----------------------
  // Declare and initialize variables.
  int bytesSent;
  int bytesRecv = SOCKET_ERROR;
  char sendbuf[32] = "Client: Sending data.";
  char recvbuf[32] = "";

  //----------------------
  // Send and receive data.
  bytesSent = send( ConnectSocket, sendbuf, strlen(sendbuf), 0 );
  printf( "Bytes Sent: %ld/n", bytesSent );

  while( bytesRecv == SOCKET_ERROR ) {
    bytesRecv = recv( ConnectSocket, recvbuf, 32, 0 );
    if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) {
      printf( "Connection Closed./n");
      break;
    }
    printf( "Bytes Recv: %ld/n", bytesRecv );
  }

  WSACleanup();
  return;
}

 

************************************安全线***********************************

 

Complete Server Code

The following is the complete source code for the TCP/IP Server application.

Server Source Code

 

#include <stdio.h>
#include "winsock2.h"
void main() {
// Initialize Winsock.
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
printf("Error at WSAStartup()/n");
// Create a socket.
SOCKET m_socket;
m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( m_socket == INVALID_SOCKET ) {
printf( "Error at socket(): %ld/n", WSAGetLastError() );
WSACleanup();
return;
}
// Bind the 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( m_socket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
printf( "bind() failed./n" );
closesocket(m_socket);
return;
}
// Listen on the socket.
if ( listen( m_socket, 1 ) == SOCKET_ERROR )
printf( "Error listening on socket./n");
// Accept connections.
SOCKET AcceptSocket;
printf( "Waiting for a client to connect.../n" );
while (1) {
AcceptSocket = SOCKET_ERROR;
while ( AcceptSocket == SOCKET_ERROR ) {
AcceptSocket = accept( m_socket, NULL, NULL );
}
printf( "Client Connected./n");
m_socket = AcceptSocket; 
break;
}
// Send and receive data.
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[32] = "Server: Sending Data.";
char recvbuf[32] = "";
bytesRecv = recv( m_socket, recvbuf, 32, 0 );
printf( "Bytes Recv: %ld/n", bytesRecv );
bytesSent = send( m_socket, sendbuf, strlen(sendbuf), 0 );
printf( "Bytes Sent: %ld/n", bytesSent );
return;
}
 
***********************************安全线********************************************
 

Complete Server Code

The following is the complete source code for the TCP/IP Server application.

Server Source Code

 

#include <stdio.h>
#include "winsock2.h"
void main() {
// Initialize Winsock.
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
printf("Error at WSAStartup()/n");
// Create a socket.
SOCKET m_socket;
m_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( m_socket == INVALID_SOCKET ) {
printf( "Error at socket(): %ld/n", WSAGetLastError() );
WSACleanup();
return;
}
// Bind the 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( m_socket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
printf( "bind() failed./n" );
closesocket(m_socket);
return;
}
// Listen on the socket.
if ( listen( m_socket, 1 ) == SOCKET_ERROR )
printf( "Error listening on socket./n");
// Accept connections.
SOCKET AcceptSocket;
printf( "Waiting for a client to connect.../n" );
while (1) {
AcceptSocket = SOCKET_ERROR;
while ( AcceptSocket == SOCKET_ERROR ) {
AcceptSocket = accept( m_socket, NULL, NULL );
}
printf( "Client Connected./n");
m_socket = AcceptSocket; 
break;
}
// Send and receive data.
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[32] = "Server: Sending Data.";
char recvbuf[32] = "";
bytesRecv = recv( m_socket, recvbuf, 32, 0 );
printf( "Bytes Recv: %ld/n", bytesRecv );
bytesSent = send( m_socket, sendbuf, strlen(sendbuf), 0 );
printf( "Bytes Sent: %ld/n", bytesSent );
return;
}
NOTE:
A: winsock版本号变成0202. 即更改0101为0202. 
B: 在服务器端的程序不能直接拷贝使用,至少在我用的WIN32控制台是这样。因为send和recv函数绑定的套接字是accpet返回的函数。
至于在MFC里边为什么能用,现在还没有时间去想,等有时间想了,验证通过了在写出来。。。
C: 在连接的时候还有一个问题,就是找不到一些函数,解决的办法是
    C-1:  在头文件处添加#pragma comment( lib, "ws2_32")或者
 C-2 : Alt+F7 / Propery Pages / Configuration Properies / Linker /General / Addinional Library Directions 添加2_32.dll。
 
个人的平台: IBM-T40, XP-sp3, VS2005_C++.
 
Marrying Christmas

   
  相关解决方案