socket通信

 什么是Socket?

Socket是一个通信的端点。一对进程在网络直接通过一对socket通信,每个进程一个。

一个socket由一个IP地址和端口号确定。Socket封装了一些操作,使得网络里两个进程的数据通信比较方便。基于TCP协议和UDP协议的socket用得很多。

下图展示了两种方式的通信过程




②建立socket进行通信使用的几个函数

以Linux系统为例,说明一下几个函数

A.  socket()函数

[cpp]  view plain  copy
  1. int socket(int domain, int type, int protocol);  

这个操作类似于打开文件操作,返回socket的socket描述符。

参数:

domain:协议域,又称为协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE。协议族决定了socket的地址类型,通信时采用与其相符的地址,AF_INET用ipv4地址(32位)和16位端口号的组合

type:指定socket类型,常用的有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET,前两个分别对应TCP和UDP类型的socket

protocol:指定协议,常用有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC,协议和socket的类型要匹配。0会选择type对应的默认类型。


B.  bind()函数

[cpp]  view plain  copy
  1. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);  

把一个地址族的特定地址指定给socket,而不是由系统随机分配.

参数:

sockfd:socket描述符,socket()函数返回的int值

addr:一个地址结构体的const指针,指向要绑定给sockfd的地址,结构体的结构和地址协议相符。

如ipv4的地对应的

[cpp]  view plain  copy
  1. struct sockaddr_in {  
  2.    sa_family_t    sin_family; /*地址族: AF_INET */  
  3.    in_port_t      sin_port;   /*网络字节序的端口号 */  
  4.    struct in_addr sin_addr;   /*internet 地址 */  
  5. };  
  6.  /* Internet 地址. */  
  7. struct in_addr {  
  8.    uint32_t       s_addr;     /* 网络字节序的地址 */  
  9. };  

应该注意使用htol,htos函数将主机字节顺序转换为网络字节顺序,避免潜在的错误。

C.  listen()、connect()函数

使用时依次调用socket(),connect(),然后调用listen()来监听socket,客户端调用connect是,服务器就会收到这个请求。

[cpp]  view plain  copy
  1. int listen(int sockfd, int backlog);  
sockfd是要监听的socket的描述符

backlog是这个socket可以排队连接的最大链接个数,也就是这个socket的等待队列的长度。调用listen,socket开始等待客户的链接请求

[cpp]  view plain  copy
  1. int  connect(int sockfd, const structsockaddr *addr, socklen_t addrlen);  
sockfd 是客户端socket描述字

addr为服务器的socket地址

addr_len是socket地址的长度。

客户端通过调用connect函数来建立与TCP服务器的连接。调用listen(),socket开始等待客户的链接请求

 D.  accept()函数

服务器端第四个要调用的函数,服务器收到请求后,用accept接受请求,然后链接就建立了,可以开始读写操作。

[cpp]  view plain  copy
  1. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  

sockfd是服务器socket描述字

addr是指针,用于返回客户端地址

addrlen是协议地址的长度。函数的返回值是内核自动生成的一个全新的描述字,代表一个和客户端的TCP链接


E.read(),write()读写操作

相关函数原型如下所示

[cpp]  view plain  copy
  1.  #include <unistd.h>  
  2.        ssize_t read(int fd, void *buf, size_t count);  
  3.       ssize_t write(int fd, const void *buf, size_t count);  
  4.    
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7.    
  8.       ssize_t send(int sockfd, const void *buf, size_t len, int flags);  
  9.       ssize_t recv(int sockfd, void *buf, size_t len, int flags);  
  10.    
  11.       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,  
  12.                       const struct sockaddr*dest_addr, socklen_t addrlen);  
  13.       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,  
  14.                         struct sockaddr*src_addr, socklen_t *addrlen);  
  15.    
  16.       ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);  
  17.        ssize_t recvmsg(int sockfd, struct msghdr*msg, int flags);  

E.  close()函数

读写完毕后要关闭相应的socket描述字

[cpp]  view plain  copy
  1. #include <unistd.h>  
  2. int close(int fd);  

③代码示例

代码源自http://www.embedu.org/column/column179.htm

TCP socket

