TCP与UDP的区别

1 篇文章 0 订阅
1 篇文章 0 订阅

 1.TCP与UDP在概念上的区别:

Tcp是一种面向连接的,可靠的字节流服务。(设有数据包编号与差错控制机制。)

特点:

由于网络的复杂性,传输信息时,数据包可能会丢失,差错控制中的确认机制在接收到数据包是发送确认信息,若是数据包丢失,则回发数据包编号,让对方重新发送;

由于网络的复杂性,传输信息时有多种网络传送途径可以选择,数据包被接收的顺序与发送顺序不同,可以根据数据包的编号,将数据包重组。

优点:

网络连接是以点对点的形式,加上上述特点,保证了数据的安全性,数据包不会中途被劫。

缺点:耗费资源很多。

UDP是无连接的,不可靠的数据协议报。通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据(没有类似TCP的差错控制和数据包编号机制)。

特点:通讯速度比较快,不需要TCP的三次握手。

1.多播:

与发送主机的子网相同的主机,都会在自己的端口收到主机发出来的UDP消息(发送到(包括本机)所有在这个网络地址的主机上,例如192.168.0.255,则在192.168.0.的主机都会收到)。消息会被复制并发到每个主机的网卡上去,网卡收到消息后提交给操作系统去处理,操作系统发现有程序在端口接收UDP数据则把消息转给相应的程序去处理,如果没有程序接收来自该端口的UDP消息,则操作系统丢弃该消息。

2.单播:

则消息只会从主机发到特定的主机上,主机的网卡收到消息后转给操作系统去处理,操作系统再把此消息转给相应程序去处理,如果没有程序处理就丢弃该包。

3.组播:

则消息只会从主机发到加入了规定组的主机的端口。象广播一样,组播消息一样会被复制发到网络所有主机的网卡上,但只有宣布加入这个组的主机的网卡才会把数据提交给操作系统去处理。如果没有加入组,则网卡直接将数据丢弃。要想接收组播消息的主机必须运行命令加入组

2.TCP和UDP编程过程:



TCP握手协议 
在TCP/IP协议中,TCP协议采用三次握手建立一个连接。 
第一次握手:建立连接时,客户端发送SYN包(SYN=J)到服务器,并进入SYN_SEND状态,等待服务器确认; 
第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=J+1),同时自己也发送一个SYN包(SYN=K),即SYN+ACK包,此时服务器V状态; 
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=K+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据,



四次分手:
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。


1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
    这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在 一个报文里来发送。

但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以 未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报 文和FIN报文多数情况下都是分开发送的.

2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。






源代码实例:

NO.1  UDP广播实例:

客户端:

include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>
#include <pthread.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

int sockfd;
struct sockaddr_in servaddr,recvaddr;
pthread_t t1;

void rev()
{
    int ret;
    char buffer[BUFFER_SIZE];
    uint32_t len=sizeof(recvaddr);
    while(1)
    {
        if((ret=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&recvaddr,&len))<0)
        {
            perror("ERROR reading from socket");
            break;
        }
        buffer[ret]=0;
        printf("[form:%s:%u]%s\n",inet_ntoa(recvaddr.sin_addr),ntohs(recvaddr.sin_port), buffer);

    }
}
int main(int argc,char **argv)
{
    int opt=1;
    int ret=RET_OK;
    char buffer[BUFFER_SIZE];
    struct hostent *server;

    if(argc<2)
    {
        fprintf(stderr,"usage &s hostname\n",argv[0]);
        return RET_ERR;
    }
    
    if((server=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
    {
        herror("ERROR opening socket");
        return RET_ERR;
    }
   

 //广播设置套接字
    if((setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt)))<0)
    {
        perror("ERROR setsockopt");
        goto failed;
    }


    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换
    
    pthread_create(&t1, NULL, (void*)rev, NULL);

    while(1)
    {
        printf("Enter the message : ");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL)break;
        if((ret=sendto(sockfd,buffer,strlen(buffer)+1,0,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
        {
            perror("ERROR writing to socket");
            break;
        }
        
    }
    failed:
    close(sockfd);

return ret<0?RET_ERR:RET_OK;
}

服务器端:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

int main(int agrc,char **argv)
{
    int sockfd,opt=1;
    uint32_t len;
    struct sockaddr_in cliaddr;
    uint8_t buffer[BUFFER_SIZE];
    int ret=RET_OK;
    
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }
    
    //SO_REUSEADDR允许重用本地址
    if((ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0)
    {
        perror("ERROR setsockopt");
        goto failed;
    }

    memset(&cliaddr,0,sizeof(cliaddr));
    cliaddr.sin_family=AF_INET;
    cliaddr.sin_addr.s_addr=INADDR_ANY;
    cliaddr.sin_port=htons(ECHO_PORT);

    if((ret=bind(sockfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr)))<0)
    {
        perror("ERROR on binding");
        goto failed;
    }

    do
    {
        len=sizeof(cliaddr);
        if((ret=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&cliaddr,&len))>0)
        {
            buffer[ret] = 0;
            printf("[%s:%d]%s",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port),buffer);
            ret=sendto(sockfd,buffer,ret,0,(struct sockaddr*)&cliaddr,len);
        }
    }while(ret>=0);
    failed:
        close(sockfd);
        return 0;
}

