TCP程序设计

来源:互联网 发布:windows配置失败 编辑:程序博客网 时间:2024/05/14 17:41

Socket和基本结构体

Linux中的网络编程通过Socket(套接字)实现,Socket是一种文件描述符。

Socket有三种类型:

流式套接字(SOCK_STREAM):使用TCP协议。

数据报套接字(SOCK_DGRAM):使用UDP协议。

原始套接字(SOCK_RAW):使用IP协议,主要用于新的网络协议的测试等。

网络地址

在socket程序设计当中,struct sockaddr用于记录网络地址:

struct sockaddr

{

    u_short sa_family;

    char sa_data[14];

}

sa_family:协议族,采用“AF_XXX”的形式,如AF_INET(IP协议族)。

sa_data:14字节的特定协议地址。

地址结构

struct sockaddr_in

{

    short int sa_family;   /*协议族*/

    unsigned short int sin_port;  /*端口号*/

    struct in_addr sin_addr;        /*协议特定地址*/

    unsigned char sin_zero[8];     /*填0*/

}

注意:在一般应用中,一般不会使用sockaddr,一般使用sockaddr_in,因为其操作简单。

typedef struct in_addr

{

    union

    {

        struct

           {

                unsigned char s_b1,s_b2,s_b3,s_b4;

           }S_un_b;

           struct

          {

                unsigned short s_w1,s_w2; 

           }S_un_w;

         unsigned long S_addr;   /*通常在联合体当中使用这个结构体,此为32位无符号整数来记录IP地址(假设为IP协议)

     }S_un;

}

地址转换

IP地址通常由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用以下两个函数:

int inet_aton(const char *cp,struct in_addr *inp)

char *inet_ntoa(struct in_addr *in)

函数里面a代表ascii(字符),n代表network。

inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。inet_ntoa是将32位IP地址转换为a.b.c.d的格式。

字节序转换

不同类型的CPU对变量的字节存储顺序可能不同:(低字节先传输为big endian),有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据顺序是一定要统一的。所以当内部字节顺序和网络字节顺序不同是,必须要进行转换。

字节序转换函数

htons

把unsigned short 类型从主机序转换到网络序

htonl

把unsigned long类型从主机序转换到网络序

ntohs

把unsigned short 类型从网络序转换到主机序

ntohl

把unsigned long类型从网络序转换到主机序

IP与主机名:

在网络中标识一台主机可以用IP地址,也可以使用主机名。

struct hostent *gethostbyname(const char *hostname)

struct hostent

{

    char *h_name;            /*主机的正式名称*/

    char *h_aliases;          /*主机的别名*/

    int h_addrtype;             /*主机的地址类型 AF_INET*/

    int h_length;                  /*主机的地址长度*/

    char **h_addr_list;        /*主机的IP地址列表*/

}

#define h_addr h_addr_list[0];   /*主机的第一个IP地址*/

基于TCP的网络编程

基于TCP-服务器

1 创建一个socket,用函数socket();

2 绑定IP地址、端口等信息到socket上,用函数bind();

3 设置最大的允许的连接数,用listen();

4 等待客户端的连接请求,用函数accept(),如果没有连接请求则程序阻塞在accept处。

5 收发数据,用函数send()和recv(),或者read()和write();

6 关闭网络连接;

基于TCP-客户端

1 创建一个socket,用函数socket();

2 设置要连接的服务器的IP地址和端口属性

3 连接服务器,用函数connect();

4 收发数据,用函数send()和recv(),或者read()和write();

5 关闭网络连接;

TCP通信实例如下:

1 TCP服务器程序:

[cpp] view plaincopy
  1. /********************************************************** 
  2. *实验要求:   建立基于TCP协议的服务器与客户端的连接。客户端向服务器发送 
  3. *           字符串,服务器将收到的字符串打印出来。 
  4. *功能描述:   根据套接字建立TCP连接的过程,创建服务端程序,并在服务端等 
  5. *           待接收客户端的数据,并打印到终端上。 
  6. *日    期:   2010-9-17 
  7. *作    者:   国嵌 
  8. **********************************************************/  
  9. #include <stdlib.h>   
  10. #include <stdio.h>   
  11. #include <errno.h>   
  12. #include <string.h>   
  13. #include <netdb.h>   
  14. #include <sys/types.h>   
  15. #include <netinet/in.h>   
  16. #include <sys/socket.h>   
  17.   
  18. #define portnumber 3333  
  19.   
  20. /* 
  21. * 程序入口 
  22. * */  
  23. int main(int argc, char *argv[])   
  24. {   
  25.     int sockfd,new_fd;   
  26.     struct sockaddr_in server_addr;   
  27.     struct sockaddr_in client_addr;   
  28.     int sin_size;   
  29.     int nbytes;  
  30.     char buffer[1024];  
  31.       
  32.   
  33.     /* 服务器端开始建立sockfd描述符 */   
  34.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP  
  35.     {   
  36.         fprintf(stderr,"Socket error:%s\n\a",strerror(errno));   
  37.         exit(1);   
  38.     }   
  39.   
  40.     /* 服务器端填充 sockaddr结构 */   
  41.     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0  
  42.     server_addr.sin_family=AF_INET;                 // Internet  
  43.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  // (将本机器上的long数据转化为网络上的long数据)服务器程序能运行在任何ip的主机上  //INADDR_ANY 表示主机可以是任意IP地址,即服务器程序可以绑定到所有的IP上  
  44.     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip  
  45.     server_addr.sin_port=htons(portnumber);         // (将本机器上的short数据转化为网络上的short数据)端口号  
  46.       
  47.     /* 捆绑sockfd描述符到IP地址 */   
  48.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)   
  49.     {   
  50.         fprintf(stderr,"Bind error:%s\n\a",strerror(errno));   
  51.         exit(1);   
  52.     }   
  53.   
  54.     /* 设置允许连接的最大客户端数 */   
  55.     if(listen(sockfd,5)==-1)   
  56.     {   
  57.         fprintf(stderr,"Listen error:%s\n\a",strerror(errno));   
  58.         exit(1);   
  59.     }   
  60.   
  61.     while(1)   
  62.     {   
  63.         /* 服务器阻塞,直到客户程序建立连接 */   
  64.         sin_size=sizeof(struct sockaddr_in);   
  65.         if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)   
  66.         {   
  67.             fprintf(stderr,"Accept error:%s\n\a",strerror(errno));   
  68.             exit(1);   
  69.         }   
  70.   
  71.         fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串  
  72.         if((nbytes=read(new_fd,buffer,1024))==-1)   
  73.         {   
  74.             fprintf(stderr,"Read Error:%s\n",strerror(errno));   
  75.             exit(1);   
  76.         }         
  77.         buffer[nbytes]='\0';  
  78.         printf("Server received %s\n",buffer);  
  79.         /* 这个通讯已经结束 */   
  80.         close(new_fd);   
  81.         /* 循环下一个 */   
  82.     }   
  83.   
  84.     /* 结束通讯 */   
  85.     close(sockfd);   
  86.     exit(0);   
  87. }  


