网络编程套接字(二)

先来学习网络编程中的socket API
接口1:

 int socket(int domain, int type, int protocol);
//创建socket文件描述符,该函数调用成功返回一个文件描述符,(TCP/UDP客户端+服务器),出错返回-1;
//应用程序可以像读写文件一样用read/write在网络上收发数据
//对于IPV4,domain参数指定为AF_INET,对于UDP协议,type的参数指定为SOCK_DGRAM,表示面向数据报的传输协议;对于TCP协议,type参数指定为SOCK_STREAM,表示面向字节流的传输协议;
//int protocol用0表示即可

接口2

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后,就可以向服务器发起连接,服务器需要调用bind()绑定一个固定的网络地址和端口号;
//bind()成功返回0;失败返回-1
//bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号
//stuct sockaddr*是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而结构体的的长度各不相同,所以需要第三个参数addrlen指定结构体的长度
对myaddr参数的初始化方法如下:
bzero(&serveraddr,sizeof(serveraddr));//将结构体清零
serveraddr.sin_family=AF_INET;//设置地址类型
serveraddr.sin_addr.s_addr=htons(INADDR_ANY);
//将网络地址设置为INADDR_ANY,这个宏表示本地内任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时,才能确定下来到底用哪个IP地址
serveraddr.sin_port=htons(SERV_PORT);//设定端口号9090

接口3

int listen(int sockfd, int backlog);
//声明sockfd处于监听状态,当请求较多时,服务器压力较大,来不及接受连接,需要设置一个backlog阻塞队列,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,backlog的设置不能太长,一般为5-10,
//listen()成功返回0,失败返回-1;

接口4

  int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  //三次握手完成后,服务器调用accept()接受连接;
  //如果服务器调用accept()时还没有客户的连接请求,就阻塞等待知道有客户建立连接 
  //addr是一个输出型参数,accept()返回时输出客户端的地址和端口号
  //如果addr的参数传NULL,表示不关心客户端的地址
  //addrlen是一个输入输出型参数,传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出,传出的是客户端地址结构体的实际长度

接口5

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//客户端需要调用connect()连接服务器,
//connectbind的参数形式一致,区别在于bind的参数是自己的地址,connect的参数是对方的地址,
//connect成功返回0,出错返回-1

下面来实现一个简单版本的网络聊天工具
TCP服务器:server.c的作用是接受client的请求,并与client进行简单的数据通信
client.c

#include<stdio.h>
#include <arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#define _PORT_ 9090
#define _BACKLOG_ 10 

int main()

{
  int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
      perror("socket");
    }
  struct sockaddr_in server_socket;
  struct sockaddr_in client_socket;
  bzero(&server_socket,sizeof(server_socket));
  server_socket.sin_family=AF_INET;
  server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
  server_socket.sin_port=htons(_PORT_ );
  if(bind(sock,(struct sockaddr*)&server_socket,\
    sizeof(struct sockaddr_in))<0)
  {
    perror("bind error");
    return 1;
  }
  if(listen(sock,_BACKLOG_)<0)
  {
    perror("listen error");
    return 2;
  }
  //监听成功
  printf("bind and listen success,wait accept...\n");
 while(1)
 {
   socklen_t len=0;
   int client_sock=accept(sock,(struct sockaddr*)&client_socket,&len);
   if(client_sock<0)
   {
     perror("accept error");
     close(sock);
     return 3;
   }
   char buf_ip[24];
   buf_ip[0]=0; //数组清空
   memset(buf_ip,'\0',sizeof(buf_ip));
   inet_ntop(AF_INET,&client_socket.sin_addr,buf_ip,sizeof(buf_ip));
   printf("get connect,ip is: %s ,port is: %d\n",buf_ip,htons(client_socket.sin_port));
   while(1)
   {
     char buf[1024];
     memset(buf,'\0',sizeof(buf));
     read(client_sock,buf,sizeof(buf));
     printf("client: #%s\n",buf);
     printf("server:$");
     memset(buf,'\0',sizeof(buf));
     fgets(buf,sizeof(buf),stdin);
     buf[strlen(buf)-1]='\0';
     write(client_sock,buf,strlen(buf)+1);
     printf("please wait...\n");
   }
 }
 close(sock);
 return 0;
}

有关read()的返回值:
大于0:实际读到的文件大小
小于0:读取文件失败
等于0:读到文件结尾,写端关闭,客户端关闭;

TCP客户端:client.c的作用是连接server,并向server发起连接请求
client.c


#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define _SERVER_IP_ "192.168.101.134"
#define _SERVER_PORT_  9090
//192.168.0.0 9090
int main(int argc,char *argv[])
{
   if(argc!=3)
   {
     printf("Usage :client IP\n");
     return 1;
    }
   char *str=argv[1];
   char buf[1024];
   memset(buf,'\0',sizeof(buf));
   struct sockaddr_in server_sock;
   int sock=socket(AF_INET,SOCK_STREAM,0);
   bzero(&server_sock,sizeof(server_sock));
   server_sock.sin_family=AF_INET;
   inet_pton(AF_INET,_SERVER_IP_ ,&server_sock.sin_addr);
   server_sock.sin_port=htons(_SERVER_PORT_);
   int ret=connect(sock,(struct sockaddr*)&server_sock,sizeof(server_sock));
   if(ret<0)
   {
     perror("connect error");
     return 1;
   }
   printf("connect success\n");
   while(1)
   {
     printf("client: #");
     fgets(buf,sizeof(buf),stdin);
     buf[strlen(buf)-1]='\0';
     write(sock,buf,sizeof(buf));
     if(strncmp(buf,"quit",4)==0)
     {
      printf("quit\n");
     break;
     }
     printf("please wait...\n");
     read(sock,buf,sizeof(buf));
     printf("server :$%s\n",buf);
   }
   close(sock);
}

多线程版本服务器与多进程版本服务器的比较:
多进程服务器
优点:可以处理多用户请求,代码编写很简单,稳定性强(进程具有独立性)
缺点:创建子进程浪费时间,连接到来时才创建子进程,性能受损;多进程只能服务有限个客户,进程特别吃资源,导致上限较低,只能用于小型服务
;进程切换周期边长,多进程随着客户端的增多,会使CPIU压力增大,调度压力增大,影响服务性能。
多线程服务器与多进程服务器相比稳定性差,有可能因为线程安全问题导致服务器挂掉。

猜你喜欢

转载自blog.csdn.net/wyn126/article/details/80235087