server client 于青林

本文介绍了一个基于TCP的多客户端群聊服务器的设计与实现,通过使用select多路复用技术来高效处理多个客户端的并发连接请求。服务器接收来自客户端的消息后将其广播给所有连接的客户端,客户端程序通过父子进程分离读写操作实现双向通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

班级: 09 计应 1       姓名:于青林     学号: 0906041017


前言:服务器与多个客户端建立链接,当客户端向服务器发送数据时,服务器会将这些数据转发给所有与它取得链接的客户端,即实现一个群发的效果。

------------------------------------ service . c ------------------------------------

/*------------------------------------------------------------------------------------
 注释:server.c是服务器的程序,接受客户端的链接请求并将客户端发来的数据转发给所有的客户端;
执行指令使server.c变为可执行文件:
[root@localhost ~]#gcc service.c -o service
-------------------------------------------------------------------------------------*/


/* server.c */
#include                                  
#include                                
#include                                
#include                            
#include "wrap.h"                                  

#define MAXLINE 80                                
#define SERV_PORT 8000                            

int main ( int argc , char ** argv )                  
       
int i , maxi , maxfd , listenfd , connfd , sockfd ;  
       
int nready , client [ FD_SETSIZE ];         //client     [FD_SETSIZE] 存放有数据请求的客户端;              
        ssize_t n
;                                      
        fd_set rset
, allset ;                            
       
char buf [ MAXLINE ];                              
       
char str [ INET_ADDRSTRLEN ];                      
        socklen_t cliaddr_len
;                          
       
struct sockaddr_in      cliaddr , servaddr ;
       
        listenfd
= Socket ( AF_INET , SOCK_STREAM , 0 );     //socket()打开一个网络通讯端口,返回一个套接字描述符给listenfd;

        bzero
(& servaddr , sizeof ( servaddr ));             //首先将结构体清零;
        servaddr
. sin_family       = AF_INET ;             //设置地址类型为AF_INET;
        servaddr
. sin_addr . s_addr = htonl ( INADDR_ANY );   //转换ip地址字节序,网络地址为INADDR_ANY,这个宏表示本地任意IP地址
        servaddr
. sin_port         = htons ( SERV_PORT );     //转换端口的字节序。到目前为止,结构体servaddr的设置已经完毕;

       
int opt = 1 ;
        setsockopt
( listenfd , SOL_SOCKET , SO_REUSEADDR , & opt , sizeof ( opt ));     //允许创建端口号相同但IP地址不同的多个socket描述符

       
Bind ( listenfd , ( struct sockaddr *)& servaddr , sizeof ( servaddr )); //将所监听的端口号与服务器的地址、端口绑定;

       
Listen ( listenfd , 20 );       //listen()声明服务器处于监听状态,并且最多允许有20个客户端处于连接待状态;

        maxfd
= listenfd ;                     //将所监听的最大的套接字描述符赋给maxfd;
        maxi
= - 1 ;                                    
       
for ( i = 0 ; i < FD_SETSIZE ; i ++)                
                client
[ i ] = - 1 ;         //for循环将client[i]的值设为-1,client[i]在下文中用来保存套接字描述符,这样可以把建立数据请求的端口赋给最前面的client[i];
        FD_ZERO
(& allset );           //将allset套接字描述符集清空。
        FD_SET
