linux网络编程

实现一个简单的Client/Server通信的程序:

服务器端和客户端的通信过程: 

1. 服务器端:

1) 服务器端创建套接字(socket() )

2) 服务器端将套接字绑定到本机端口( bind() )

3) 服务器端的套接字转化为监听套接字( listen() )

4) 服务器端阻塞等待客户端发送信息到服务器( accept() )

5) 服务器端读取客户端的数据( read() )

6) 服务器端向客户端发送数据( write() )

7) 关闭连接(close() )

2.客户端:

1) 创建客户端套接字(socket() )

2) 向服务器端请求连接 (connect() )

3) 向服务器端写数据 (writ())

4) 接收服务器端的数据 (read ())

5) 关闭连接 (close())

编程常用数据结构和API:

1)定义套接字地址结构

# include<linux/socket.h>

struct sockaddr{                     //  通用的套接字地址

    unsigned short sa_family ;  //  地址类型 AF_XXX

    char sa_data[14];                // 协议地址

}

struct sockaddr_in{                    //TCP/IP族的套接字地址

     unsigned short sin_family;     //地址类型

     unsigned short int sin_port ;  //端口号

     struct in_addr sin_addr;         //IP地址

     unsigned char sin_zero[8];     //填充字节

struct in_addr{     //  IP地址

      unsigned long s_addr;

}

设置服务器地址的一个示例代码如下:

struct sockaddr_in sock;

memset(&sock,0,sizeof(sockaddr_in));

sock.sin_family=AF_INET;

sock.sin_port=80;

inet_aton("127.0.0.1",&sock.sin_addr)

其中,inet_aton 和 memset函数的定义如下:

#include<netinet/in.h>

#include<arpa/inet.h>

int inet_aton(const char *cp,struct in_addr *inp) //将cp所指向的字符串转化为二进制的网络字节顺序的IP地址

memset(void *s,int c,size_t n)  //将s所指向的前n个字节赋成参数c所指定的值

2)创建套接字

#include<sys/types.h>

#include<sys/socket.h>

int socket(int domain,int type,int protocol)

// domain: AF_INET。。。

// type: 套接字类型 (SOCK_STREAM: TCP流套接字  |  SOCK_DGRAM:  UDP流套接字 )

// protocol =0                             

3) 绑定套接字(将创建套接字和服务器地址绑定)

#include <sys/types.h>

#include <sys/socket.h>

int bind (int sockfd, struct sockaddr * my_addr, socklen_t addlen)

4) 建立监听(将套接字转换为监听套接字)

#include<sys/socket.h>

int listen(int s, int backlog)

//s: 套接字

//backlog : 定义连接请求队列的长度

5) 服务器端阻塞等待客户端连接请求

#include <sys/types>

#include <sys/socket>

int accept(int s, struct sockaddr_in * addr, socket_t addrlen);

6) 客户端请求连接

#inlcude <sys/types.h>

#inlcude <sys/socket.h>

int connect (int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);

//sockfd:套接字

//serv_addr :服务器地址结构体

//addrlen : 地址结构体的长度

7) 接收数据

#inlcude <sys/types.h>

#inlcude <sys/socket.h>

size_t recv(int s, void * buf, size_t len, int flags);

//返回发送数据的长度

8) 发送数据

#inlcude <sys/types.h>

#inlcude <sys/socket.h>

size_t send(int s, const void* msg, size_t  len, int flags);

//返回发送数据的长度

下面来看三个实例~

1) 基于TCP 协议的服务器端和客户端通信程序:

///服务器端  my_server.c

#include <sys/type.h>

#include <sys/socket.h>

#include <stdio.h>

#include <string.h>

#include <netinet/in.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <errno.h>

#include "my_recv.h" //自定义头文件

#define SERV_PORT    4507   // 服务器端口

#define LISTENQ    12            // 连接请求队列的最大长度

//发送数据

void send_data(int conn_fd,const char* string)

{

     if( send(conn_fd,srtring,strlen(string),0)<0) {

         // my_err("send",__LINE__);  // my_err 函数在my_recv.h 中声明

         printf("send error\n");

         exit(1);

      }

}

