目录

一、TCP/UDP的区别

二、TCP通信流程:

三、socket地址

四、IP地址转换函数(字符串ip-整数,主机、网络字节序的转换)

五、服务器端和客户端相关的函数

一、TCP/UDP的区别

UDP:用户数据协议,面向无连接,可以单播、多播、广播,面向数据报,不可靠;

TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输。


UDP和TCP的区别
区别UDPTCP
是否创建连接无连接面向连接
是否可靠不可靠可靠的
连接对象的个数一对一、一对多、多对一、多对多仅支持一对一
传输方式面向数据报面向字节流
首部开销8个字节最少20个字节
适用场景实时应用(视频会议、直播)可靠性高的场合(文件传输)

二、TCP通信流程:

服务器端:被动连接的角色

1、创建一个用于监听的套接字

        -监听:监听有客户端的连接

        -套接字:其实就是一个文件描述符

2、将监听的文件描述符和与本地的IP和端口号绑定(IP和端口号:服务器的地址信息) 

        -客户端连接服务器的时候使用的就是这个IP和端口

3、设置监听,监听的fd开始工作

4、阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd)

5、通信

        -接收数据

        -发送数据

6、通信结束,断开连接。

客户端的通信流程

1、创建一个用于通信的套接字(fd);

2、连接服务器,需要指定连接的服务器的IP和端口;

3、连接成功,客户端可以直接和服务器通信;

        -接收数据

        -发送数据

4、通信结束,断开连接。

三、socket地址

sockaddr_in 的结构体:

struct sockaddr_in
{
    sa_family_t        sin_family;     //地址族(Address Family)一般选择AF_INET
    uint16_t           sin_port;       //16位TCP/UDP端口
    struct in_addr     sin_addr;       //32位IP地址
    char               sin_zero[8];    //不使用
};

struct int_addr
{
    int_addr_t s_addr;//int 类型 占4个字节
};

四、IP地址转换函数(字符串ip-整数,主机、网络字节序的转换)

通常,人们习惯用可读性好的字符串来表示IP地址,比如用点分十进制字符串表示IPv4地址,以及用十六进制字符串表示IPv6地址。但是编程中我们需要先把他们转化为整数(二进制数)方能使用。而记录日志的时候则相反,我们要把整数表示的IP地址转化为可读的字符串。下面的3个函数可用于用点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换。

#include <arpa/inet.h>
int inet_pton(int af,const char *src,void *dst);
// p:点分十进制IP字符串,n:network,网络字节序的整数
-af:地址族     AF_INET    AF_INET6
-src:需要转换的点分十进制的IP字节串
-dst:转换后的结果保存在这个里面 
char *inet_ntop(int af,const void *src,char *dst,socklen_t size);
-af:地址族,AF_INET     AF_INET6
-src:网络字节序整数存放的地方
-dst:要转换的ip地址字符串保存的地方
-size:第三个参数的大小(数组的大小)
-返回值:返回转换后的数据的地址(字符串),和dst是一样的

 用代码举个例子

  1 #include <stdio.h>
  2 #include <arpa/inet.h>
  3 int main()
  4 {
  5         //1、创建一个ip字符串,点分十进制IP字符串
  6         char buf[]="192.168.17.110";
  7         unsigned int num=0;
  8         inet_pton(AF_INET,buf,&num);//将点分十进制的IP字符串转换成网络字节序
  9 
 10         unsigned char *p=(unsigned char *)&num;
 11         printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));
 12 
 13         //将网络字节序的IP整数转换成点分十进制字符串
 14         char ip[16]="";
 15         const char *str=inet_ntop(AF_INET,&num,ip,16);
 16         printf("str:%s\n",str);
 17         printf("ip:%s\n",ip);
 18         return 0;
 19 
 20 }

 输出结果为:

 由结果可知,按照这样的使用规则是正确的。

五、服务器端和客户端相关的函数

