1
1.1代码示例
# include <stdio.h>
# include <stdio.h> # include <sys/types.h>
# include <sys/socket.h> # include <netinet/in.h>
# include <netinet/ip.h>
# include <arpa/inet.h> # include <sys/stat.h>
# include <fcntl.h> # include <unistd.h> # include <string.h> int main ( void )
{ int sockfd= 0 ; int ret= 0 ; struct sockaddr_in seraddr; char tmpbuff[ 4096 ] = { "I need some offers" } ; int cnt= 0 ; ssize_t nsize= 0 ; sockfd= socket ( AF_INET, SOCK_STREAM, 0 ) ; if ( - 1 == sockfd) { perror ( "fail to socket" ) ; return - 1 ; } seraddr. sin_family= AF_INET; seraddr. sin_port= htons ( 50000 ) ; seraddr. sin_addr. s_addr= inet_addr ( "192.168.1.123" ) ; ret= connect ( sockfd, ( struct sockaddr * ) & seraddr, sizeof ( seraddr) ) ; if ( - 1 == ret) { perror ( "fail to connect" ) ; return - 1 ; } while ( 1 ) {
# if 0 memset ( tmpbuff, 0 , sizeof ( tmpbuff) ) ; sprintf ( tmpbuff, "client send:### %d" , cnt) ; cnt++ ; nsize= send ( sockfd, tmpbuff, strlen ( tmpbuff) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return - 1 ; }
# endif # if 1 memset ( tmpbuff, 0 , sizeof ( tmpbuff) ) ; nsize= recv ( sockfd, tmpbuff, sizeof ( tmpbuff) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return - 1 ; }
# endif printf ( "client recv:### %s\n" , tmpbuff) ; } close ( sockfd) ; return 0 ;
} int main ( void )
{ int sockfd= 0 ; struct sockaddr_in seraddr; int ret= 0 ; int confd= 0 ; char tmpbuff[ 4096 ] = { 0 } ; ssize_t nsize= 0 ; sockfd= socket ( AF_INET, SOCK_STREAM, 0 ) ; if ( - 1 == sockfd) { perror ( "fail to socket" ) ; return - 1 ; } seraddr. sin_family= AF_INET; seraddr. sin_port= htons ( 50000 ) ; seraddr. sin_addr. s_addr= inet_addr ( "192.168.1.123" ) ; ret= bind ( sockfd, ( struct sockaddr * ) & seraddr, sizeof ( seraddr) ) ; if ( - 1 == ret) { perror ( "fail to bind" ) ; return - 1 ; } ret= listen ( sockfd, 10 ) ; if ( - 1 == ret) { perror ( "fail to listen" ) ; return - 1 ; } while ( 1 ) { confd= accept ( sockfd, NULL , NULL ) ; if ( - 1 == confd) { perror ( "fail to accept" ) ; return - 1 ; } # if 0 memset ( tmpbuff, 0 , sizeof ( tmpbuff) ) ; nsize= recv ( confd, tmpbuff, sizeof ( tmpbuff) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return - 1 ; } else if ( 0 == nsize) { return 0 ; }
# endif memset ( tmpbuff, 0 , sizeof ( tmpbuff) ) ; sprintf ( tmpbuff, "server send:### %s" , tmpbuff) ; nsize= send ( confd, tmpbuff, strlen ( tmpbuff) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return - 1 ; } } close ( confd) ; close ( sockfd) ; return 0 ;
}
1.2运行结果
(1)运行服务器
(2)运行客户端现象如下:客户端发生阻塞
(1)关闭服务器:客户端不阻塞-说明recv函数为非阻塞状态
总结:
(1)如果将服务器accept函数放在while循环内部:同时运行客户端与服务器,可以发现服务器可以与多个客户端建立连接(解决的问题-服务器并发访问)。
(2)但是只能进行一次数据发送(虽然解决了服务器的并发访问问题,但是对于大量的数据传输却手足无措)小意外:当服务器端退出,此时客户端不再阻塞,正常执行函数体。(这就说明了,此时recv函数为非阻塞状态=》那么服务器和客户端同时运行,为什么会发生阻塞?)补充:
(1)如果将服务器端accept函数放在while循环外部(对比TCP文件传输):1)只有一个客户端能够与服务器建立连接并进行数据收发;其它客户端被阻塞,无法与服务器建立连接。(问题:(1)服务器只能与一个客户端建立连接?(2)那么如何解决这个问题?(3)是从服务端解决这个问题,还是从客户端解决这个问题? 先解决问题(3):显然,要解决这个问题肯定是要从服务器端去解决--客户端是向服务器端发起连接请求,那么具体是否能够建立连接,显然是取决于服务器端的意愿和能力;所以,多个客户端向服务器端发起连接请求,能不能实现的关键在于服务器能不能接受多个客户端接入,服务器端是否允许多个客户端接入。 下面再解决问题(2):解决这个问题,其实用一句话来表述就是解决服务器并发访问问题,这个问题后面专项解决,现在只需要明白所面临的问题是服务器并发访问的问题即可。)(2)如果将connect函数放在循环外部,将accept函数放在循环外部,此时意味着:客户端与服务器端第一次建立连接,可以发送一次数据;发完数据接着进行第二次,第三次。。。连接请求;此时当然是报错!因为同一个网络套接字只能建立一次连接,不能重复建立连接(这是由TCP通信机制决定的);但是这种方式可以将服务器同时与多个客户端建立连接。。。。。。。。。。。。。。总结一下就是:客户端与服务器端要进行多次请求连接-建立连接;那么每进行一次通信,都必须关闭(close)通信套接字(已经建立三次握手,四次挥手连接的套接字confd-accept)。
问题:(1)在客户端或者服务器的