2 TCP客户端程序:

[cpp] view plaincopy
  1. /********************************************************** 
  2. *实验要求:   建立基于TCP协议的服务器与客户端的连接。客户端向服务器发送 
  3. *           字符串,服务器将收到的字符串打印出来。 
  4. *功能描述:   根据套接字建立TCP连接的过程,创建客户端程序,并在与服务端 
  5. *           建立连接后,向其发送指定字符串。 
  6. *日    期:   2010-9-17 
  7. *作    者:   国嵌 
  8. **********************************************************/  
  9. #include <stdlib.h>   
  10. #include <stdio.h>   
  11. #include <errno.h>   
  12. #include <string.h>   
  13. #include <netdb.h>   
  14. #include <sys/types.h>   
  15. #include <netinet/in.h>   
  16. #include <sys/socket.h>   
  17.   
  18. #define portnumber 3333  
  19.   
  20. /* 
  21. * 程序入口 
  22. * */  
  23. int main(int argc, char *argv[])   
  24. {   
  25.     int sockfd;   
  26.     char buffer[1024];   
  27.     struct sockaddr_in server_addr;   
  28.     struct hostent *host;   
  29.   
  30.     if(argc!=2)   
  31.     {   
  32.         fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);   
  33.         exit(1);   
  34.     }   
  35.   
  36.     /* 使用hostname查询host 名字 */  
  37.     if((host=gethostbyname(argv[1]))==NULL)   
  38.     {   
  39.         fprintf(stderr,"Gethostname error\n");   
  40.         exit(1);   
  41.     }   
  42.   
  43.     /* 客户程序开始建立 sockfd描述符 */   
  44.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP  
  45.     {   
  46.         fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));   
  47.         exit(1);   
  48.     }   
  49.   
  50.     /* 客户程序填充服务端的资料 */   
  51.     bzero(&server_addr,sizeof(server_addr)); // 初始化,置0  
  52.     server_addr.sin_family=AF_INET;          // IPV4  
  53.     server_addr.sin_port=htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号  
  54.     server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址  
  55.       
  56.     /* 客户程序发起连接请求 */   
  57.     if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)   
  58.     {   
  59.         fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));   
  60.         exit(1);   
  61.     }   
  62.   
  63.     /* 连接成功了 */   
  64.     printf("Please input char:\n");  
  65.       
  66.     /* 发送数据 */  
  67.     fgets(buffer,1024,stdin);   
  68.     write(sockfd,buffer,strlen(buffer));   
  69.     /* 结束通讯 */   
  70.     close(sockfd);   
  71.     exit(0);   
  72. }  


运行结果:

先运行TCP服务器程序

[root@localhost TCP]# ./tcp_server

/****阻塞处*****/

发生阻塞,阻塞发生在tcp_server.c中的accept函数处

然后运行TCP客户端程序重新开一个终端:

[root@localhost TCP]# ./tcp_client 192.168.0.102                       /*192.168.0.102为服务器地址*/

Please input char:

一旦连接后,主机端发生变化,如下:

[root@localhost TCP]# ./tcp_server

Server get connection from 192.168.0.102

/****阻塞处*****/

同时发生阻塞,阻塞发生在TCP_server.c中的read函数中,由于此时客户端没有向服务器发送数据

接下来我们从客户端发送数据:

[root@localhost TCP]# ./tcp_client 192.168.0.102                      

Please input char:

1234567

则服务器端也发生相应变化如下:

[root@localhost TCP]# ./tcp_server

Server get connection from 192.168.0.102

Server received 1234567

/*****阻塞处****/

又发生阻塞,阻塞在accept处,因为此时tcp_server.c中的while循环一次结束,关闭连接,又循环到accept处。依照此方法不断发送与接收数据。

 

0 0
原创粉丝点击