运行过程:用ifconfig eth0 设置ip,并查看广播地址作为client.c的参数运行;

运行结果:多个服务器可以同时接收来自客户端的信息;

UDP,client中使用connect的情况(differ from TCP):指定接收数据的地址,其他IP地址的数据拒绝接收,

此时,sendto,recvfrom可以用write和read代替.

int sockfd;
struct sockaddr_in servaddr,recvaddr;
pthread_t t1;

void rev()
{
    int ret;
    char buffer[BUFFER_SIZE];
    uint32_t len=sizeof(recvaddr);
    while(1)
    {
        if((ret=read(sockfd,buffer,sizeof(buffer)-1,0))<0)
        {
            perror("ERROR reading from socket");
            break;
        }
        buffer[ret]=0;
        printf("[form:%s:%u]%s\n",inet_ntoa(recvaddr.sin_addr),ntohs(recvaddr.sin_port), buffer);

        //printf("Server echo message: %s\n",buffer);

    }
}
int main(int argc,char **argv)
{
    int opt=1;
    int ret=RET_OK;
    char buffer[BUFFER_SIZE];
    struct hostent *server;

    if(argc<2)
    {
        fprintf(stderr,"usage &s hostname\n",argv[0]);
        return RET_ERR;
    }
    
    if((server=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
    {
        herror("ERROR opening socket");
        return RET_ERR;
    }
#if 0
    //广播设置套接字
    if((setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt)))<0)
    {
        perror("ERROR setsockopt");
        goto failed;
    }
#endif

    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    if((ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))<0) //创建连接
    {
        perror("ERROR connecting");
        goto failed;
    }
    pthread_create(&t1, NULL, (void*)rev, NULL);

    while(1)
    {
        printf("Enter the message : ");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL)break;
        if((ret=write(sockfd,buffer,strlen(buffer)+1,))<0)
        {
            perror("ERROR writing to socket");
            break;
        }
        
#if 0
        retry:
        if((ret=read(sockfd,buffer,sizeof(buffer)-1))<0)
        {
            perror("ERROR reading from socket");
            break;
        }

        /*  单播时不用connect则再次判断的代码段 */
        if(servaddr.sin_family!=recvaddr.sin_family||servaddr.sin_addr.s_addr!=recvaddr.sin_addr.s_addr||servaddr.sin_port!=recvaddr.sin_port)
        {
            fprintf(stderr,"Received %s echo\r\n",inet_ntoa(recvaddr.sin_addr));
            goto retry;
        }
#endif

    }
    failed:
    close(sockfd);
    return ret<0?RET_ERR:RET_OK;

}



NO.2UDP单播

客户端:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>
#include <pthread.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

int sockfd;
struct sockaddr_in servaddr,recvaddr;
pthread_t t1;

