tcp传输例子

/*
文档笔记:


1.
    TCP通信结构图
      服务器                                客户端
        |
        v
      创建套接字:socket()
        |
        v 
      绑定套接字:bind()
        |                                    |   
        v                                    v                                             
      监听:listen()                     创建套接字:socket()   
        |                                    |
        v                                    v    
      接受连接:accept()       <---      连接服务器:connect()
        |                                    |
        v                                    v       
      收消息:read()/recv()    <---      发消息:write()/send() 
        |                                    |
        v                                    v    
      发消息:write()/send()   --->      收消息:read()/recv()         


三次握手:第一次是客户端请求连接,第二次是服务器授予连接,第三次是客户端发送确认连接给服务器。 


    


2.TCP/IP协议:(Transmission Control Protocol/Internet Protocol)传输控制协议/因特网互联协议,又名网络通讯协议。
通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,知道所有的数据安全正确地传输到目的地,
而IP是给因特网的每台电脑规定一个IP地址。   


3.socket套接字,指的是在网络通信以前建立的通信接口。进行网络连接以前,需要向系统注册申请一个新的socket,然后使用这个socket进行网络连接。


4.int socket(int domain,int type,int protocol);  
返回值:成功时,返回一个小的非负整数值,与文件描述符类似,出错时,返回-1。 
参数:  domain: 协议族  a.AF_INET //IPv4  b.AF_INET6 //IPv6
type: socket类型  
a.SOCK_STREAM   //TCP 
b.SOCK_DGRAM    //UDP 
c.SOCK_RAW      //IP  
protocol:所使用的协议。一般是等于0。




type: socket类型  a.SOCK_DGRAM  //TCP b.SOCK_DGRAM  //UDP c.SOCK_RAW     //IP  protocol:所使用的协议。一般是等于0。    


5.int bind(int sockfd,struct sockaddr *my_addr,socklen_t addrlen)  绑定端口:当socket函数返回一个描述符时,只是存在于其协议族的空间中,
并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),
bind函数可以将一组固定的地址绑定到sockfd上。  返回值:0 -- 成功,-1 -- 出错。 
参数:  sockfd:是socket返回的描述符。  my_addr:指定了想要绑定的IP和端口号,均要使用网络字节序。 
addrlen:是前面struct sockaddr(与sockaddr_in等价)的长度。 
struct sockadr_in{  
unsigned short int sin_family;          //协议族 
unsigned short int sin_port;            //端口号       
struct in_addr sin_addr;                //Internet地址  
unsigned char sin_zero[8];              //未使用的字段,填充为0
};  


struct in_addr{  
unsigned long s_addr; 
};  
a. 利用u_long htonl(u_long hostlong);将主机字节序转换为TCP/IP网络字节序. 
b. 利用u_short htons(u_short hostshort);将主机字节序转换为TCP/IP网络字节序 


inet_addr()是将一个点分10进制的ip地址(如192.168.0.1)转换为上述结构中需要的32位ip地址(0xC0A80001)




6.int listen(int sockfd, int count); 监听:等待用户的连接。  
返回值:0 -- 成功,-1 -- 出错。 
参数:  sockfd: 创建的套接字描述符。 count:  最大连接数。   
7.int connect(int sockfd, struct  sockaddr *addr, socklen_t addlen ); 
连接:客户端通过connect函数实现和服务器建立连接。 返回值:0 -- 成功,-1 -- 出错。 
参数:  sockfd: 客户端的socket描述符。 
        addr:   服务器地址。  
        addlen: socket地址的长度。
        
        
8.int accept(int sockfd, struct sockaddr *addr, socklen_t *add_len);  
连接:accept函数成功返回,那么服务器与客户端成功连接,此时服务器即可通过accept函数返回的连接套接字和客户端进行通信。
accept函数默认是阻塞进程,直到有客户端连接成功,他会返回一个连接套接字。 
参数:  sockfd:  服务器套接字描述符。  addr:    用来返回客户端的协议地址。 add_len: 协议地址的长度。 
  
9.size_t send(int sockfd, const char *buf, size_t len, int flag); 
返回值:  >0 – 成功拷贝至发送缓冲区的字节数(可能小于len), 
          -1 – 出错,并置错误号errno. 
参数:  sockfd: 发送端套接字描述符(非监听描述符),也就是说在服务器端就是连接套接字,在客户端就是他本省那个套接字。  
buf:    要发送数据的缓存。 
len:    实际要发送的数据长度。  
flag:   指定的协议,一般设置为0。   


10.size_t recv(int sockfd, char *buf, size_t len, int flag); 
返回值:成功时,返回拷贝的字节数,失败返回-1。 
参数:  sockfd: 发送端套接字描述符(非监听描述符),也就是说在服务器端就是连接套接字,在客户端就是他本省那个套接字。  
buf:    指定缓存区地址,用于存储接收数据。 
len:    指定的用于接收数据的缓冲区长度。 
flag:   指定的协议,一般设置为0。    


例子:(客户端像服务器下载文件)        
        
*/



