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)。
2 多线程解决TCP并发问题
2.1 源码示例
# 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 ; struct sockaddr_in seraddr; int ret= 0 ; ssize_t nsize= 0 ; char sendbuffer[ 4096 ] = { "|c-send|" } ; char recvbuffer[ 4096 ] = { "|c-recv|" } ; int cnt= 100 ; 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 ) { memset ( sendbuffer, 0 , sizeof ( sendbuffer) ) ; sprintf ( sendbuffer, "< %d >" , -- cnt) ; nsize= send ( sockfd, sendbuffer, sizeof ( sendbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return - 1 ; } printf ( "client send: %s\n" , sendbuffer) ; sleep ( 1 ) ; memset ( recvbuffer, 0 , sizeof ( recvbuffer) ) ; nsize= recv ( sockfd, recvbuffer, sizeof ( recvbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return - 1 ; } printf ( "client recv: %s\n" , recvbuffer) ; sleep ( 1 ) ; } return 0 ;
}
# 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> # include <pthread.h> void * handle_tcp_client ( void * arg) ; int main ( void )
{ pthread_t tid= 0 ; pthread_attr_t attr; int sockfd= 0 ; struct sockaddr_in seraddr; int ret= 0 ; int confd= 0 ; pthread_attr_init ( & attr) ; pthread_attr_setdetachstate ( & attr, PTHREAD_CREATE_DETACHED) ; sockfd= socket ( AF_INET, SOCK_STREAM, 0 ) ; if ( - 1 == sockfd) { perror ( "fail to sockfd" ) ; 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 pthread_create ( & tid, & attr, handle_tcp_client, & confd) ;
# endif
# if 1 pthread_create ( & tid, & attr, handle_tcp_client, confd) ;
# endif } close ( sockfd) ; return 0 ;
} void * handle_tcp_client ( void * arg)
{ ssize_t nsize= 0 ; char sendbuffer[ 4096 ] = { "|s-send|" } ; char recvbuffer[ 4096 ] = { "|s-recv|" } ;
# if 0 int confd= * ( int * ) arg;
# endif
# if 1 int confd= arg;
# endif int cnt= 0 ; while ( 1 ) { memset ( recvbuffer, 0 , sizeof ( recvbuffer) ) ; nsize= recv ( confd, recvbuffer, sizeof ( recvbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return NULL ; } else if ( 0 == nsize) { return NULL ; } printf ( "server recv: %s\n" , recvbuffer) ; sleep ( 1 ) ; memset ( sendbuffer, 0 , sizeof ( sendbuffer) ) ; sprintf ( sendbuffer, "< %d >" , ++ cnt) ; nsize= send ( confd, sendbuffer, strlen ( sendbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return NULL ; } printf ( "server send: %s\n" , sendbuffer) ; sleep ( 1 ) ; } return NULL ;
}
2.2 运行结果
2.3 分析总结
服务器端:两次接收:两个客户端与服务器端建立连接,接受两个客户端分别发送过来的数据;两次发送:服务器端与两个客户端建立连接,发送数据时向两个客户端分别发送一次数据。客户端:一次发送:向服务器端发送一次数据。一次接收:接受服务器端发送过来的数据。问题:(1)如果连入多个客户端,那么就创建了多个通信套接字(confd);服务器端创建多线程的时候,为回调函数传参问题:1)回调函数传参传【&confd】:那么后建立的服务器端与客户端的连接产生的文件描述符(confd)就会将先建立的服务器端与客户端的连接产生的文件描述符(confd)覆盖;这将会导致不同的客户端的不同数据请求得到服务器端的数据响应是一样的(即只有一个客户端可以收到自己想要的数据,而其它的客户端收到的将不是自己请求的数据)。2)回调函数传参传【confd】:就可以解决先建立连接产生的文件描述符被覆盖的问题。(同时编译过程中产生2个警告!)(2)运行效果:
3 IO多路复用解决TCP并发问题
3.1 select
3.1.1 源码示例
3.1.1 客户端
# 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 ; struct sockaddr_in seraddr; int ret= 0 ; ssize_t nsize= 0 ; char sendbuffer[ 4096 ] = { 0 } ; char recvbuffer[ 4096 ] = { 0 } ; int send_cnt= 1 ;
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 ) { memset ( sendbuffer, 0 , sizeof ( sendbuffer) ) ; sprintf ( sendbuffer, "|%-3d|" , send_cnt++ ) ; nsize= send ( sockfd, sendbuffer, sizeof ( sendbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return - 1 ; } printf ( "client send: %s\n" , sendbuffer) ; memset ( recvbuffer, 0 , sizeof ( recvbuffer) ) ; nsize= recv ( sockfd, recvbuffer, sizeof ( recvbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return - 1 ; } printf ( "client recv: %s\n" , recvbuffer) ; sleep ( 3 ) ; } close ( sockfd) ; return 0 ;
}
3.1.2 服务器端
# 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 ; struct sockaddr_in seraddr; int ret= 0 ; int confd= 0 ; int maxfd= 0 ; ssize_t nsize= 0 ; char sendbuffer[ 4096 ] = { 0 } ; char recvbuffer[ 4096 ] = { 0 } ; int send_cnt= 99 ; fd_set rdfds; fd_set tmpfds; 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 ; } FD_ZERO ( & rdfds) ; FD_SET ( sockfd, & rdfds) ; while ( 1 ) { tmpfds= rdfds; ret= select ( sockfd+ 1 , & tmpfds, NULL , NULL , NULL ) ; if ( - 1 == ret) { perror ( "fail to select" ) ; return - 1 ; } if ( FD_ISSET ( sockfd, & tmpfds) ) { confd= accept ( sockfd, NULL , NULL ) ; if ( - 1 == confd) { perror ( "fail to accept" ) ; FD_CLR ( sockfd, & rdfds) ; close ( sockfd) ; continue ; } FD_SET ( confd, & rdfds) ;
} if ( FD_ISSET ( sockfd, & tmpfds) ) { memset ( recvbuffer, 0 , sizeof ( recvbuffer) ) ; nsize= recv ( confd, recvbuffer, sizeof ( recvbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to recv" ) ; return - 1 ; } printf ( "server recv: %s\n" , recvbuffer) ; memset ( sendbuffer, 0 , sizeof ( sendbuffer) ) ; sprintf ( sendbuffer, "|%-3d|" , send_cnt-- ) ; nsize= send ( confd, sendbuffer, sizeof ( sendbuffer) , 0 ) ; if ( - 1 == nsize) { perror ( "fail to send" ) ; return - 1 ; } printf ( "server send: %s\n" , sendbuffer) ; } sleep ( 3 ) ; } close ( confd) ; close ( sockfd) ;
}
3.1.3 服务器端改进
3.1.2 运行结果
3.1.3 分析总结
3.2 poll
3.2.1 源码示例
3.2.2 运行结果
3.2.3 分析总结
3.3 epoll
3.3.1 源码示例
3.3.2 运行结果
3.3.3 分析总结