TCP 服务端

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <sys/socket.h>  
  4. #include <arpa/inet.h>  
  5. #include <stdlib.h>  
  6. #include <string.h>  
  7. #include <unistd.h>  
  8. #include <netinet/in.h>  
  9. #include <errno.h>  
  10.   
  11.   
  12. #define MAX_MSG_SIZE 256  
  13. #define SERVER_PORT  9987  
  14.   
  15.   
  16. #define BACKLOG 2  
  17.   
  18.   
  19. int GetServerAddr(char * addrname){  
  20.     printf("please input server addr:");  
  21.     scanf("%s",addrname);  
  22.     return 1;   
  23. }  
  24.   
  25.   
  26. int main(){    
  27.     int sock_fd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */    
  28.     struct sockaddr_in ser_addr; /* 本机地址信息 */    
  29.     struct sockaddr_in cli_addr; /* 客户端地址信息 */    
  30.     char msg[MAX_MSG_SIZE];/* 缓冲区*/    
  31.     int ser_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */    
  32.     if(ser_sockfd<0)    
  33.            {/*创建失败 */    
  34.                   fprintf(stderr,"socker Error:%s\n",strerror(errno));    
  35.                   exit(1);    
  36.           }    
  37.     /* 初始化服务器地址*/    
  38.     socklen_t  addrlen=sizeof(struct sockaddr_in);    
  39.     bzero(&ser_addr,addrlen);    
  40.     ser_addr.sin_family=AF_INET;    
  41.     ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);    
  42.     ser_addr.sin_port=htons(SERVER_PORT);    
  43.     if(bind(ser_sockfd,(struct sockaddr*)&ser_addr,sizeof(struct sockaddr_in))<0){ /*绑定失败 */    
  44.             fprintf(stderr,"Bind Error:%s\n",strerror(errno));    
  45.             exit(1);    
  46.     }    
  47.     /*侦听客户端请求*/    
  48.     if(listen(ser_sockfd,BACKLOG)<0){    
  49.         fprintf(stderr,"Listen Error:%s\n",strerror(errno));    
  50.         close(ser_sockfd);    
  51.         exit(1);    
  52.     }    
  53.     while(1){/* 等待接收客户连接请求*/    
  54.         int cli_sockfd=accept(ser_sockfd,(struct sockaddr*) &cli_addr, &addrlen);    
  55.         if(cli_sockfd<=0){    
  56.             fprintf(stderr,"Accept Error:%s\n",strerror(errno));    
  57.         }else{/*开始服务*/    
  58.             recv(cli_sockfd, msg, (size_t)MAX_MSG_SIZE, 0); /* 接受数据*/    
  59.             printf("received a connection from %sn", inet_ntoa(cli_addr.sin_addr));    
  60.             printf("%s\n",msg);/*在屏幕上打印出来 */    
  61.             strcpy(msg,"hi,I am server!");    
  62.             send(cli_sockfd, msg, sizeof(msg),0); /*发送的数据*/    
  63.             close(cli_sockfd);    
  64.         }    
  65.     }    
  66.     close(ser_sockfd);    
  67.     return 0;    
  68.  }    

TCP 客户端

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <sys/types.h>    
  3. #include <sys/socket.h>  
  4. #include <arpa/inet.h>  
  5. #include <stdlib.h>  
  6. #include <string.h>  
  7. #include <unistd.h>  
  8. #include <netinet/in.h>  
  9. #include <errno.h>  
  10.   
  11.   
  12. #define MAX_MSG_SIZE 256  
  13. #define SERVER_PORT  9987  
  14.   
  15.   
  16. int GetServerAddr(char * addrname){    
  17.     printf("please input server addr:");    
  18.     scanf("%s",addrname);    
  19.     return 1;    
  20. }    
  21.   
  22.   
  23. int main(){    
  24.     int cli_sockfd;/*客户端SOCKET */    
  25.     int addrlen;    
  26.     char seraddr[14];    
  27.     struct sockaddr_in ser_addr,/* 服务器的地址*/    
  28.     cli_addr;/* 客户端的地址*/    
  29.     char msg[MAX_MSG_SIZE];/* 缓冲区*/    
  30.     
  31.     GetServerAddr(seraddr);    
  32.     
  33.     cli_sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建连接的SOCKET */    
  34.     
  35.     if(cli_sockfd<0){/*创建失败 */    
  36.         fprintf(stderr,"socker Error:%s\n",strerror(errno));    
  37.         exit(1);    
  38.     }    
  39.     /* 初始化客户端地址*/    
  40.     addrlen=sizeof(struct sockaddr_in);    
  41.     bzero(&ser_addr,addrlen);    
  42.     cli_addr.sin_family=AF_INET;    
  43.     cli_addr.sin_addr.s_addr=htonl(INADDR_ANY);    
  44.     cli_addr.sin_port=0;    
  45.     if(bind(cli_sockfd,(struct sockaddr*)&cli_addr,addrlen)<0){    
  46.         /*绑定失败 */    
  47.         fprintf(stderr,"Bind Error:%s\n",strerror(errno));    
  48.         exit(1);    
  49.     }    
  50.     /* 初始化服务器地址*/    
  51.     addrlen=sizeof(struct sockaddr_in);    
  52.     bzero(&ser_addr,addrlen);    
  53.     ser_addr.sin_family=AF_INET;    
  54.     ser_addr.sin_addr.s_addr=inet_addr(seraddr);    
  55.     ser_addr.sin_port=htons(SERVER_PORT);    
  56.     if(connect(cli_sockfd,(struct sockaddr*)&ser_addr, addrlen)!=0)/*请求连接*/    
  57.     {    
  58.         /*连接失败 */    
  59.         fprintf(stderr,"Connect Error:%s\n",strerror(errno));    
  60.         close(cli_sockfd);    
  61.         exit(1);    
  62.     }    
  63.     strcpy(msg,"hi,I am client!");    
  64.     send(cli_sockfd, msg, sizeof(msg),0);/*发送数据*/    
  65.     recv(cli_sockfd, msg, MAX_MSG_SIZE,0); /* 接受数据*/    
  66.     printf("%s\n",msg);/*在屏幕上打印出来 */    
  67.     close(cli_sockfd);    
  68.     
  69.     return 0;    
  70. }    