void rev()
{
    int ret;
    char buffer[BUFFER_SIZE];
    uint32_t len=sizeof(recvaddr);
    while(1)
    {
        if((ret=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&recvaddr,&len))<0)
        {
            perror("ERROR reading from socket");
            break;
        }
        buffer[ret]=0;
        printf("[form:%s:%u]%s\n",inet_ntoa(recvaddr.sin_addr),ntohs(recvaddr.sin_port), buffer);

    }
}
int main(int argc,char **argv)
{
    int opt=1;
    int ret=RET_OK;
    char buffer[BUFFER_SIZE];
    struct hostent *server;

    if(argc<2)
    {
        fprintf(stderr,"usage &s hostname\n",argv[0]);
        return RET_ERR;
    }
    
    if((server=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))<0)
    {
        herror("ERROR opening socket");
        return RET_ERR;
    }
    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    pthread_create(&t1, NULL, (void*)rev, NULL);

    while(1)
    {
        printf("Enter the message : ");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL)break;
        if((ret=sendto(sockfd,buffer,strlen(buffer)+1,0,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
        {
            perror("ERROR writing to socket");
            break;
       }
    }
    failed:
    close(sockfd);
    return ret<0?RET_ERR:RET_OK;

}
服务器端同上;

运行过程:./client ip地址;

运行结果:一个服务器终端可以接收数据。


NO.3支持多人聊天和私聊的客户端

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>
#include<pthread.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029
struct sockaddr_in recvaddr;
char buffer[BUFFER_SIZE];

void recv2(void* sockfd)
{
    uint32_t len=sizeof(recvaddr);
   int ret;
    while(1)
    { 
    if((ret=recvfrom((int)sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&recvaddr,&len))<0)
        {
            perror("ERROR reading from socket");
            break;
        }
        buffer[ret]=0;
        printf("Server echo message: %s\n",buffer);
    
    }
}
int main(int argc,char **argv)
{
    int sockfd;
    pthread_t t1;
    int ret=RET_OK;
    struct sockaddr_in servaddr,tempaddr;
    struct hostent *server;
    char tempbuf[BUFFER_SIZE],buf[BUFFER_SIZE];

    if(argc<2)
    {
        fprintf(stderr,"usage &s hostname\n",argv[0]);
        return RET_ERR;
    }
    
    if((server=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    
    if((sockfd=socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP))<0)
    {
        herror("ERROR opening socket");
        return RET_ERR;
    }

    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    pthread_create(&t1,NULL,(void *)recv2,(void *)sockfd);
    while(1)
    {
        printf("Enter the message : ");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL)break;
     
        if(strstr(buffer,":") && strstr(buffer,"@"))  /*私人聊天*/
        {
                tempaddr.sin_family=AF_INET;                
                tempaddr.sin_addr.s_addr=inet_addr(strtok(buffer,":"));
                tempaddr.sin_port=htons(atoi(strtok(NULL,"@")) );
                char *del=":@";
                strcpy(tempbuf,strtok(NULL,del));

            if((ret=sendto(sockfd,tempbuf,strlen(tempbuf),0,(struct sockaddr*)&tempaddr,sizeof(tempaddr)))<0)
                  {
                    perror("ERROR writing to socket");
                    break;
                   }           
            }
        else   /*多人聊天*/
            {
                if((ret=sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
                {
                    perror("ERROR writing to socket");
                    break;
                }
            }
        
        
    }
    close(sockfd);
    return ret<0?RET_ERR:RET_OK;

}
服务器端同上


NO.4 TCP半双工聊天

客户端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

int main(int argc,char **argv)
{
    int sockfd,ret=RET_OK;
    struct sockaddr_in servaddr;
    struct hostent *server;

    char buffer[BUFFER_SIZE];

    if(argc<2)
    {
        fprintf(stderr,"usage %s hostname\n",argv[0]);
        return RET_ERR;
    }
    if((server=gethostbyname(argv[1]))==NULL) //由域名获取IP地址
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0) //创建套接字
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }

    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    if((ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))<0) //创建连接
    {
        perror("ERROR connecting");
        goto failed;
    }
    while(1)
    {
        printf("\tEnter the message : ");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL) //输入字符到buffer指针
        {
            break;
        }
        if((ret=write(sockfd,buffer,strlen(buffer)))<0)   //把buffer的内容 写入strlen(buffer)的字节到sockfd的socket中
        {
            perror("ERROR write to socket");
            break;
        }
        if((ret=read(sockfd,buffer,sizeof(buffer)-1))<0)//读取到buffer中
        {
            perror("ERROR read from socket");
            break;
        }
        else
        {
            buffer[ret]='\0';
            printf("\tServer echo message: %s\n",buffer);
        }
        if(ret==0)
        {
            printf("Server disconnect\n");
            break;
        }
    }
    failed:
        close(sockfd);
        return ret<0?RET_ERR:RET_OK;
}
服务器端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

