网络编程套接字的应用之TCP和UDP

 

一、UDP

 

 

1、总结:

server:

 

(1)创建socket

 

(2)绑定端口号

 

(3)循环的读取数据

 

(4)针对读取到的数据进行计算与处理

 

(5)把处理后的结果发送给客户端

 

client:

 

(1)创建socket

 

(2)给服务器发送请求

(3)从服务器中读取结果

2、实现代码:
服务器端

1、尝试从socket中读取数据(进行一系列的计算)
2、把字符串原封不动的写会数据端
//端口号的绑定(1025-65535)[1-1024为知名端口号,预留给操作系统,若要使用,需要在root用户下]
//./server 192.168.80.132 9090
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
# include<netinet/in.h>
int main(int argc,char* argv[])
{
    if(argc != 3)//判断命令行参数够不够
    {
        printf("Usage ./server [ip] [port]\n");
        return 1;//退出,结果错误
    }
//创建socket文件描述符
    int fd = socket(AF_INET,SOCK_DGRAM,0);//AF_INET:IPV4版本;SOCK_DGRAM:数据报
    if(fd < 0)//文件描述符是有限的,创建完了,则失败。文件描述符可通过(ulimit -a)查看,
//查看的结果都保存PCB中,bash进程可继承。也可以通过(ulimit -n 个数)来进行修改。所以可以通过ulimit查看并修改文件描述符的个数
//对于一个进程,栈有多大,同ulimit
    {                                                                            
        perror("socket");
        return 1;
    }
//把点分十进制的ip地址转化为uint-32的ip地址
//把主机序转化为网络字符序
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;//添加协议族
    addr.sin_addr.s_addr = inet_addr(argv[1]);//把点分十进制的ip地址转化为uint-32的ip地址
    addr.sin_port = htons(atoi(argv[2]));//把主机序转化为网络字符序
  int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret < 0){//如果一个端口号已被替换,则bind失败
        perror("bind");
        return 1;
    }
    while(1){//死循环,反复从客户端接收信息
//读
         char buf[1024] = {0};
         struct sockaddr_in peer;//对端的地址,输出型参数,不用进行初始化
        socklen_t len = sizeof(peer);
        ssize_t read_size = recvfrom(fd,buf,sizeof(buf) - 1,0,(struct sockaddr*)&peer,&len);//
//recvfrom中第三个参数为缓冲区的长度;第四个参数叫做flag默认为0;第五个(输出型的参数,表示
ip地址的端口号,一旦返回就可以把客户端的ip地址和端口号拿到结构中,获取对端的i地址和端口号)和第六个参数(输入输出型参数:传入时,返回缓冲区的长度;传出时返回结构体的长度)
//recfrom的返回值为ssize_t:表示有符号的长整型
        if(read_size < 0)//执行失败,继续执行下一步
        {
             //filure                                                                                  
             perror("recvfrom");
             continue;
         }
         buf[read_size] = '\0';//双重保证,保证缓冲区中的内容以'\0'结束
        printf("client %s:%d say:%s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);//接受完毕
//inet_ntoa:从点分十进制的ip地址转换为字符串
//ntohs(peer.sin_port):返回端口号
//返回客户端:发送多大,接受多大的值,所以使用strlen,而不是使用sizeof
        sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&peer,len);//len:输入型参数,不用取地址//struct sockaddr*:指针强转
    }
    close(fd);
    return 0;
}

客户端代码:

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
// ./client 127.0.0.1 9090   127.0.0.1:环回ip自己的ip【发送数据、接收数据的人都是自己】
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        printf("Usage ./server [ip] [port]\n");
        return 1;
    }
 //创建socket
    int fd = socket(AF_INET,SOCK_DGRAM,0);
    if(fd < 0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in server_addr;
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=inet_addr(argv[1]);
    server_addr.sin_port=htons(atoi(argv[2]));
//客户端不用绑定端口号,是因为操作系统会在程序运行时自动分配端口号。
//由于客户端的机器装了哪些程序不确定。如果强行绑定端口号,就可能和客户端的
//程序冲突,操作系统就会在通信时自动分配一个端口号。
//服务器端必须绑定端口号,如果不绑定,操作系统自动分配,客户端不知道
//端口号是谁,则无法发送请求。所以服务器一定要绑定端口号。
//进行数据传输
    while(1){
         char buf[1024] = {0};
//调用read函数从标准输入读数据
        ssize_t read_size =read(fd,buf,sizeof(buf) - 1);//
        if(read_size < 0)
        {
             perror("recvfrom");
             return 1;
         }
       if(read_size==0){
            //read返回0意味着读到了eof文件结束标志,读完了
            printf("read done\n");
            return 0;//代码执行完毕,并且结果正确
       }
       buf[read_size]='\0';
       sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
    //读数据
    char buf_output[1024]={0};
    read_size=recvfrom(fd,buf_output,sizeof(buf_output)-1,0,NULL,NULL);
//最后两个参数为空是因为一定知道是从哪儿来的,一定是经过server服务器来的
//服务器的ip地址和端口号已经得到了
    if(read_size<0){
       perror("recvfrom");
       return 1;
    }
    buf_output[read_size]='\0';
    printf("server response :%s\n",buf_output);
  }
    close(fd);
    return 0;
}

