网络编程

在一次服务中提供数据或服务的称为服务器,获取数据的称为客户端。

在网络基础中讲到有许多协议,那这里就面临协议的选择,TCP还是UDP?

先来看看TCP和UDP的区别:
TCP:
面向连接的(服务器和客户端之间维护一条专有线路) 
可靠的(数据无差错、不丢失、不重复、并且按序到达) 
流式服务
UDP:
无连接(每次都要探寻线路) 
不可靠的
数据报服务

线路连接上以后,未来时间段不间断发送数据,选择TCP。但是维护连接线路要付出代价,根据 数据是否重要来选择可靠还是不可靠,有些数据不一定要求可靠。

两种协议自然就有两种编程方式:
TCP编程流程:
  • 服务器端sever:
  1. 创建socket
  2. bind(将IP地址和端口号命名,一般我们访问网站都不是直接输IP地址的吧)    
  3. listen(监听连接,创建监听队列)
  4. accept(从listen拿到已经完成连接的)
  5. recv/send(收发数据)
  6. close(关闭连接,所有完成后再关闭一次socket)
  • 客户端client:
  1. 创建socket ()/* bind 一般不要*/
  2. 发起连接connect
  3. recv/send(收发数据)
  4. close

具体函数如下:
1、创建socket
int socket(int domain, int type, int protocol); 
//domain    协议簇 IPv4(AF_INET)/ IPv6(AF_INET6); 
//type      选择TCP(SOCK_STREAM)还是UDP(SOCK_DGRAM); 
//protocol  TCP下具体的哪个协议,现在已经不用,给0; 
//成功返回一个socket文件描述符,失败返回-1;

2、命名socket
int bind(int sockfd,const struct sockaddr*addr,int addrlen);
//sockfd    socket函数返回的文件描述符;
//addr      指定IP地址和端口号
//addrlen   该socket地址的长度
//成功返回0,失败返回-1并设置erron。常见erron是EACCESS(被绑定的地址是受保护的地址,即小于1024,仅超级用户能够访问)
EADDRINUSE(被绑定的地址正在使用中)






struct sockaddr_in{
      sa_family_t sin_family;   /*地址族:AF_INET*/
      u_int16_t sin_port;       /*端口号(网络字节序:大端模式)*/
      struct in_addr sin_addr; /*IPv4地址结构体*/
};
struct sin_addr{
       u_int32_t s_addr;        /*IPv4地址,用网络字节序表示*/
};
所有专用socket地址类型的变量在实际使用时都需要强制转换成通用的sockaddr地址类型,因为所有socket编程接口使用的地址参数类型都是sockaddr。当然 在这里也要弄清楚主机字节序和网络字节序

通常都是用点分十进制字符串表示IPv4地址,但是编程中需要转换成整数(二进制)使用。系统提供了3个函数用于,点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换:

#include<arpa/inet.h>
int_addr_t  inet_addr(const char*str);
//将点分十进制字符串表示的IPv4地址转换成用网络字节序整数表示的IPv4地址,失败时返回INADDR_NONE,方便

int inet_aton(const char*cp,struct in_addr *inp);
//将点分十进制字符串表示的IPv4地址转换成用网络字节序整数表示的IPv4地址,
将转化结果储存于参数inp指向的地址结构中,成功返回1,失败返回0.相比于inet_addr更安全

char *inet_ntoa(struct in_addr in);
//用网络字节序整数表示的IPv4地址转换成点分十进制字符串表示的IPv4地址,该函数不可重入

3、监听socket



int listen(int sockfd, int backlog);
//创建一个监听队列,有连接放到队列中,backlog表示队列大小,但是底层有默认值,给的小了就用默认值
//成功返回0,失败返回-1


4、接收连接



int accept(int sockfd,struct sockaddr*addr,int len);
//sockfd     执行过listen监听的socket
//addr        记录客户端的IP地址和端口号
//len         长度
//成功返回获取到的和客户端连接的文件描述符,此后由该返回值和客户端通信,失败返回-1

5、发起连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//addr 要连接的服务器的IP地址和端口号
//成功返回0,失败返回-1

6、关闭连接



int close(int fd);
//fd    待关闭的socket

7、收发数据
#include<sys/typs.h>
#include<sys/socket.h>
ssize_t  recv(int sockfd, void*buf, size_t len, int flags);
//接受(读取)sockfd上的数据,放到buf缓冲区,大小为len,flags暂且设为0;
//成功返回实际读取到的数据长度,返回0说明对方已关闭连接,出错返回-1;
ssize_t  send(int sockfd, const void*buf, size_t len, int flags);
//buf缓冲区的数据发送(写入)sockfd,大小为len,flags暂且设为0;
//成功返回实际读写入数据长度,出错返回-1;




既然要完成通信,那么至少有一个服务器端一个客户端,代码如下:
//服务器端ser.c 
void main()
{
      int sockfd = socket(AF_INET,SOCK_STREAM,0);
      assert(sockfd != -1);
  
      struct sockaddr_in ser,cli;
      ser.sin_family = AF_INET;
      ser.sin_port = htons(6000);
      ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  
      int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
      assert(res != -1);
  
      listen(sockfd,5);
  
      int len = sizeof(cli);
      int c = accept(sockfd,(struct sockaddr*)&cli,&len);
      assert(c != -1);
  
      char buff[128] = {0};
      recv(c,buff,127,0);
      printf("recv::%s\n",buff);
      send(c,"I Know",strlen("I Know"),0);
  
      close(c);
  
      close(sockfd);
  
 }

//客户端cli.c
void main()
 {
     int sockfd = socket(AF_INET,SOCK_STREAN,0);
      assert(sockfd != -1);
   
      struct sockaddr_in ser,cli;
      ser.sin_family = AF_INET;
     ser.sin_port = htons(6000);
      ser.sin_addr.s_addr = inet_addr("127.0.0.1");
      
      int res = connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
      assert(res != -1);
   
      send(sockfd,"Hello World",strlen("Hello World"),0);
      char buff[128] = {0};
      recv(sockfd,buff,127,0);
  
      printf("recv::%s\n",buff);
  
      close(sockfd);
  }

但是在实际应用中,服务器发送一次并没有结束,客户端可以一直申请,因此服务器端要循环收发数据。
 void main()
 {
     int sockfd = socket(AF_INET,SOCK_STREAM,0);
     assert(sockfd != -1);
 
     struct sockaddr_in ser,cli;
     ser.sin_family = AF_INET;
     ser.sin_port = htons(6000);
     ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  
      int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
      assert(res != -1);
  
      listen(sockfd,5);
  
  
      while(1)
      {
          int len = sizeof(cli);
          int c = accept(sockfd,(struct sockaddr*)&cli,&len);
          assert(c != -1);
  
          char buff[128] = {0};
          recv(c,buff,127,0);
          printf("recv::%s\n",buff);
          send(c,"I Know",strlen("I Know"),0);
  
          close(c);
      }
  
      close(sockfd);
  
  }







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值