char buffer[BUFFER_SIZE];    

static int my_readline(int fd,void *buf,unsigned len)
{
    int n,rc;
    char c,*ptr;
    ptr=buf;
    for(n=1;n<len;n++)
    {
        again:
        if((rc=read(fd,&c,1))==1)
        {
            *ptr++=c;
            if(c=='\n')break;
        }else if(rc==0)
        {
            if(n==1)return 0;
            else break;
        }else
        {
            if(errno==EINTR)goto again;
            return -1;
        }
    }
    *ptr=0;
    return n;
}

static int proc_echo(int sockfd)
{
    int ret;
    while(1)
    {
        if((ret=my_readline(sockfd,buffer,sizeof(buffer)-1))<0)
        {
            perror("read");
            return -1;
        }else if(ret==0)
        {
            printf("client disconnect.\n");
            return 0;
        }
        buffer[ret]='\0';
        printf("client echo message:  %s\n",buffer);
        printf("\tEnter message:\n ");
        fflush(stdout);
        if(fgets(buffer,sizeof(buffer),stdin)==NULL) //输入字符到buffer指针
        {
            break;
        }
        if((ret=write(sockfd,buffer,strlen(buffer)))<0)    //把buffer的内容 写入strlen(buffer)的字节到nsock的socket中
        {
            perror("ERROR write to socket");
            break;
        }
    }
  return -1;
}
int main(int argc,char **argv)
{
    int sockfd,nsock,ret=0;
    uint32_t len;
    struct sockaddr_in servaddr,cliaddr;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0) 
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }
    memset(&servaddr,0,sizeof(servaddr));

    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=INADDR_ANY;
    servaddr.sin_port=htons(ECHO_PORT);

    if((ret=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
    {
        perror("ERROR on binding");
        goto failed;
    }

    if((ret=listen(sockfd,LISTEN_QUEUE_NUM))!=0)
            {
        perror("ERROR on listen");
        goto failed;
           }    
    printf("\tserver is listening>>>\n");
    while(1)
    {
        len=sizeof(cliaddr);
        if((nsock=accept(sockfd,(struct sockaddr*)&cliaddr,(uint32_t*)&len))<0)
        {
            perror("accept");    
            break;        
        }
        printf("\tclient connect success!!!\n");
        proc_echo(nsock);
        close(nsock);
    }
    
    failed:
    close(sockfd);
    return ret<0?RET_ERR:RET_OK;
}
运行:./ser, server is listening>>>

          ./cli 服务器地址;

                 Enter:  ......

          服务器:读出数据,并从键盘接收数据回发给客户;(此时客户端阻塞直到收到服务器的回信);


NO.5 TCP全双工通信

客户端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include"echo_net.h"

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029
char buffer[BUFFER_SIZE];
pthread_t t2;

static void proc_echo(void *sockfd)
{
    int ret;
    
    while(1)
    {
        if((ret=read((int)sockfd,buffer,sizeof(buffer)-1))<0)//读取到buffer中
            {
            perror("ERROR read from socket");
            break;
            }
        if(ret==0)
            {
            printf("Server disconnect\n");
            //pthread_cancel(t2);
            break;
            }

            buffer[ret]='\0';
            printf("\tServer echo message: %s\n",buffer);
            
    }
}

int main(int argc,char **argv)
{
    int sockfd,ret=RET_OK;
    pthread_t t2;
    struct sockaddr_in servaddr;
    struct hostent *server;

    if(argc<2)
    {
        fprintf(stderr,"usage %s hostname\n",argv[0]);
        return RET_ERR;
    }
    if((server=gethostbyname(argv[1]))==NULL) //由域名获取IP地址
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0) //创建套接字
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }

    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    if((ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))<0) //创建连接
    {
        perror("ERROR connecting");
        goto failed;
    }
    pthread_create(&t2,NULL,(void *)proc_echo,(void*)sockfd);
    while(1)
    {
        printf("\tEnter the message : \n");
        if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL) //输入字符到buffer指针
        {
            break;
        }
        if((ret=write(sockfd,buffer,strlen(buffer)))<0)    //把buffer的内容 写入strlen(buffer)的字节到sockfd的socket中
        {
            perror("ERROR write to socket");
            break;
        }
    }
  
    failed:
        close(sockfd);
        return ret<0?RET_ERR:RET_OK;
}