(1)服务器端

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <string.h>
  5 #include <arpa/inet.h>
  6 int main()
  7 {
  8         //1、创建socket(用于监听的套接字)
  9         int lfd=socket(AF_INET,SOCK_STREAM,0);
 10         if(lfd==-1)
 11         {
 12                 perror("socket");
 13                 exit(-1);
 14         }
 15         //2、绑定
 16         struct sockaddr_in saddr;
 17         saddr.sin_family = AF_INET;
 18         //inet_pton(AF_INET,"192.168.17.134", saddr.sin_addr.s_addr);
 19         saddr.sin_addr.s_addr=0;//0.0.0.0或者INADDR_ANY
 20         saddr.sin_port=htons(9999);
 21         int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
 22         if(ret==-1)
 23         {
 24                 perror("bind");
 25                 exit(-1);
 26         }
 27         //3、监听
 28         ret=listen(lfd,8);//同时能接收8个连接
 29         if(ret==-1)
 30         {
 31                 perror("listen");
 32         }
 33         //4、接收客户端连接
 34         struct sockaddr_in clientaddr;
 35         socklen_t len=sizeof(clientaddr);
 36         int num=accept(lfd,(struct sockaddr *)&clientaddr,&len);
 37         if(num==-1)
 38         {
 39                 perror("accept");
 40                 exit(-1);
 41         }
 42         //输出客户端信息
 43         char clientIP[16];
 44         inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
 45         unsigned short clientPort = ntohs(clientaddr.sin_port);
 46         printf("client ip is %s,port is %d\n",clientIP,clientPort);
 47         //5、获取客户端的数据
 48         //给客户端发送数据
 49         char recvBuf[1024]={0};
            while(1)
            {
    
 50         int fd=read(num,recvBuf,sizeof(recvBuf));
 51         if(fd==-1)
 52         {
 53                 perror("read");
 54                 exit(-1);
 55         }
 56         else if(fd>0)
 57         {
 58                 printf("recv client data :%s\n",recvBuf);
 59         }
 60         else if(len==0)
 61         {
 62                 //表示客户端断开连接
 63                 printf("client close...");
 64         }
 65         char *data="hello , i am server";
 66         write(num,data,strlen(data));
            }
 67         close(lfd);
 68        
 69         return 0;
 70 }

(2)客户端

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <string.h>
  5 #include <arpa/inet.h>
  6 int main()
  7 {
  8         //1、创建socket
  9         int lfd=socket(AF_INET,SOCK_STREAM,0);
 10         if(lfd==-1)
 11         {
 12                 perror("socket");
 13                 exit(-1);
 14         }
 15         //2、连接服务器
 16         struct sockaddr_in serveraddr;
 17         serveraddr.sin_family=AF_INET;
 18         inet_pton(AF_INET,"192.168.17.134",&serveraddr.sin_addr.s_addr);
 19         serveraddr.sin_port=htons(9999);
 20         int ret=connect(lfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
 21         if(ret==-1)
 22         {
 23                 perror("connect");
 24                 exit(-1);
 25         }
 26         //3、通信
 27         //给客户端发送数据
 28         char recvBuf[1024]={0};
 29         while(1)
 30         {
 31         char *data="hello , i am client";
 32         write(lfd,data,strlen(data)+1);
 33         sleep(1);
 34         //接收服务器发来的数据  
 35         int len=read(lfd,recvBuf,sizeof(recvBuf));
 36         if(len==-1)
 37         {
 38                 perror("read");
 39                 exit(-1);
 40         }
 41         else if(len>0)
 42         {
 43                 printf("recv server data:%s\n",recvBuf);
 44         }
 45         else if(len==0)
 46         {
 47                 //与服务器断开
 48                 printf("server closed ...\n");
 49         }
 50         }
 51         //关闭文件描述符
 52         close(lfd);
 53 //      close(len);
 54 
 55 
 56 
 57         return 0;
 58 }

然后开始运行,运行时一定要先运行服务器端,再运行客户端,否则会报错。

服务器端运行结果为: 

客户端运行结果为: 

我在写服务器端的时候遇到了一个小插曲,就是我在写read()函数和read()函数时,把第一个位置写成了socket的返回值,发现客户端一直不能接收来自服务端的消息,后来我把这个地方改成 accept()函数的返回值后就一切正常了。

真的受教了!

本内容为合法授权发布,文章内容为作者独立观点,不代表开发云立场,未经允许不得转载。

CSDN开发云