//服务器:循环的读数据,然后将数据写会socket
//客户端:循环的从标准输入读,写给服务器,然后再尝试从服务器读,最后写到标准输出
//

makefile中的代码:

.PHONY:all
all:server client

server:server.c
        gcc $^ -o $@
client:client.c
        gcc $^ -o $@

.PHONY:clean
clean:
        rm server client

先启动服务器,因为服务器是等待接收数据(若是先启动客户端,发送的数据不知道发送给谁了)
直接输入./server,提示打开方式不对:应该说明IP地址和端口号

输入ip地址和端口号后:

服务器启动完毕

验证服务器启动完毕:重新开一个窗口输入命令:netstat -anp | grep 9090

全0的ip地址表示任何的IP地址,相当于通配符。0.0.0.0:*表示对ip地址和端口号都没有限制
在ip地址相同的情况下,不能同时启动多个端口号也相同的服务器端(同样的ip地址和端口号不能被一起绑定)
启动客户端:./client ip地址 端口号
此处的IP地址和端口号必须和服务器端的IP地址和端口号相同,如果不匹配,则无法运行结果

在虚拟机上进行测试:

(1)设置---网络适配器必须设置为桥接模式;

(2)还必须保证服务器和客户端对应的主机在同一个局域网内【两台主机都在同一个路由器上,同一个手机热点】