服务器端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include <pthread.h>
#include"echo_net.h"
#define BUFFER_SIZE 256

pthread_t t2;

static void proc_echo_2(void *sockfd);

static int my_readline(int fd,void *buf,unsigned len)
{
    int n,rc;
    char c,*ptr;
    ptr=buf;
    for(n=1;n<len;n++)
    {
        again:
        if((rc=read(fd,&c,1))==1)
        {
            *ptr++=c;
            if(c=='\n')break;
        }else if(rc==0)
        {
            if(n==1)return 0;
            else break;
        }else
        {
            if(errno==EINTR)goto again;
            return -1;
        }
    }
    *ptr=0;
    return n;
}

static int proc_echo(int sockfd)
{
    int ret;
    pthread_t t2;
    char buf[BUFFER_SIZE];
    pthread_create(&t2,NULL,(void *)proc_echo_2,(void*)sockfd);
    while(1)
    {
        if((ret=my_readline(sockfd,buf,sizeof(buf)-1))<0)
        {
            perror("read");
            return -1;
        }else if(ret==0)
        {
            printf("client disconnect.\n");
            pthread_cancel(t2);    
            return 0;
        }
        buf[ret]='\0';
        printf("client echo message:  %s",buf);
        fflush(stdout);
    }
    return -1;
}

static void proc_echo_2(void *sockfd)
{
    int ret;
    char buffer[BUFFER_SIZE];

    while(1)
    {
        printf("\tEnter message:\n ");
            fflush(stdout);
        if(fgets(buffer,sizeof(buffer),stdin)==NULL) //输入字符到buffer指针
            {
              break;
            }
        if((ret=write((int)sockfd,buffer,strlen(buffer)))<0)    //把buffer的内容 写入strlen(buffer)的字节到nsock的socket中
            {
                perror("ERROR write to socket");
                break;
            }
    }

}

int main(int argc,char **argv)
{
    int sockfd,nsock,ret=0;
    int opt;
    char buf[32];
    uint32_t len;
    
    struct sockaddr_in servaddr,cliaddr;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }
    //SO_REUSEADDR允许重用本地址
        if((ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0)
        {
            perror("ERROR setsockopt");
            return RET_ERR;
        }

    memset(&servaddr,0,sizeof(servaddr));

    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=INADDR_ANY;
    servaddr.sin_port=htons(ECHO_PORT);

    if((ret=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)))<0)
    {
        perror("ERROR on binding");
        goto failed;
    }

    if((ret=listen(sockfd,LISTEN_QUEUE_NUM))!=0)
            {
        perror("ERROR on listen");
        goto failed;
           }
    printf("\tserver is listening>>>\n");

    while(1)
    {
        len=sizeof(cliaddr);
        if((nsock=accept(sockfd,(struct sockaddr*)&cliaddr,(uint32_t*)&len))<0)
        {
                    perror("accept");
                    break;
        }
        printf("\tclient connect success!!!\n");
    //    printf("\tclient id =%s\n",inet_ntoa(cliaddr.sin_addr));
        printf("client id =%s\n",
                inet_ntop(cliaddr.sin_family,&cliaddr.sin_addr,buf,sizeof(buf)));

        printf("\tport =%d\n",ntohs(cliaddr.sin_port));
        pthread_create(&t2,NULL,(void *)proc_echo_2,(void*)sockfd);
        proc_echo(nsock);
        close(nsock);
    }

    failed:
    close(sockfd);
    return ret<0?RET_ERR:RET_OK;
}

运行:服务器与客户端双方可以随时发送信息;


NO.6 TCP I/O复用之select()
客户端:

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<errno.h>

#define RET_OK 0
#define RET_ERR -1

#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256

#define ECHO_PORT 2029

