linux下C语言socket的基本实现

概念了解

  • 网络由下往上分为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
    我们知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,三者从本质上来说没有可比性,为了概念更加的清晰,我们还是得了解一下三者之间的关系。
  • 我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容。 如果想要使传输的数据有意义,则必须使用到应用层协议。应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
  • 而我们平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。通过Socket,我们才能使用TCP/IP协议。 实际上,Socket跟TCP/IP协议没有必然的联系。 Socket编程接口在设计的时候,就希望也能适应其他的网络协议。 所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。大家可别忘了socket的英语意思是插座,就是提供接口的意思。不同语言的socket都是这些基本函数实现的。我们并不需要了解这些函数的内部实现机制,只需要会调用就行,站在巨人头上摘苹果。

基本模型

socket概念图

编程实例

客户端


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>

#define MAXLINE    1024
#define SERV_PORT  9998

int main(int argc, char *argv[])
        {
            char sendbuf[MAXLINE],receivebuf[MAXLINE];
            struct sockaddr_in servaddr;      //定义sockaddr类型结构体servaddr
            int client_sockfd;
            int rec_len;
            // 判断命令端输入的参数是否正确 
             if( argc != 2)
             {
                 printf("usage: ./client <ipaddress>\n");
                 exit(0);
              }
             // 调用socket函数
             if((client_sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
              {
                  perror("socket");//调用perror函数,该函数会自行打印出错信息
                  exit(0);
              }
             //初始化结构体
            memset(&servaddr,0,sizeof(servaddr));      //数据初始化-清零 
            servaddr.sin_family = AF_INET;             //设置IPv4通信 
            servaddr.sin_port = htons(SERV_PORT);      //设置服务器端口号 
            // IP地址转换函数inet_pton,将点分十进制转换为二进制 
             if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
              {
                   printf("inet_pton error for %s\n",argv[1]);
                   exit(0);
              }
             //调用connect函数
            if( connect(client_sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0)
              {
                  perror("connected failed");
                  exit(0);
              }
            //循环发送接收数据,send发送数据,recv接收数据 

       while(1)
           {
             printf("send msg to server: \n");
             fgets(sendbuf, 1024, stdin);//从标准输入读数据
             // 向服务器端发送数据 
             if( send(client_sockfd, sendbuf, strlen(sendbuf), 0) < 0)
              {
                  printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
                  exit(0);
              }
             // 接受服务器端传过来的数据 
             if((rec_len = recv(client_sockfd,receivebuf, MAXLINE,0)) == -1)
              {
                   perror("recv error");
                   exit(1);
              }
             printf("Response from server: %s\n",receivebuf);
           }
       //关闭套接字 
    close(client_sockfd);
    return 0;
        }

服务器端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define SERV_PORT 9998
#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    socket_fd, connect_fd;
    struct sockaddr_in     servaddr;
    char    buf[MAXLINE],sendbuf[MAXLINE];
    int     len
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
    printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
    exit(0);
    }
    //初始化
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
    servaddr.sin_port = htons(SERV_PORT);
    //调用bind函数,绑定
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0)
    {
         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
         exit(0);
    }
    //调用listen函数监听端口
    if( listen(socket_fd, 10) <0)
    {
         printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
         exit(0);
    }
    printf("waiting for client's connection......\n");

   //调用accept函数 
    if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) <0)
     {
          printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
          exit(1);
     }
        //接受客户端传过来的数据
  while((len= recv(connect_fd, buf, MAXLINE, 0))>0)
     {
       printf("receive message from client: %s\n", buf);
       //向客户端发送回应数据
        printf("send message to client: \n");
        fgets(sendbuf, 4096, stdin);
       if( send(connect_fd, sendbuf, strlen(sendbuf), 0) < 0)
         {
            printf("send messaeg error: %s(errno: %d)\n", strerror(errno), errno);
            exit(0);
         }
     }
  close(connect_fd);
  close(socket_fd);
}

功能实现

这里写图片描述
这里写图片描述
这两个基本程序能够实现C/S间通信,如果将服务端程序交叉编译后放在fl2440开发板上跑,可以直接连上其他服务器的公网ip地址,前提是服务器上有server.c的程序在跑。如果是采用端口转发的服务器还需登录路由器,设置对应的端口号以开启端口转发,比如这里提供的9998号端口。

补充知识点

Window和linux的socket的不同之处:
◇.头文件
Window:winsock.h或winsock2.h
linux: netinet/in.h(大部分都在这),unistd.h(close函数)

◇.初始化
window: 需要WSAStartup启动Ws2_32.lib;#program comment(lib, “Ws2_32”)加载lib库
linux: 无需

◇.关闭socket
window: closesocket()
linux: close()

◇.socket的类型
window: SOCKET
linux: int

◇.错误代码的获取
window: getlasterror()/WSAGetLastError()
linux: 为成功返回-1;错误代码在errno中(头文件errno.h)

◇.设置非阻塞
window: ioctlsocket()
linux: fcntl(), (头文件fcnt.h)

◇.send函数最后一个参数
window: 一般是0
linux: 最好设置为MSG_NOSIGNAL;表示出错后不向系统发信号,否则程序会退出!

◇.多线程
window:包含process.h,用_beginthread/_endthread
linux:包含pthread.h,用pthread_create/pthread_exit

◇.使用IP地址
window:addr_var.sin_addr.S_un.S_addr
linux:addr_var.sin_addr.s_addr

猜你喜欢

转载自blog.csdn.net/xn6666/article/details/80300814