linux socket编程学习

来源:互联网 发布:python爬虫多线程pool 编辑:程序博客网 时间:2024/05/22 04:48

socket()

  简述:

  创建一个套接口。

  #include <winsock.h>

  SOCKET PASCAL FAR socket( int af, int type, int protocol);

  af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。

  type:新套接口的类型描述。

  protocol:套接口所用的协议。如调用者不想指定,可用0

  注释:

  socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。

  对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的通讯域。支持下述类型描述:

  类型 解释

  SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP

  SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP

  SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()recv()来接收。

  实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间,且数据不能在一定时间成功发送,则认为连接中断,其后续的调用也将以WSAETIMEOUT错误返回。

  SOCK_DGRAM类型套接口允许使用sendto()recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()recv()与该端口进行数据报的发送与接收。

  返回值:

  若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVAID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEAFNOSUPPORT:不支持指定的地址族。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEMFILE:无可用文件描述字。

  WSAENOBUFS:无可用缓冲区,无法创建套接口。

  WSAEPROTONOSUPPORT:不支持指定的协议。

  WSAEPROTOTYPE:指定的协议不适用于本套接口。

WSAESOCKTNOSUPPORT:本地址族中不支持该类型套接口。

 

bind()

  简述:

  将一本地地址与一套接口捆绑。

  #include <winsock.h>

  int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR* name,

  int namelen);

  s:标识一未捆绑套接口的描述字。

  name:赋予套接口的地址。sockaddr结构定义如下:

  struct sockaddr{

  u_short sa_family;

  char sa_data[14];

  };

  namelenname名字的长度。

  注释:

  本函数适用于未连接的数据报或流类套接口,在connect()listen()调用前使用。当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。

  在Internet地址族中,一个名字包括几个组成部分,对于SOCK_PROGRAMSOCK_STREAM类套接口,名字由三部分组成:主机地址,协议号(显式设置为UDPTCP)和用以区分应用的端口号。如果一个应用并不关心分配给它的地址,则可将Internet地址设置为INADDR_ANY,或将端口号置为0。如果Internet地址段为INADDR_ANY,则可使用任意网络接口;在有多种主机环境下可简化编程。如果端口号置为0,则WINDOWS套接口实现将给应用程序分配一个值在10245000之间的唯一的端口。应用程序可在bind()后用getsockname()来获知所分配的地址,但必需注意的是,getsockname()只有在套接口连接成功后才会填写Internet地址,这是由于在多种主机环境下若干种Internet地址都是有效的。

  如果一个应用程序需要把端口捆绑到超过10245000范围的特定端口时,比如rsh需要捆绑到任一保留端口,则可如下编程:

  SOCKADDR_IN sin;

  SOCKET s;

  u_short alport=IPPORT_RESERVED;

  sin.sin_family=AF_INET;

  sin.sin_addr.s_addr=0;

  for (;;) {

  sin.sin_port=htons(alport);

  if (bind(s,(LPSOCKADDR)&sin, sizeof(sin))=0) {

  /* it worked */

  }

  if (GetLastError()!=WSAEADDRINUSE) {

  /* fail */

  }

  alport-;

  if (alport=IPPORT_RESERVED/2) {

  /* failall unassigned reserved ports are */

  /* in use. */

  }

  }

  返回值:

  如无错误发生,则bind()返回0。否则的话,将返回SOCKET_ERROR,应用程序可通过WSAGetLastError()获取相应错误代码。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEADDRINUSE:所定端口已在使用中(参见setoption()中的SO_REUSEADDR选项)。

  WSAEFAULTnamelen参数太小(小于sockaddr结构的大小)。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEAFNOSUPPORT:本协议不支持所指定的地址族。

  WSAEINVAL:该套接口已与一个地址捆绑。

  WSAENOBUFS:无足够可用缓冲区,连接过多。

  WSAENOTSOCK:描述字不是一个套接口。

 