( listenfd , & allset );   //向allset套接字描述符集中添加服务器所监听到的端口(即listenfd所接受到的请求);

       
for ( ; ; ) {               //for 用于循环接受有数据请求,要与服务器交互的client的端口(用select这个系统调用);                    
                rset
= allset ;           // 把allset套接字描述符集的内容赋给rset;
                nready
= select ( maxfd + 1 , & rset , NULL , NULL , NULL );   // 调用select将rset有效的套接字描述符的个数给nready;
               
if ( nready < 0 )                         //select调用出错时,会返回一个负数给nready。该语句判断select是否调用成功。
                        perr_exit
( "select error" );                

               
if ( FD_ISSET ( listenfd , & rset )) {   //判断listenfd所接受到的客户端的请求是否在rset集合中,这是一个监听到的客户端与所监听客户端中有数据请求的客户端的一个比对,测试该数据请求的客户端是否在监听的队列中。
                        cliaddr_len
= sizeof ( cliaddr );   //把cliaddr结构体的长度赋给cliaddr_len,作为缓冲区的长度。
                        connfd
= Accept ( listenfd , ( struct sockaddr *)& cliaddr , & cliaddr_len );   //接受客户端的连接请求,与客户端建立连接。

                        printf
( "received from %s at PORT %d\n" ,
                               inet_ntop
( AF_INET , & cliaddr . sin_addr , str , sizeof ( str )),
                               ntohs
( cliaddr . sin_port ));           //打印客户端的ip地址和端口号。

                       
for ( i = 0 ; i < FD_SETSIZE ; i ++)           //for循环,i作为client的下标,表示放有数据请求的客户端的最小索引
                               
if ( client [ i ] < 0 ) {                    
                                        client
[ i ] = connfd ;                  
                                       
break ;                   //把有数据请求的客户端的套接字描述符放置到client[i]中最靠前的位置。
                               
}
                       
if ( i == FD_SETSIZE ) {         //若i以达到最大值FD_SETSIZE,则表示有数据请求的客户端已达到FD_SETSIZE
                                fputs
( "too many clients\n" , stderr );    
                               
exit ( 1 );                                
                       
}

                        FD_SET
( connfd , & allset );         //向allset套接字描述符集中添加与服务器建立连接并有数据请求的客户端端口;
                       
if ( connfd > maxfd )                        
                                maxfd
= connfd ;         //若此时建立并有数据请求的客户端已大于原来的套接字描述符最大值则用connfd从置maxfd;
                       
if ( i > maxi )                              
                                maxi
= i ;                   //maxi 表示已用的 client[i]的最大索引

                       
if (-- nready == 0 )                        
                               
continue ;                   //若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,则进行下一轮的select循环;
               
}

               
for ( i = 0 ; i <= maxi ; i ++) {         //for循环处理有数据请求的客户端;
                       
if ( ( sockfd = client [ i ]) < 0 )   //把client[i]中存放的客户端的套接字描述符赋给sockfd。若小于0,表示这个client[i]中没有套接字描述符。
                               
continue ;                   //继续执行for循环,查找数据请求的客户端;                      
                       
if ( FD_ISSET ( sockfd , & rset )) {   //测试sockfd是否在rset这个描述符集中;
                               
if ( ( n = Read ( sockfd , buf , MAXLINE )) == 0 ) {     // 读入客户端的数据,若n等于0表示客户端已经关闭了连接;
                               
                                       
Close ( sockfd );                     //客户端关闭连接了,服务器也关闭与客户端相应的连接;
                                        FD_CLR
( sockfd , & allset );           //清空与客户端连接的套接字描述符在allset集中;
                                        client
[ i ] = - 1 ;                   //同时将放置这个客户端套接字的数组位置设为-1,用来存放下一次的客户端数据请求的描述符;
                               
} else {                             //若n不为0,则处理客户端的数据请求;
                                       
for ( i = 0 ; i <= maxi ; i ++)        
                                               
Write ( client [ i ], buf , n );         // 把 buf写回与服务器相连的每一个的客户
                               
}

                               
if (-- nready == 0 )      
                                       
break ;   //再次判断nready,若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,则进行下一轮的select循环;
                       
}
               
}
       
}
}


------------------------------------ client . c ------------------------------------

/*-------------------------------------------------------------------------------------------
注释:client.c是客户端的程序,向服务器发送请求建立链接,并向服务器发送数据,让服务器转发给所有的客户端。
执行指令使client.c变为可执行文件:
[root@localhost pop3]# gcc client.c -0 client
--------------------------------------------------------------------------------------------*/


/* client.c */
#include                                
#include                                
#include                                
#include                            
#include "wrap.h"                                  

#define MAXLINE 80                                
#define SERV_PORT 8000                            