(3)关闭服务器端的防火墙(在网络安全的角度上进制一些有嫌疑的程序、非法的程序访问主机

虚拟机的IP地址也是局域网的IP地址,只能在局域网内使用,或者使用云服务器。

*******拷贝到云服务器的方式:
(1)sz  程序名:把这个程序先拷贝到桌面上,将这个可执行程序从桌面拖到云服务器上;再加上可执行权限
;接着再云服务器中输入:./server 0 9090 此时的0表示任何一个IP地址【一个主机可能会有多个IP地址,,从任何一个IP地址过来都可以】
./client IP地址                     此时的IP地址是指服务器的外网IP
虚拟机的IP地址和云服务器感知的IP地址不同,是因为NAT机制

TCP:

服务器端:

# include<stdio.h>                                                                                   
  # include<stdlib.h>                                                                                  
  # include<unistd.h>                                                                                  
  # include<arpa/inet.h>                                                                               
  # include<sys/socket.h>                                                                              
  # include<string.h>                                                                                  
  # include<netinet/in.h>                                                                              
  typedef struct sockaddr sockaddr;                                                                    
  typedef struct sockaddr_in sockaddr_in;                                                              
                                                                                                       
  //./server 0 9090                                                                                    
  int main(int argc,char* argva[])                                                                     
  {                                                                                                    
    if(argc!=3){                                                                                       
      printf("Usage ./server [ip][port]\n");                                                           
      return 1;            
    }                                                                                                  
    //创建socket                                                                                       
    int fd=socket(AF_INET,SOCK_STREAM,0);                                                              
    if(fd<0)                                                                                           
    {                                                                                                  
      perror("socket");                                                                                
      return 1;                                                                                        
    }                                                                                                  
    sockaddr_in addr;                                                                                  
    addr.sin_family=AF_INET;                                                                           
    addr.sin_addr.s_addr=inet_addr(argv[1]);                                                           
E>  addr.sin_port=htons(atoi(argv[2]));
    int ret=bind(fd,(sockaddr*)&addr,sizeof(addr));
    if(ret<0){
      perror("bind");
      return 1;
    }                                                                                                  
    //把tcp socket变成被动的模式。此时才能允许客户端来建立连接                                         
    ret=listen(fd,5);                                                                                  
    if(fd<0){                                                                                          
      perror("listen");                                                                                
      return 1;                                                                                        
    }                                                                                                  
    while(1){                                                                                          
      sockaddr_in peer;                                                                                
      socklen_t len=sizeof(peer);                                                                      
      int new_fd=accept(fd,(sockaddr *)&peer,&len);                                                    
      if(new_fd<0){
        perror("accept");
        continue;
      }
      while(1){
        char buf[1024]={0};
        ssize_t read_size=read(new_fd,buf,strlen(buf));
        if(read_size<0){
          perror("read");                                                                              
          return 1;                                                                                    
        }                                                                                              
        if(read_size==0)                                                                               
        {                                                                                              
          //读到了EOF,对于此场景下,意味着客户端关闭了 socket;                                       
          printf("read done");                                                                         
          //此处这个关闭,一定不要忘记,否则就会出现文件描述符的泄漏                                   
          //并且在tcp中能够看到close_wait状态                                                          
          close(new_fd);                                                                               
          break; 
        }                                                                                              
        buf[read_size]='\0';                                                                           
        printf("client %s:%d say:%s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);             
        write(fd,buf,strlen(buf));                                                                     
      }                                                                                                
    }                                                                                                  
    return 0;
  }

客户端:

# include<stdio.h>                                                                                   
  # include<stdlib.h>                                                                                  
  # include<string.h>                                                                                  
  # include<sys/socket.h>                                                                              
  # include<netinet/in.h>                                                                              
  # include<arpa/inet.h>                                                                               
  typedef struct sockaddr sockaddr;                                                                    
  typedef struct sockaddr_in sockaddr_in;                                                              
                                                                                                       
  int main(int argc,char* argv[])                                                                      
  {                                                                                                    
    if(argc!=3)                                                                                        
    {                                                                                                  
      printf("Usage ./server[ip][port]");                                                              
      return 1;                                                                                        
    }         
    int fd=socket(AF_INET,SOCK_STREAM,0);                                                              
    if(fd<0)                                                                                           
    {                                                                                                  
      perror("socket");                                                                                
      return 1;                                                                                        
    }                                                                                                  
    sockaddr_in server_addr;                                                                           
    server_addr.sin_family=AF_INET;                                                                    
    server_addr.sin_addr.s_addr=inet_addr(argv[1]);                                                    
    server_addr.sin_port=htons(atoi(argv[2]));                                                         
    int ret=connect(fd,(sockaddr*)&server_addr,sizeof(server_addr));                                   
    if(ret<0){
    perro("connect");
      return 1;
    }
    while(1){
   char buf[1024]={0};                                                                              
    ssize_t read_size=read(0,buf,sizeof(buf)-1);                                                     
      if(read_size<0)                                                                                  
      {                                                                                                
        perror("read");                                                                                
        return 1;                                                                                      
      }                                                                                                
      if(read_size==0){                                                                                
        printf("read done");                                                                           
        return 0;                                                                                      
      }                                                                                                
      buf[read_size]='\0';
    write(fd,buf,strlen(buf));
      char buf_output[1024]={0};
    read_size=read(fd,buf_output,sizeof(buf_output)-1);
      if(read_size<0){
        perror("read");                                                                                
        return 1;                                                                                      
      }                                                                                                
      if(read_size==0){                                                                                
        //服务器率先关闭了new_sock                                                                     
        printf("read done");                                                                           
        return 0;                                                                                      
      }                                                                                                
      buf_output[read_size]='\0';                                                                      
      printf("server resp:%s\n",buf_output);                                                           
    }
  close(fd);
    return 0;
  }

总结:

客户端:
1、从标准输入中读取用户输入
2、把这个字符串通过socket传递给服务器
3、尝试从服务器读取数据
4、把从服务器读取到的数据显示到标准输出上

//客户端一般不用绑定端口号,操作系统在程序运行时,自动分配。由于客户端机器装了哪些程序不确定。
//如果强行绑定端口号,就可能和客户端的其他程序冲突
//数据传输

//read等于0表示读到了EOF

//端口号通过服务器读来,客户端知道,所以不用设置端口号,置为NULL

 

二、TCP

实现tcp多进程和多线程的版本,多线程的系统开销比较小,但是反复创建进程和线程的情况下,还是存在很大的开销。

还可以用生产者,消费者模型构建,用一个专门的线程作为生成者模型,只用负责从socket中读取数据,放到一个队列里;有若干个消费者线程来复用队列中的数据,通过队列中的数据进行一系列计算。
1、TCP协议的特点:
1)、有连接;TCP建立连接、断开连接的过程(重要)
TCP建立连接的过程:
1‘两次握手:可能会发生丢包
2’三次握手:发送方发送信息,对方返回信息(收到这个消息,对这个消息的应答),发送方发送收到了响应的消息。【发送方与服务器端建立连接,服务器端进行响应;服务器端和发送端建立连接,发送端对服务器端进行响应;双方都尝试对对方进行连接,并进行响应】

TCP断开连接的过程:四次挥手:
1‘

2)、可靠传输
3)、面向字节流

2、小节:

server:

 

(1)创建socket

 

(2)绑定端口号

 

(3)把socket转换成被动模式(listen)

 

(4)循环的进行accept

 

(5)从accept返回的new_fd中读取客户端的请求

 

(6)根据读取到的请求进行计算和处理

 

(7)把处理后的结果发送给客户端

【多进程,多线程,IO多路复用】

 

client:

 

(1)创建socket

 

(2)和服务器建立连接

 

(3)给服务器发送数据

 

(4)从服务器读取返回的结果

(5)和服务器断开连接

3、实现代码:

客户端

 

 

服务器端:

 

 

 

缺点

 

 

改进

网络传输

 

 

 

 

 

 

 

 

基于socket实现一个联机对战的三子棋:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuruhua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值