listen()

  简述:

  创建一个套接口并监听申请的连接.

  #include <winsock.h>

  int PASCAL FAR listen( SOCKET s, int backlog);

  S:用于标识一个已捆绑未连接套接口的描述字。

  backlog:等待连接队列的最大长度。

  注释:

  为了接受连接,先用socket()创建一个套接口,然后用listen()为申请进入的连接建立一个后备日志,然后便可用accept()接受连接了。listen()仅适用于支持连接的套接口,如SOCK_STREAM类型的。套接口s处于一种变动模式,申请进入的连接请求被确认,并排队等待被接受。这个函数特别适用于同时有多个连接请求的服务器;如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。

  当没有可用的描述字时,listen()函数仍试图正常地工作。它仍接受请求直至队列变空。当有可用描述字时,后续的一次listen()accept()调用会将队列按照当前或最近的后备日志重新填充,如有可能的话,将恢复监听申请进入的连接请求。

  兼容性:

  后备日志当前被(默认地)限制为5。如同4.3 BSD Unix中的一样,小于1或大于5的数都会被舍入最近的有效值。

  返回值:

  如无错误发生,listen()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEADDRINUSE:试图用listen()去监听一个正在使用中的地址。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEINVAL:该套接口未用bind()进行捆绑,或已被连接。

  WSAEISCONN:套接口已被连接。

  WSAEMFILE:无可用文件描述字。

  WSAENOBUFS:无可用缓冲区空间。

  WSAENOTSOCK:描述字不是一个套接口。

WSAEOPNOTSUPP:该套接口不正常listen()调用。

 

accept()

  简述:

  在一个套接口接受一个连接。

  #include <winsock.h>

  SOCKET PASCAL FAR accept( SOCKET s, struct sockaddr FAR* addr,

  int FAR* addrlen);

  s:套接口描述字,该套接口在listen()后监听连接。

  addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。

  addrlen:(可选)指针,指向存有addr地址长度的整形数。

  注释:

  本函数s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。如果队列中无等待连接,且套接口为阻塞方式,则accept()阻塞调用进程直至新的连接出现。如果套接口为非阻塞方式且队列中无等待连接,则accept()返回一错误代码。已接受连接的套接口不能用于接受新的连接,原套接口仍保持开放。

  addr参数为一个返回参数,其中填写的是为通讯层所知的连接实体地址。addr参数的实际格式由通讯时产生的地址族确定。addrlen参数也是一个返回参数,在调用时初始化为addr所指的地址空间;在调用结束时它包含了实际返回的地址的长度(用字节数表示)。该函数与SOCK_STREAM类型的面向连接的套接口一起使用。如果addraddrlen中有一个为零NULL,将不返回所接受的套接口远程地址的任何信息。

  返回值:

  如果没有错误产生,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,应用程序可通过调用WSAGetLastError()来获得特定的错误代码。

  addrlen所指的整形数初始时包含addr所指地址空间的大小,在返回时它包含实际返回地址的字节长度。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEFAULTaddrlen参数太小(小于socket结构的大小)。

  WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEINVAL:在accept()前未激活listen()

  WSAEMFILE:调用accept()时队列为空,无可用的描述字。

  WSAENOBUFS:无可用缓冲区空间。

  WSAENOTSOCK:描述字不是一个套接口。

  WSAEOPNOTSUPP:该套接口类型不支持面向连接服务。

WSAEWOULDBLOCK:该套接口为非阻塞方式且无连接可供接受。

 

connect()

  简述:

  建立与一个端的连接。

  #include <winsock.h>

  int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR* name,

  int namelen);

  s:标识一个未连接套接口的描述字。

  name:欲进行连接的端口名。

  namelen:名字长度。

  注释:

  本函数用于创建与指定外部端口的连接。s参数指定一个未连接的数据报或流类套接口。如套接口未被捆绑,则系统赋给本地关联一个唯一的值,且设置套接口为已捆绑。请注意若名字结构中的地址域为全零的话,则connect()将返回WSAEADDRNOTAVAIL错误。

  对于流类套接口(SOCK_STREAM类型),利用名字来与一个远程主机建立连接,一旦套接口调用成功返回,它就能收发数据了。对于数据报类套接口(SOCK_DGRAM类型),则设置成一个缺省的目的地址,并用它来进行后续的send()recv()调用。

  返回值:

  若无错误发生,则connect()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。对阻塞套接口而言,若返回值为SOCKET_ERROR则应用程序调用WSAGetLsatError()。如果它指出错误代码为WSAEWOULDBLOCK,则您的应用程序可以:

  1.select(),通过检查套接口是否可写,来确定连接请求是否完成。或者,

  2.如果您的应用程序使用基于消息的WSAAsynSelect()来表示对连接事件的兴趣,则当连接操作完成后,您会收到一个FD_CONNECT消息。

  错误代码:

  WSAENOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEADDRINUSE:所指的地址已在使用中。

  WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEADDRNOTAVAIL:在本地机器上找不到所指的地址。

  WSAENOTSUPPORT:所指族中地址无法与本套接口一起使用。

  WSAECONNREFUSED:连接尝试被强制拒绝。

  WSAEDESTADDREQ:需要目的地址。

  WSAEFAULTnamelen参数不正确。

  WSAEINVAL:套接口没有准备好与一地址捆绑。

  WSAEISCONN:套接口早已连接。

  WSAEMFILE:无多余文件描述字。

  WSAENETUNREACH:当前无法从本主机访问网络。

  WSAENOBUFS:无可用缓冲区。套接口未被连接。

  WSAENOTSOCK:描述字不是一个套接口。

  WSAETIMEOUT:超时时间到。

  WSAEWOULDBLOCK:套接口设置为非阻塞方式且连接不能立即建立。可用select()调用对套接口写,因为select()时会进行连接。

 