int main(int argc,char **argv)
{
    int maxfd=0;
    fd_set rset,set;
    int nfound,bytesread;
    int sockfd;
    int ret=RET_OK;
    struct sockaddr_in servaddr;
    struct hostent *server;
    char buffer[BUFFER_SIZE];

    if(argc<2)
    {
        fprintf(stderr,"usage &s hostname\n",argv[0]);
        return RET_ERR;
    }
    
    if((server=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname.");
        return RET_ERR;
    }
    
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        herror("ERROR opening socket");
        return RET_ERR;
    }


    memset(&servaddr,0,sizeof(servaddr));//申请信息表空间

    servaddr.sin_family=AF_INET;//设为IPv4
    servaddr.sin_addr.s_addr=*(uint32_t *)server->h_addr;//地址转换,32位大端字序
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);//设定端口,16位大端字序转换

    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        perror("ERROR connect");
        return RET_ERR;
    }

    maxfd=fileno(stdin);
    FD_ZERO(&set);
    FD_SET(sockfd,&set);
    FD_SET(maxfd,&set);
    maxfd=(maxfd>sockfd?maxfd:sockfd)+1;

    while(1)
    {
        
        rset=set;
        if((nfound=select(maxfd,&rset,(fd_set*)0,(fd_set*)0,NULL))<0)
        {
            if(errno==EINTR)
            {
                fprintf(stderr,"interrupted system call\n");
                continue;
            }
            perror("select");
            exit(1);
        }
        
        if(FD_ISSET(fileno(stdin),&rset))
        {
            
            if(fgets(buffer,sizeof(buffer)-1,stdin)==NULL)
            {
                if(ferror(stdin))
                {
                    perror("stdin");
                    return -1;
                }
                return 0;
            }
            if(write(sockfd,buffer,strlen(buffer))<0)
            {    
                perror("ERROR writing");
                return -1;
            }
        }
        if(FD_ISSET(sockfd,&rset))
        {
            if((bytesread=read(sockfd,buffer,sizeof(buffer)))<0)
            {    
                perror("ERROR read");
                exit(1);
            }else if(bytesread==0)
            {
                fprintf(stderr,"server disconnect\n");
                exit(0);
            }
                
        buffer[bytesread]=0;//最后加上结束符
        printf("buffer: %s",buffer);
        }
    
    }

    return 0;

}

服务器端:

#include<sys/types.h>
#include<ctype.h>
#include<strings.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<errno.h>
#include<sys/time.h>
#include<stdio.h>
#include<string.h>
#include<sys/select.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/wait.h>

#define RET_OK 0
#define RET_ERR -1
#define LISTEN_QUEUE_NUM 5
#define BUFFER_SIZE 256
#define ECHO_PORT 2029