/*  主要是建立一个服务器,然后发送一个目录给客户端,客户端发送要下载的文件名,  
    然后将此文件发给客户端。  执行的时候:gcc server.c -o ser      
    ./ser /home/robert/file/  (主要是路径后面加个/) 
*/

#include <stdio.h> 
#include <stdlib.h>
#include <string.h> 
#include <dirent.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>   
void send_dir(char * dirname);  
void send_file(char *filename,char *download_file); 
int cfd;   



#define TCP_BUF_LEN                 			20

void usage(char *command)
{
    printf("usage: %s file direction\n",command);    
    exit(0);
}


int main(int argc,char *argv[]) 
{       
        int nZero = 0; 
        
        if(argc !=2)
        {
            usage(argv[0]);
        }
       
        //创建一个套接字  
        int sfd = socket(AF_INET,SOCK_STREAM,0);  
        if(sfd < 0)  
        {   
            printf("creat socket failed!\n");   return -1;  
        }    
        
        
        //绑定套接字  
        struct sockaddr_in addr_srv;  
        //将端口,协议,IP地址赋值然后绑定到套接字上  
        addr_srv.sin_family = AF_INET;  
        addr_srv.sin_port = htons(6500); 
        addr_srv.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY本地IP地址。 
        
        nZero = TCP_BUF_LEN;
 	    setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (char*)&nZero, sizeof((char*)&nZero));
	    setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, (char*)&nZero, sizeof((char*)&nZero));     
        
        int ret = bind(sfd,(struct  sockaddr*)&addr_srv,sizeof(addr_srv));  
        if(ret < 0)  
        {   
            printf("bind socket failed!\n");   
            
            return -1;  
        }    
         
         //监听  
        ret = listen(sfd,10);  
        if(ret < 0)  
        {   
            printf("listen error!\n");   
            return -1;  
        }
                
        struct sockaddr_in addr_cli; //接收客户端的协议信息  
        socklen_t add_len = sizeof(addr_cli);//char a[30] = "abcd",sizeof(a)=30,strlen(a)=4. 
        char msg[256] = {0};  //为了让服务器一直运行  
        
        while(1)  
       {   
            //连接,获取客户端的协议地址          
            cfd = accept(sfd,(struct sockaddr *)&addr_cli,&add_len);   
            if(cfd < 0)   
            {    
                printf("connect failed!\n");    
                return -1;   
            } 
        
        
         
            printf("send file dir!\n");   
            send_dir(argv[1]);     
         
            recv(cfd,msg,sizeof(msg),0);     
            printf("IP:%s:%s\n",inet_ntoa(addr_cli.sin_addr),msg);    
            send_file(argv[1],msg);
            
            printf("11111111111111111111\n"); 
       }   
       
       close(cfd);  
       close(sfd);  
       return 0; 
            
            
}  

    
           
           
void send_dir(char * dirname) 
{   
        DIR *dir = opendir(dirname);  
        if(dir == NULL)  
        {   
            printf("open file's dir failed!\n");   
            exit(-1);  
        
        }
          
        struct dirent *ptr;          
        char buf[256] = {0};            //存储文件名 
         
         while(ptr=readdir(dir)) 
         {  
            
            if(ptr->d_name[0] != '.')//因为一般文件前面都没有点,有点都是缓存文件   
            {       
                strcpy(buf,ptr->d_name);    
                strcat(buf,"\n");//为了使得一行一个文件名 这样好看点       
                send(cfd,buf,strlen(buf),0);//发送文件名  
            }
            usleep(500);  
         
         } 
        
        memset(buf,0,sizeof(buf));  
        
        usleep(500);  
        strcpy(buf,"$");//$是一个标志,表明文件名称发送完毕。        
        send(cfd,buf,strlen(buf),0);  
        
        closedir(dir); 

}  
           
    
    
    
    
    
           