recv()

  简述:

  从一个套接口接收数据。

  #include <winsock.h>

  int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags);

  s:一个标识已连接套接口的描述字。

  buf:用于接收数据的缓冲区。

  len:缓冲区长度。

  flags:指定调用方式。

  注释:

  本函数用于已连接的数据报或流式套接口s进行数据的接收。

  对SOCK_STREAM类型的套接口来说,本函数将返回所有可用的信息,最大可达缓冲区的大小。如果套接口被设置为线内接收带外数据(选项为SO_OOBINLINE),且有带外数据未读入,则返回带外数据。应用程序可通过调用ioctlsocket()SOCATMARK命令来确定是否有带外数据待读入。

  对于数据报类套接口,队列中第一个数据报中的数据被解包,但最多不超过缓冲区的大小。如果数据报大于缓冲区,那么缓冲区中只有数据报的前面部分,其他的数据都丢失了,并且recv()函数返回WSAEMSGSIZE错误。如果没有数据待读,那么除非是非阻塞模式,不然的话套接口将一直等待数据的到来,此时将返回SOCKET_ERROR错误,错误代码是WSAEWOULDBLOCK。用select()WSAAsynSelect()可以获知何时数据到达。

  如果套接口为SOCK_STREAM类型,并且远端优雅地中止了连接,那么recv()一个数据也不读取,立即返回。如果立即被强制中止,那么recv()将以WSAECONNRESET错误失败返回。在套接口的所设选项之上,还可用标志位flag来影响函数的执行方式。也就是说,本函数的语义既取决于套接口选项,也取决于标志位参数。标志位可取下列值:

  值 意义

  MSG_PEEK查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除。

  MSG_OOB 处理带外数据(参见2.2.3节具体讨论)。

  返回值:

  若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAENOTCONN:套接口未连接。

  WSAEINTR:阻塞进程被WSACancelBlockingCall()取消。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAENOTSOCK:描述字不是一个套接口。

  WSAEOPNOTSUPP:指定了MSG_OOB,但套接口不是SOCK_STREAM类型的。

  WSAESHUTDOWN:套接口已被关闭。当一个套接口以02how参数调用shutdown()关闭后,无法再用recv()接收数据。

  WSAEWOULDBLOCK:套接口标识为非阻塞模式,但接收操作会产生阻塞。

  WSAEMSGSIZE:数据报太大无法全部装入缓冲区,故被剪切。

  WSAEINVAL:套接口未用bind()进行捆绑。

  WSAECONNABORTED:由于超时或其他原因,虚电路失效。

  WSAECONNRESET:远端强制中止了虚电路。

 