UDP Socket

UDP服务端

[cpp]  view plain  copy
  1. int main(){  
  2.     int ser_sockfd;  
  3.     int len;  
  4.     //int addrlen;  
  5.     socklen_t addrlen;  
  6.     char seraddr[100];  
  7.     struct sockaddr_in ser_addr;  
  8.     /*建立socket*/  
  9.     ser_sockfd=socket(AF_INET,SOCK_DGRAM,0);  
  10.     if(ser_sockfd<0){  
  11.         printf("I cannot socket success\n");  
  12.         return 1;  
  13.      }  
  14.     /*填写sockaddr_in 结构*/  
  15.     addrlen=sizeof(struct sockaddr_in);  
  16.     bzero(&ser_addr,addrlen);  
  17.     ser_addr.sin_family=AF_INET;  
  18.     ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
  19.     ser_addr.sin_port=htons(SERVER_PORT);  
  20.     /*绑定客户端*/  
  21.     if(bind(ser_sockfd,(struct sockaddr *)&ser_addr,addrlen)<0){  
  22.         printf("connect");  
  23.         return 1;  
  24.     }  
  25.     while(1){  
  26.         bzero(seraddr,sizeof(seraddr));  
  27.         len=recvfrom(ser_sockfd,seraddr,sizeof(seraddr),0,(struct sockaddr*)&ser_addr,&addrlen);  
  28.         /*显示client端的网络地址*/  
  29.         printf("receive from %s\n",inet_ntoa(ser_addr.sin_addr));  
  30.         /*显示客户端发来的字串*/  
  31.         printf("recevce:%s",seraddr);  
  32.         /*将字串返回给client端*/  
  33.         sendto(ser_sockfd,seraddr,len,0,(struct sockaddr*)&ser_addr,addrlen);  
  34.     }  
  35.   
  36.     return 0;  
  37. }  

UDP客户端

[cpp]  view plain  copy
  1. int GetServerAddr(char * addrname){  
  2.     printf("please input server addr:");  
  3.     scanf("%s",addrname);  
  4.     return 1;  
  5. }  
  6. int main(int argc,char **argv){  
  7.     int cli_sockfd;  
  8.     int len;  
  9.     socklen_t addrlen;  
  10.     char seraddr[14];  
  11.     struct sockaddr_in cli_addr;  
  12.     char buffer[256];  
  13.     GetServerAddr(seraddr);  
  14.     /* 建立socket*/  
  15.     cli_sockfd=socket(AF_INET,SOCK_DGRAM,0);  
  16.     if(cli_sockfd<0){  
  17.         printf("I cannot socket success\n");  
  18.         return 1;  
  19.     }  
  20.     /* 填写sockaddr_in*/  
  21.     addrlen=sizeof(struct sockaddr_in);  
  22.     bzero(&cli_addr,addrlen);  
  23.     cli_addr.sin_family=AF_INET;  
  24.     cli_addr.sin_addr.s_addr=inet_addr(seraddr);  
  25.     //cli_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
  26.     cli_addr.sin_port=htons(SERVER_PORT);  
  27.   
  28.     bzero(buffer,sizeof(buffer));  
  29.     /* 从标准输入设备取得字符串*/  
  30.     len=read(STDIN_FILENO,buffer,sizeof(buffer));  
  31.     /* 将字符串传送给server端*/  
  32.     sendto(cli_sockfd,buffer,len,0,(struct sockaddr*)&cli_addr,addrlen);  
  33.     /* 接收server端返回的字符串*/  
  34.     len=recvfrom(cli_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&cli_addr,&addrlen);  
  35.     //printf("receive from %s\n",inet_ntoa(cli_addr.sin_addr));  
  36.     printf("receive: %s",buffer);  
  37.     close(cli_sockfd);  
  38.     return 0;  
  39. }  



socket中的TCP_IP的三次握手与四次挥手:


(一)socket中TCP的三次握手建立连接:


(1)tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:


客户端向服务器发送一个SYN J
服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
客户端再想服务器发一个确认ACK K+1
只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:


(2)示意图:


(3)步骤:
1.当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;
2.服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;
3.客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。


(4)总结:
客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。
--------------
(二)socket中TCP的四次挥手释放连接:


(1)示意图:


(2)过程:
1.某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
2.另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
3.一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
4.接收到这个FIN的源发送端TCP对它进行确认。


说明:

本文由giantpoplar发表于CSDN

文章地址 http://blog.csdn.net/giantpoplar/article/details/47657303

转载请保留本说明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值