int main ( int argc , char * argv [])                  
{
       
struct sockaddr_in servaddr ;                  
       
char buf [ MAXLINE ];                              
       
int sockfd , n ;                                  
   
        sockfd
= Socket ( AF_INET , SOCK_STREAM , 0 );       //socket()打开一个网络通讯端口,返回一个套接字描述符给sockfd;

        bzero
(& servaddr , sizeof ( servaddr ));             //首先将结构体清零;
        servaddr
. sin_family = AF_INET ;                   //设置地址类型为AF_INET;
        inet_pton
( AF_INET , "127.0.0.1" , & servaddr . sin_addr );   // 由服务器端IP 转化为网络字节序保存在servaddr.sin_addr
        servaddr
. sin_port = htons ( SERV_PORT );           //转换端口的字节序。
   
       
Connect ( sockfd , ( struct sockaddr *)& servaddr , sizeof ( servaddr )); //与服务器建立链接;
while ( 1 )                                          
{       pid_t pid ;                                      
       
char * message ;                                  
       
int n ;                                        
        pid
= fork ();                       //调用fork()创建一个子进程;
       
if ( pid < 0 ) {                     //调用fork时出错;
                perror
( "fork failed" );                      
               
exit ( 1 );                                    
       
}
       
if ( pid == 0 ) {                 //若pid == 0则是子进程
                        n
= Read ( sockfd , buf , MAXLINE );           //把服务器上的数据读到buf中;
               
if ( n == 0 )                                   //若n等于0表示服务器已经关闭了连接;
                        printf
( "the servre has closed.\n" );
               
else                                       //即n>0,从服务器上读数据成功;
                       
Write ( STDOUT_FILENO , buf , n );         //把buf中的数据写到显示器上;
               
}
       
else {                                     //若pid >0则是父进程  
                        fgets
( buf , MAXLINE , stdin );               //用fgets从键盘读数据到buf中;
                       
Write ( sockfd , buf , strlen ( buf ));             //把buf中的数据写给服务器;
       
           
}
       
}

       
Close ( sockfd );     //关闭与服务器建立的链接;
       
return 0 ;                                      


------------------------------------ wrap . h ------------------------------------

/*-------------------------------------------------------------------------------------------
注释:service.c  client.c都包含了wrap.h这个头文件;

--------------------------------------------------------------------------------------------*/

#include
#include
#include

void perr_exit ( const char * s )
{
        perror
( s );
       
exit ( 1 );
}

int Accept ( int fd , struct sockaddr * sa , socklen_t * salenptr )
{
       
int n ;

again
:
       
if ( ( n = accept ( fd , sa , salenptr )) < 0 ) {
               
if (( errno == ECONNABORTED ) || ( errno == EINTR ))
                       
goto again ;
               
else
                        perr_exit
( "accept error" );
       
}
       
return n ;
}

void Bind ( int fd , const struct sockaddr * sa , socklen_t salen )
{
       
if ( bind ( fd , sa , salen ) < 0 )
                perr_exit
( "bind error" );
}

void Connect ( int fd , const struct sockaddr * sa , socklen_t salen )
{
       
if ( connect ( fd , sa , salen ) < 0 )
                perr_exit
( "connect error" );
}

void Listen ( int fd , int backlog )
{
       
if ( listen ( fd , backlog ) < 0 )
                perr_exit
( "listen error" );
}

int Socket ( int family , int type , int protocol )
{
       
int n ;

       
if ( ( n = socket ( family , type , protocol )) < 0 )
                perr_exit
( "socket error" );
       
return n ;
}

ssize_t
Read ( int fd , void * ptr , size_t nbytes )
{
        ssize_t n
;

again
:
       
if ( ( n = read ( fd , ptr , nbytes )) == - 1 ) {
               
if ( errno == EINTR )
                       
goto again ;
               
else
                       
return - 1 ;
       
}
       
return n ;
}

ssize_t
Write ( int fd , const void * ptr , size_t nbytes )
{
        ssize_t n
;

again
:
       
if ( ( n = write ( fd , ptr , nbytes )) == - 1 ) {
               
if ( errno == EINTR )
                       
goto again ;
               
else
                       
return - 1 ;
       
}
       
return n ;
}

void Close ( int fd )
{
       
if ( close ( fd ) == - 1 )
                perr_exit
( "close error" );
}
ssize_t
Readn ( int fd , void * vptr , size_t n )
{
        size_t  nleft
;
        ssize_t nread
;
       
char   * ptr ;

        ptr
= vptr ;
        nleft
= n ;
       
while ( nleft > 0 ) {
               
if ( ( nread = read ( fd , ptr , nleft )) < 0 ) {
                       
if ( errno == EINTR )
                                nread
= 0 ;
                       
else
                               
return - 1 ;
               
} else if ( nread == 0 )
                       
break ;

                nleft
-= nread ;
                ptr
+= nread ;
       
}
       
return n - nleft ;
}

ssize_t
Writen ( int fd , const void * vptr , size_t n )
{
        size_t nleft
;
        ssize_t nwritten
;
       
const char * ptr ;

        ptr
= vptr ;
        nleft
= n ;
       
while ( nleft > 0 ) {
               
if ( ( nwritten = write ( fd , ptr , nleft )) <= 0 ) {
                       
if ( nwritten < 0 && errno == EINTR )
                                nwritten
= 0 ;
                       
else
                               
return - 1 ;
               
}

                nleft
-= nwritten ;
                ptr
+= nwritten ;
       
}
       
return n ;
}
static ssize_t my_read ( int fd , char * ptr )
{
       
static int read_cnt ;
       
static char * read_ptr ;
       
static char read_buf [ 100 ];

       
if ( read_cnt <= 0 ) {
        again
:
               
if ( ( read_cnt = read ( fd , read_buf , sizeof ( read_buf ))) < 0 ) {
                       
if ( errno == EINTR )
                               
goto again ;
                       
return - 1 ;
               
} else if ( read_cnt == 0 )
                       
return 0 ;
                read_ptr
= read_buf ;
       
}
        read_cnt
--;
       
* ptr = * read_ptr ++;
       
return 1 ;
}

ssize_t
Readline ( int fd , void * vptr , size_t maxlen )
{
        ssize_t n
, rc ;
       
char    c , * ptr ;

        ptr
= vptr ;
       
for ( n = 1 ; n < maxlen ; n ++) {
               
if ( ( rc = my_read ( fd , & c )) == 1 ) {
                       
* ptr ++ = c ;
                       
if ( c   == '\n' )
                               
break ;
               
} else if ( rc == 0 ) {
                       
* ptr = 0 ;
                       
return n - 1 ;
               
} else
                       
return - 1 ;
       
}
       
* ptr   = 0 ;
       
return n ;
}


/*------------------------------------------------------------------------------------
   执行结果:
服务器端:
[root@localhost Desktop]# ./server
received from 127.0.0.1 at PORT 40555
received from 127.0.0.1 at PORT 40556
received from 218.198.18.30 at PORT 44625

客户端:
客户端1
[root@localhost Desktop]# ./client
nihao          客户端1的输入;  
nihao                 接受服务器转发的自己的数据;
good job                   接受服务器转发的客户端2的数据;
hello world             接受服务器转发的客户端3的数据;

客户端2
[root@localhost Desktop]# ./client
nihao                接受服务器转发的客户端1的数据;
good job                  客户端2的输入;
good job                  接受服务器转发的自己的数据;
hello world       接受服务器转发的客户端3的数据;

客户端3
[root@localhost Desktop]# ./client
nihao                接受服务器转发的客户端1的数据;
good job                  接受服务器转发的客户端2的数据;
hello world       客户端3的输入;
hello world       接受服务器转发的自己的数据;
-------------------------------------------------------------------------------------*/





<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(669) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~
评论热议
内容概要:本文详细阐述了DeepSeek大模型在服装行业的应用方案,旨在通过人工智能技术提升服装企业的运营效率和市场竞争力。文章首先介绍了服装行业的现状与挑战,指出传统模式难以应对复杂的市场变化。DeepSeek大模型凭借其强大的数据分析和模式识别能力,能够精准预测市场趋势、优化供应链管理、提升产品设计效率,并实现个性化推荐。具体应用场景包括设计灵感生成、自动化设计、虚拟试衣、需求预测、生产流程优化、精准营销、智能客服、用户体验提升等。此外,文章还探讨了数据安全与隐私保护的重要性,以及技术实施与集成的具体步骤。最后,文章展望了未来市场扩展和技术升级的方向,强调了持续优化和合作的重要性。 适用人群:服装行业的企业管理层、技术负责人、市场和销售团队、供应链管理人员。 使用场景及目标:①通过市场趋势预测和用户偏好分析,提升设计效率和产品创新;②优化供应链管理,减少库存积压和生产浪费;③实现精准营销,提高客户满意度和转化率;④通过智能客服和虚拟试衣技术,提升用户体验;⑤确保数据安全和隐私保护,建立用户信任。 阅读建议:此资源不仅涵盖技术实现的细节,还涉及业务流程的优化和管理策略的调整,建议读者结合实际业务需求,重点关注与自身工作相关的部分,并逐步推进技术的应用和创新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值