send()

  简述:

  向一个已连接的套接口发送数据。

  #include <winsock.h>

  int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);

  s:一个用于标识已连接套接口的描述字。

  buf:包含待发送数据的缓冲区。

  len:缓冲区中数据的长度。

  flags:调用执行方式。

  注释:

  send()适用于已连接的数据报或流式套接口发送数据。对于数据报类套接口,必需注意发送数据长度不应超过通讯子网的IP包最大长度。IP包最大长度在WSAStartup()调用返回的WSADataiMaxUdpDg元素中。如果数据太长无法自动通过下层协议,则返回WSAEMSGSIZE错误,数据不会被发送。

  请注意成功地完成send()调用并不意味着数据传送到达。

  如果传送系统的缓冲区空间不够保存需传送的数据,除非套接口处于非阻塞I/O方式,否则send()将阻塞。对于非阻塞SOCK_STREAM类型的套接口,实际写的数据数目可能在1到所需大小之间,其值取决于本地和远端主机的缓冲区大小。可用select()调用来确定何时能够进一步发送数据。

  在相关套接口的选项之上,还可通过标志位flag来影响函数的执行方式。也就是说,本函数的语义既取决于套接口的选项也取决于标志位。后者由以下一些值组成:

  值 意义

  MSG_DONTROUTE 指明数据不选径。一个WINDOWS套接口供应商可以忽略此标志;MSG_OOB 发送带外数据(仅适用于SO_STREAM;)。

  返回值:

  若无错误发生,send()返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小)。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

  错误代码:

  WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()

  WSAENETDOWNWINDOWS套接口实现检测到网络子系统失效。

  WSAEACESS:要求地址为广播地址,但相关标志未能正确设置。

  WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。

  WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

  WSAEFAULTbuf参数不在用户地址空间中的有效位置。

  WSAENETRESET:由于WINDOWS套接口实现放弃了连接,故该连接必需被复位。

  WSAENOBUFSWINDOWS套接口实现报告一个缓冲区死锁。

  WSAENOTCONN:套接口未被连接。

  WSAENOTSOCK:描述字不是一个套接口。

  WSAEOPNOTSUPP:已设置了MSG_OOB,但套接口非SOCK_STREAM类型。

  WSAESHUTDOWN:套接口已被关闭。一个套接口以12how参数调用shutdown()关闭后,无法再用sned()函数。

  WSAEWOULDBLOCK

  WSAEMSGSIZE:套接口为SOCK_DGRAM类型,且数据报大于WINDOWS套接口实现所支持的最大值。

  WSAEINVAL:套接口未用bind()捆绑。

  WSAECONNABORTED:由于超时或其他原因引起虚电路的中断。

WSAECONNRESET:虚电路被远端复位。

 

fcntl

  表头文件 #include <fcntl.h>

  #include <fcntl.h>

  定义函数 int fcntl(int fd , int cmd);

  int fcntl(int fd,int cmd,long arg);

  int fcntl(int fd,int cmd,struct flock * lock);

  fcntl()用来操作文件描述词的一些特性。参数fd代表欲设置的文件描述词,参数cmd代表欲操作的指令。

  有以下几种情况:

  F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。执行成功则返回新复制的文件描述词。请参考dup2()F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。

  F_SETFD 设置close-on-exec 旗标。该旗标以参数arg FD_CLOEXEC位决定。

  F_GETFL 取得文件描述词状态旗标,此旗标为open()的参数flags

  F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPENDO_NONBLOCKO_ASYNC位的改变,其他位的改变将不受影响。

  F_GETLK 取得文件锁定的状态。

  F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCKF_WRLCKF_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES EAGAIN

  F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。参数lock指针为flock 结构指针,定义如下

  struct flcok

  {

  short int l_type;

  short int l_whence;

  off_t l_start;

  off_t l_len;

  pid_t l_pid;

  };

  l_type 有三种状态:

  F_RDLCK 建立一个供读取用的锁定

  F_WRLCK 建立一个供写入用的锁定

  F_UNLCK 删除之前建立的锁定

  l_whence 也有三种方式:

  SEEK_SET 以文件开头为锁定的起始位置。

  SEEK_CUR 以目前文件读写位置为锁定的起始位置

  SEEK_END 以文件结尾为锁定的起始位置。

返回值 成功则返回0,若有错误则返回-1,错误原因存于errno.

命令字(cmd)F_GETFLF_SETFL的标志如下面的描述:
            O_NONBLOCK            
非阻塞I/O;如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,readwrite调用返回-1EAGAIN错误
            O_APPEND                    
强制每次写(write)操作都添加在文件大的末尾,相当于open(2)O_APPEND标志
            O_DIRECT                    