int main(int argc,char **argv)
{
    struct sockaddr_in servaddr,remote;
    int request_sock,new_sock;
    int nfound,fd,maxfd,bytesread;
    uint32_t addrlen;
    fd_set rset,set;
    struct timeval timeout;
    char buf[BUFFER_SIZE];

    if((request_sock=socket(AF_INET,SOCK_STREAM, IPPROTO_TCP))<0)
    {
        perror("ERROR opening socket");
        return RET_ERR;
    }
    memset(&servaddr,0,sizeof(servaddr));

    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=INADDR_ANY;
    servaddr.sin_port=htons((uint16_t)ECHO_PORT);

    if(bind(request_sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
    {
        perror("ERROR on binding");
        return RET_ERR;
    }
    if(listen(request_sock,LISTEN_QUEUE_NUM)<0)
    {
        perror("ERROR on listen");
        return RET_ERR;
    }

    FD_ZERO(&set);
    FD_SET(request_sock,&set);
    maxfd=request_sock;
    while(1)
    {
        rset=set;
        timeout.tv_sec=10;
        timeout.tv_usec=500000;
        if((nfound=select(maxfd+1,&rset,(fd_set*)0,(fd_set*)0,&timeout))<0)
        {
            perror("select");
            return -1;
        }else if(nfound==0)
        {
            printf(".");
            fflush(stdout);
            continue;
        }

        if(FD_ISSET(request_sock,&rset))
        {
            addrlen=sizeof(remote);
            if((new_sock=accept(request_sock,(struct sockaddr*)&remote,&addrlen))<0)
            {
                perror("accept");
                return -1;
            }
            printf("connection from host %s,port %d,socket %d\r\n",inet_ntoa(remote.sin_addr),ntohs(remote.sin_port),new_sock);
            FD_SET(new_sock,&set);
            if(new_sock > maxfd)maxfd=new_sock;
            FD_CLR(request_sock,&rset);
            nfound--;
        }

        for(fd=0;fd<=maxfd&&nfound>0;fd++)
        {
            if(FD_ISSET(fd,&rset))
            {
                nfound--;
                if((bytesread=read(fd,buf,sizeof(buf)-1))<0)
                {
                    perror("read");
                }
                if(bytesread==0)
                {
                    fprintf(stderr,"server: end of file on %d\r\n",fd);
                    FD_CLR(fd,&set);
                    close(fd);
                    continue;
                }
                buf[bytesread]=0;
                printf("%s: %d bytes from %d: %s\n",argv[0],bytesread,fd,buf);
                /*私聊
                    if(buf[1]==':')
                {
                  if(write(buf[0]-'0',buf+1,bytesread)<0)
                     {
                        perror("echo");
                        FD_CLR(fd,&set);
                        close(fd);
                     }
                }

                */
                if(write(fd,buf,bytesread)<0)
                {
                    perror("echo");
                    FD_CLR(fd,&set);
                    close(fd);
                }
            }
        }
    }
    return 0;
}

NO.7TCP I/O复用之poll();

客户端:

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<netinet/in.h>
#include<errno.h>
#include<ctype.h>
#include<netdb.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/select.h>
#include<sys/poll.h>

#define RET_OK 0
#define RET_ERR -1
#define LISTEN_QUEUE_NUM 5

#define BUFFER_SIZE 256
#define ECHO_PORT   2029

int main(int argc, char **argv)
{
    int sock;
    struct sockaddr_in servaddr;
    struct hostent *server;
    static struct pollfd cpoll[2];
    int nfound, bytesread;
    char buf[BUFFER_SIZE];

    if(argc < 2)
    {
        fprintf(stderr, "usage %s hostname\n", argv[0]);
        return RET_ERR;
    }
    if((server = gethostbyname(argv[1])) == NULL)
    {
        perror("gethostbyname. ");
        return RET_ERR;
    }

    /*setup socket*/
    if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return -1;
    }

    /*fillup ip address structure*/
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = *(uint32_t *) server->h_addr;
    servaddr.sin_port = htons((uint16_t) ECHO_PORT);

    /*connect to server*/
    if(connect(sock, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        perror("connect");
        return -1;
    }

    /*fill up poll struct*/
    cpoll[0].fd = fileno(stdin);
    cpoll[0].events = POLLIN;
    cpoll[1].fd = sock;
    cpoll[1].events = POLLIN;

    while (1)
    {
        if ((nfound = poll(cpoll, 2, -1)) < 0)
        {
            if (errno == EINTR)
            {
            fprintf(stderr, "interruptedb system call\n");
            continue;
            }
            perror("select");
            exit(1);
        }
        /*check stdin is ready or not ? */
        if (cpoll[0].revents & (POLLIN | POLLERR))
        {
            if(fgets(buf, sizeof(buf), stdin) == NULL)
            {
                if (ferror(stdin))
                {
                    perror("stdin");
                    return -1;
                }
                return 0;
            }
            /*write to socket*/
            if (write(sock, buf, strlen(buf)) < 0)
            {
                perror("write");
                return -1;
            }
        }

        /*check socket is ready or not ? */
        if(cpoll[1].revents & (POLLIN | POLLERR))
        {
            /*read from socket*/
            if((bytesread = read(sock, buf, sizeof(buf))) < 0)
            {
                perror("read");
                exit(1);
            }else if(bytesread == 0)
            {
                fprintf(stderr, "server disconnect\n");
                exit(0);
            }
            buf[bytesread] = 0;
            printf("%s\n", buf);
        }
    }
    return 0;
}

服务器端:

#include<sys/types.h>
#include<ctype.h>
#include<strings.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<errno.h>
#include<sys/time.h>
#include<stdio.h>
#include<string.h>
#include<sys/poll.h>
#include<stdlib.h>

#define LISTEN_QUEUE_NUM 5
#define MAX_CONNECT 1024

#define BUFFER_SIZE 256
#define ECHO_PORT 2029

static struct pollfd clipoll[MAX_CONNECT];
int max = 0;

/*initialize poll struct*/
static void poll_init()
{
    int i;
    for (i = 0; i < MAX_CONNECT; i++)
        clipoll[i].fd = -1;
}

/*
 find a unused pollfd struct
 if found then return its index
 else return -1
*/ 
static int poll_alloc()
{
    int i;
    for (i = 0; i < MAX_CONNECT; i++)
    {
        if (clipoll[i].fd < 0)
        return i;
    }
    return -1;
}

/*find used max index in pllfd*/
static void update_max()
{
    int i;
    max = 0;
    for (i = 0; i < MAX_CONNECT; i++)
    {
        if (clipoll[i].fd > 0)
        max = i;
    }
}

/*free a pollfd when unused*/
static int poll_free(int i)
{
    close(clipoll[i].fd);
    clipoll[i].fd = -1;
    clipoll[i].events = 0;
    clipoll[i].revents = 0;
    update_max();
}

/*
  fill up pollfd with file descriptable
  and event
*/
static int poll_set_item(int fd, uint32_t events)
{
    int i;
    if ((i = poll_alloc()) < 0)
        return -1;
    clipoll[i].fd = fd;
    clipoll[i].events = events;
    clipoll[i].revents = 0;
    if (i > max)
        max = i;
    return 0;
}

int main(int argc, char **argv)
{
    struct sockaddr_in servaddr, remote;
    int request_sock, new_sock;
    int nfound, i, bytesread;
    uint32_t addrlen;
    char buf[BUFFER_SIZE];
    /*setup socket*/
    if ((request_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return -1;
    }
    /*fill up ip address structure*/
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(ECHO_PORT);
    /*bind server ip address structure*/
    if (bind(request_sock, (struct sockaddr *) &servaddr, sizeof(servaddr)) <    0)
    {
        perror("bind");
        return -1;
    }
    /*listen client*/
    if (listen(request_sock, LISTEN_QUEUE_NUM) < 0)
    {
        perror("listen");
        return -1;
    }
    poll_init();
    poll_set_item(request_sock, POLLIN);
    while (1)
    {
    /*check whether ready file exist ?*/
        if ((nfound = poll(clipoll, max + 1, 500)) < 0)
        {
            if (errno == EINTR)
            {
                printf("interruptedb system call\n");
                continue;
            }
            perror("poll");
            return -1;
        }else if(nfound == 0)
        {
            printf(".");
            fflush(stdout);
            continue;
        }
        /*check listen socket if ready or not ?*/
        if (clipoll[0].revents & (POLLIN | POLLERR))
        {
            addrlen = sizeof(remote);
            if ((new_sock =    accept(request_sock, (struct sockaddr *) &remote,    &addrlen)) < 0)
            {
                perror("accept");
                return -1;
            }
            printf("connection fromm host %s,port %d, socket %d\r\n",
                    inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);
            if (poll_set_item(new_sock, POLLIN) < 0)
                fprintf(stderr, "Too many connects\r\n");
            nfound--;
        }
        /*check communicating socket if ready or not ? */
        for (i = 1; i <= max && nfound > 0; i++)
        {
            if (clipoll[i].fd >= 0 && clipoll[i].revents & (POLLIN | POLLERR))
            {
                nfound--;
                if((bytesread = read(clipoll[i].fd, buf, sizeof(buf) - 1)) < 0)
                {
                    perror("read");
                    poll_free(i);
                    continue;
                }
                if(bytesread == 0)
                {
                    fprintf(stderr, "server: end of file on %d\r\n", clipoll[i].fd);
                    poll_free(i);
                    continue;
                }
                buf[bytesread] = 0;
                printf("%s:%d bytes from %d :%s\n", argv[0], bytesread,
                clipoll[i].fd, buf);
                if (write(clipoll[i].fd, buf, bytesread) != bytesread)
                {
                    perror("echo");
                    poll_free(i);
                    continue;
                }
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值