void send_file(char *filename,char *download_file) 
{  
    DIR *dir = opendir(filename); 
    if(dir == NULL)  
    {   printf("open dir error!\n");   
        exit(-1);   //因为这个函数是没有返回值的,所以要用exit(-1),而不能用return(-1).     
                    //exit(-1)和return(-1)基本上是一样的,就在这个有点区别。  
    }  
        
   struct dirent *ptr;  
   char buf2[200]= {0};      //发送文件内容缓冲区
   
   char cBuf[1024];
   int j;
   for(j=0;j<1000;j++)
   {
        cBuf[j]='5';
    
   }
   
   while(ptr = readdir(dir)) 
   {   
        if(strcmp(ptr->d_name,download_file) == 0)
        //字符数组不能直接比较,必须通过strcmp()函数比较   
        {    
            strcpy(buf2,filename);
            //字符数组不能直接赋值,必须通过strcpy()函数进行赋值    
            strcat(buf2,ptr->d_name);
            //在字符数组后面加字符,需要通过strcat()函数   
            
            FILE *fp = fopen(buf2,"r");    
            if(fp == NULL)    
            {     
                printf("open file error server!\n");     
                exit(-1);    
            } 
            
            memset(buf2,0,sizeof(buf2));//将buf2全部置0 
            
            
            printf("start to send file!\n");    
            int i;
            while(!feof(fp))    
            {     
                fread(buf2,1,20,fp);     
                printf("%s",buf2); 
                printf("strlen(buf2)=%d",strlen(buf2));   
                if(strlen(buf2)<20)
                {
                    memcpy(buf2+10,cBuf,20-strlen(buf2));
                }     
                send(cfd,buf2,strlen(buf2),0); 
                    
               
               
                //printf("server--->%d",i++);
                    
                memset(buf2,0,sizeof(buf2)); 
                //usleep(1000);
                
            }     
            
            printf("file have been send finshed!\n");  
            
            usleep(500);   
            strcpy(buf2,"$$$$$$$$$$$$$$$$$$$$");     
            send(cfd,buf2,strlen(buf2),0);        
            fclose(fp);    
        }    
   }   
    
   closedir(dir);
   
   //printf("2222222222222222222\n"); 



}                                                                




/*
这个是一个客户端,主要功能是连接服务器,到服务器上下载文件。
执行的时候:gcc client.c -o cli
./cli 192.168.26.32(服务器的IP)

*/

#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>



#define TCP_BUF_LEN                 			20

void usage(char *command)
{
    printf("usage: %s server ip\n",command);    
    exit(0);
}

int main(int argc,char *argv[])
{
    
    int nZero = 0;

    if(argc !=2)
    {
        usage(argv[0]);
    }


    //创建套接字
    int cfd;
    cfd = socket(AF_INET,SOCK_STREAM,0);
    if(cfd<0)
    {
        printf("create socket failed!\n");
        exit(-1);
    }    
    

    //连接服务器
    struct sockaddr_in addrcli;
    addrcli.sin_family = AF_INET;       //通信协议
    addrcli.sin_port = htons(6500);     //通信端口
    addrcli.sin_addr.s_addr = inet_addr(argv[1]);   //服务器ip
    
    nZero = TCP_BUF_LEN;
	setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, (char*)&nZero, sizeof((char*)&nZero));
	setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, (char*)&nZero, sizeof((char*)&nZero));

    
    int ret = connect(cfd,(struct sockaddr *)&addrcli,sizeof(addrcli));
    if(ret<0)
    {
        printf("connect server error!\n");
        exit(-1);    
        
    }
    
    //接收文件名称
    char msg[100]={0};
    while(recv(cfd,msg,1,0))
    {
        if(strcmp(msg,"$")==0)
        {
            break;
        }
        
        printf("%s",msg);    
    }
    
    memset(msg,0,sizeof(msg));
    
    //下载需要下载的文件
    printf("Please input you want to download file's name:\n");
    scanf("%s",msg);
    send(cfd,msg,strlen(msg),0);
    
    char c[256]={0};
    strcpy(c,"./");
    strcat(c,msg);
    
    FILE *fp=fopen(c,"w");
    if(fp == NULL)
    {
        printf("open file error!\n");
        exit(-1);
        
    }    
    
    memset(msg,0,100);
    
    printf("Start to recv file:\n");
    int i;
 
    int tmp;
    int val;
    //while(recv(cfd,msg,sizeof(*msg),0))
    while(tmp = recv(cfd,msg,20,0))
    {
        //printf(" in tmp=%d\n",tmp);
        //msg[19]='\0';
        //printf("%s",msg);
        //for(i=0;i<30;i++)
        //{
        //    printf("%c",msg[i]);
        //}
        //printf("\n");
        //msg[20]='\0';
        printf("aaaaaaaaa\n");
        //printf("%s",msg);
        //printf("bbbbbbbbb\n");
        
        if(strcmp(msg,"$$$$$$$$$$$$$$$$$$$$")==0)      //接收完毕
        {
            break;
        }
        
        
        val = fwrite(msg,strlen(msg),1,fp);
        //printf("val =%d\n",val);
        //printf("cccccccccccc\n");
        //printf("client--->%d",i++);
        //printf("dddddddddddd\n");
        
             
     
    }
    //printf("out tmp=%d\n",tmp);
    printf("Recv file finish!\n");
    fclose(fp);
    close(cfd);
    
    return 0;
    
        
        
    

}


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值