最小化或去掉readingwriting的缓存影响.系统将企图避免缓存你的读或写的数据.如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响.如果这个标志用的不够好,将大大的降低性能
            O_ASYNC                    
I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFDF_SETFL命令,这样会关闭以前设置的标志位。

fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新
的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID

 

:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <unistd.h>

#include <fcntl.h>

 

 

#define MYPORT 4567                 //开启端口

 

#define MAX_LEN_BUFF 2048   //数据 缓冲区 一条缓冲长度

#define MAX_NUM_BUFF 2048 //数据 缓冲区 缓冲数目

 

#define MAX_NUM_CONNECT 50 //socket 允许连入的最大连接数

//数据缓冲区

char databuff[MAX_NUM_BUFF][MAX_LEN_BUFF];    //消息 缓冲

int datalen[MAX_NUM_BUFF];                           //消息 长度

 

int buff_head;           //缓冲区 头指针

int buff_tail;        //缓冲区 尾指针

int data_num;      //缓冲区 消息数量

 

int main()

{

 

      int    sockfd_s ,sockfd_c;

      struct sockaddr_in server_add ,client_add;

      int    len;

      size_t clen;

     

      //消息 缓冲区 初始化

      buff_head = buff_tail = data_num = 0;

      //数据初始化

      sockfd_s = sockfd_c = 0;

      len = sizeof( struct sockaddr_in );

      clen = sizeof( struct sockaddr_in );

      memset( &server_add ,0x00 ,len );

      memset( &client_add ,0x00 ,len );

 

 

      //创建 socket ID

      sockfd_s = socket( AF_INET ,SOCK_STREAM ,0 );

      //设置 该连接为 非阻塞模式

      fcntl( sockfd_s ,F_SETFL ,O_NONBLOCK );

 

      if( sockfd_s == -1 )

      {

           perror( "error in create socket!" );

           return 1;

      }

      printf( "sockfd_s=%d\n" ,sockfd_s );

 

      //socket 信息结构体赋值

      server_add.sin_family = AF_INET;

      server_add.sin_port = htons( MYPORT );

      server_add.sin_addr.s_addr = INADDR_ANY;

 

      //绑定socket

      if( bind( sockfd_s ,( struct sockaddr * )&server_add ,len ) == -1 )

      {

           perror( "error in socket bind!" );

           return 1;

      }

      printf( "socket bind succesfull\n" );

 

      //监听socket

      if( listen( sockfd_s ,MAX_NUM_CONNECT ) == -1 )

      {

           perror( "error in socket listen!" );

           return 1;

      }

      printf( "socket listen succesfull\n" );

 

      //消息读取

      while( 1 )

      {

           //printf( "into accpet!\n" );

           //应答 客户端连接请求

           if( ( sockfd_c = accept( sockfd_s ,( struct sockaddr * )&client_add ,&clen ) ) == -1 )

           {

                 //perror( "error in accep!" );

                 usleep( 1 );

                 continue;

           }

           printf( "socket accpet succesfull sockfd_c=%d\n" ,sockfd_c );

           //发送连接成功消息

           send( sockfd_c ,"连接成功!" ,9 ,0 );

           //设置 当前连接为 非阻塞模式

           fcntl( sockfd_c ,F_SETFL ,O_NONBLOCK );

 

           //接受 客户端消息

           while( 1 )

           {

                 //接受用户发送的数据

                 datalen[buff_head] = recv( sockfd_c ,databuff[buff_head] ,MAX_LEN_BUFF ,0 );

                 //接受数据长度>0

                 if( datalen[buff_head] > 0 )

                 {

                      //在消息结尾 加上结束符 方便字符串打印

                      databuff[buff_head][datalen[buff_head]] = 0;

                      printf( "收到消息:%s\n" ,databuff[buff_head] );

                      //接受到 首字母为q的消息 程序退出

                      if( databuff[buff_head][0] == 'q' )

                      {

                            //关闭socket

                            close( sockfd_c );

                            close( sockfd_s );

                            return 0;

                      }

                      //消息缓冲区指针 循环下移

                      ++buff_head;

                      buff_head = buff_head & 0x7FF;

                 }

                 else if( datalen[buff_head] == 0 ) //socket断开 跳出消息接受循环

                 {

                      close( sockfd_c );

                      break;

                 }

           }

      }

      return 0;

}

 

原创粉丝点击