int main() {

   int sock_fd,conn_fd;

   int optval;

   int ret;

   int name_num;

   pit_t pid;

   socklen_t cli_len;

   struct sockaddr_in cli_addr,serv_addr;

   char recv_buf[128];

  

   //创建TCP套接字

   sock_fd = socket(AF_INET,SOCK_STREAM,0);

   if(sock_fd<0){

        my_err("socket",__LINE__);

   }

 

   //设置该套接字使之可以重新绑定端口

   optval=1;

   if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,(void *)optval,sizeof(int)<0){

      //  my_err("setsockopt",__LINE__);

         printf("setsockopt error\n");

         exit(1);

   }

   //初始化服务器端地址结构

   memset(&serv_addr,0,sizeof(struct sockaddr_in));

   serv_addr.sin_family=AF_INET;

   serv_addr.sin_port = htons(SERV_PORT);

   serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

   //将套接字绑定到本地端口

   if (bind(sock_fd,(struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in))<0){

       //my_err("bind",__LINE__);

          printf("bind error\n");

          exit(1);

   }

   //将套接字转化为监听套接字

   if (listen(sock_fd,LISTENQ)<0){

      //my_err("listen",__LINE__);

         printf("listen error\n");

         exit(1);

   }

   cli_len=sizeof(struct sockaddr_in);

   while(1){

     //主进程通过accept阻塞等待客户端的连接请求,并返回连接套接字用于收发数据

       conn_fd= accept(sock_fd,(struct sockaddr*)&cli_addr,&cli_len);

       if(conn_fd<0){

            //my_err("accept",__LINE__);

             printf("accept error\n");

             exit(1);

       }

    //创建子进程处理接收到的连接请求

    if ((pid=fork())==0){   //子进程

         while(1){

               if((ret=recv(conn_fd,recv_buf,sizeof(recv_buf),0))<0)<0){

                    perror("recv");

                    exit(1);

               }

               printf("the received data is:%s \n",recv_buf);

               send_data(conn_fd,"I've just received data: %s\n",recv_buf); 

          }     

          close(sock_fd);

          close(conn_fd);

          exit(0);  //结束子进程

     }

     return 0;

}

///客户端 myclient.c

#include<sys/socket.h>

#include<netinet/in.h>

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<sys/types.h>

#include<errno.h>

#include<arpa/inet.h>

int main(int argc,char **argv){

     int i;

     int ret;

     int conn_fd;

     int serv_port=4507;

     char *strIP="127.0.0.1";

     struct sockaddr_in serv_addr;

     char  input_buf[BUFSIZE]="Hello Joan!";

     char  recv_buf[BUFSIZE];

    

 

     //初始化服务器端地址结构

     memset(&serv_addr,0,sizeof(struct sockaddr_in));

     serv_addr.sin_family=AF_INET;

     serv_addr.sin_port=htons(serv_port);

     inet_aton(strIP,&serv_addr.sin_addr);

    

    //创建一个TCP套接字

    conn_fd=socket(AF_INET,SOCK_STREAM,0);

    if(conn_fd<0){

         printf("client socket error\n");

         exit(1);

     }

   

    //向服务器发送连接请求

    if(connect(conn_fd,(struct sock_addr *)&serv_addr,sizeof(sock_addr))<0){

          printf("client connect error\n");

          exit(1);

    }

   

    //向服务器发送数据

    if(send(conn_fd,input_buf,strlen(input_buf),0)<0){

         printf("client send error\n");

          exit(1);

    }

         

    // 读取服务器发送的数据

    if((ret=recv(conn_fd,recv_buf,sizeof(recv_buf),0)<0){

          printf("data too long\n");

          exit(1);

    }

    printf("the received data from server is: %s\n",recv_buf);

   close(conn_fd);

   return 0;

}

               

  

    

2) 实现将一个文件分割为N份发送到服务器端,在服务器端再重组文件的程序:

3) 实现一个简单的端口扫描程序:

// 基于UDP 协议的服务器端和客户端通信程序: 

 

相关推荐