linux:socket编程

一,IP地址,端口

我们进行网络通信时,需要的最基本的信息就是客户端的IP地址,端口,以及服务端的IP地址,端口,就比如当你需要邮寄一个快递时,你需要知道你现在在哪里,以及快递的目的地是哪里,同理,你想让一台计算机给另外一台计算机发消息,那么就需要知道自己的IP地址和端口号,以及对方的IP地址和端口号。

(1)IP地址;现阶段你可以理解为,计算机通过不同的ip地址来区别不同的主机,所以你在通信时需要知道目标主机和当前主机的ip地址才能够让两个计算机实现通信。

(2)端口;我们的计算机里面运行着不止一个应用,那么我们如何区分某个信息是哪一个应用发出的,想让目标主机的哪一个应用接收呢?,没错,这时候便需要用到端口了,你可以理解为每个应用通信时系统都会为其分配一个端口号,这个端口号是独一无二的,所以你就能识别是那个软件发出的信息,需要那个对应的软件来接收了。

二,简单的socket通信的过程,流程。

在这里插入图片描述根据流程图我们先大体了解一下过程,这些函数的具体使用方法我们后面会讲到。

三,相关函数调用了解(五个基本函数)

1,socket()调用

socket()你可以当成creat()去理解,只不过creat()是创建了一个普通的文件类型,而socket()是创建了一个socket套接字类型的文件用于通信,socket()的返回值是一个文件描述符,而他的参数,决定了你会使用什么样的ip协议去通信(比如,用ipv4还是ipv6来通信,用其中哪个的协议族的哪一个去通信,具体到哪一个协议),这正好对应了他的三个参数,分别是协议族,socket类型,以及指定的协议。下面是用法和参数。

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

domain:协议族。例如AF_INET、AF_INET6即(ipv4,ipv6)等。 
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM等。
protocol:最终确定的协议,设为0时,会自动选择type类型对应的默认协议。

2,bind()调用

前面,我们socket()指定了我们会使用什么样的协议去通信,但是我们对应的ip地址和端口都还是空着的,我们bind()的作用便是给socket()绑定上确定的协议头,IP地址以及端口,当然,在绑定之前,我们需要将对应的数据初始化。(可以对照后面的示例代码去理解)。下面是用法和参数。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd:socket的文件描述符。
个名字。
addr:一个指针,指向要给sockfd的协议地址。
addrlen:地址的长度。

3,listen()调用

socket()默认是一个主动类型的套接字类型,而我们服务端进行通信是不是需要“监听”这个端口呢?没错,这个函数就是将socket()变成被动监听类型的socket(),指定他要监听的IP地址以及端口。下面是用法和参数。

int listen(int sockfd, int backlog);
sockefd: 要监听的socket对应的文件描述符。
backlog: 最多可以等待几个客户端,比如有设置为5,那么连接第6个客户端连接服务器时,就连接不进来了

4,accept()调用

服务端监听对应的IP和端口,但是我们如何知道客户端何时连接了服务端呢?这个时候我们可以通过accept()函数来实现。下面是用法和参数。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd: listen()后的被动的socket描述字;
*addr: 用于存储客户端的ip地址以及端口的一个指针;
addrlen: 用来存储客户端协议地址的长度的指针;

5,connect()调用

客户端和服务端通信,客户端需要向服务端发起连接的请求,服务端收到请求后和客户端连接,那么服务端响应客户端的连接使用的是accept()函数,那么客户端需要依靠什么函数来发送请求呢?没错,就是connect()函数。下面是用法和参数。

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

sockfd: 客户端的socket对应的文件描述符。
addr: 需要连接的服务器的socket信息,含有服务器的IP地址和端口等。
addrlen: 服务器地址的长度。

四,简单的参考代码

1,服务端

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

#define LISTEN_PORT 8889
#define BACKLOG 13          
#define BUFFER 1024

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

        int       listen_fd,client_fd = -1;
        struct    sockaddr_in serv_addr;
        struct    sockaddr_in clie_addr;
        socklen_t  clieaddr_len;
        char      buf[BUFFER];
        int       rv = -1;

        //socket 过程,创立socket的fd并且给对应的协议进行通信。
        if ( (listen_fd = socket(AF_INET,SOCK_STREAM,0))<0 )
        {
                printf("creat listen_fd failure:%s\n",strerror(errno));
                return -1;
        }
        printf("listen_fd:%d\n",listen_fd);

        //bind过程,绑定对应监听的IP地址以及端口,就是服务器端的IP从哪里接受消息。
        //首先我们先定义一个sockaddr_in(IPV4)的变量serv_addr,然后进行赋值。
        memset(&serv_addr,0,sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port   = htons(LISTEN_PORT);//ps:注意大小端字节序。
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY泛指本机所有的IP地址(因为你可能有好几块网卡,不同的IP)。
        //然后我们进行bind绑定。
        if (bind(listen_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)))
        {
                printf("bind socket failture:%s\n",strerror(errno));
                return -2;
        }
        printf("socket[%d] bind on port:[%d]\n",listen_fd,LISTEN_PORT);

        //listen()过程,将socket()变成被动接受。
        listen(listen_fd,BACKLOG);

        while(1)
        {
                //开始accept创建一个新的文件描述符client——fd来进行通信。
                printf("Start waiting and accept client connect\n");
memset(&clie_addr,0,sizeof(clie_addr));
                //clieaddr_len = sizeof(clie_addr);//初始化len长度,accept的第三个参数必须是正的或者NULL,不使用可能产生Invalid argument错误。
                if ( (client_fd = accept(listen_fd,(struct sockaddr*)&clie_addr,&clieaddr_len)) < 0 )//accept被链接,相应时,会将客户端的地址,端口记录在clie——addr中,所以不用赋值
                {
                        printf("accept() creat client_fd failure:%s\n",strerror(errno));
                        return -3;
                }

                printf("Accept new client[%s:%d] with fd [%d]\n", inet_ntoa(clie_addr.sin_addr),ntohs(clie_addr.sin_port), client_fd);


                memset(buf,0,sizeof(buf));
                if ( (rv = read(client_fd,buf,sizeof(buf))) < 0 )//read和accept在这里面都是一个阻塞的过程,为了防止他读取失败后又去读取,不停读取
                {                                                //我们这里使用continue去阻止他发生阻塞
                        printf("read client_fd error:%s\n",strerror(errno));
                        close(client_fd);
                        continue;
                }

                else if (rv == 0)
                {
                        printf("client[%d] disconnect:\n",client_fd);
                        close(client_fd);
                        continue;
                }

                if ( write(client_fd,buf,rv) < 0 )
                {
                        printf("write data back to client failure:%s\n",strerror(errno));
                        close(client_fd);
                }

                sleep(1);
                close(client_fd);
        }
        close(listen_fd);
}
             

2,客户端

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

#define MSG_STR "Hello socket success!!!"
#define BUFFER 1024
#define SERVER_PORT 8889
#define DEF_IP "127.0.0.1"

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

        int                         rv = -1;
        int                         conn_fd = -1;
        struct sockaddr_in          serv_addr;
        char                        buf[BUFFER];
        char                        *ser_ip;

        ser_ip = DEF_IP;//DEF_IP本身就是字符串,是指针(即地址哦)所以不用担心类型转换

        //建立主动的socket文件描述符。
        if( (conn_fd = socket(AF_INET,SOCK_STREAM,0)) < 0 )
        {
                printf("conn_fd socket failture:%s\n",strerror(errno));
                return -1;
        }
        printf("creat conn_fd[%d] success\n",conn_fd);

        //首先我们初始化地址端口的参数。
        serv_addr.sin_family         = AF_INET;
        serv_addr.sin_port            = htons(SERVER_PORT);
        inet_aton( ser_ip,&serv_addr.sin_addr );//inet_aton的两个参数都是指针,在函数内定义,不使用define可以用*var=“127.0.0.1”然后使用这个*var,
                                                   //并且函数第二个参数使用的是sin。addr不用再往里面s_addr走
        //进行connect()链接服务器,后面参数是服务器的IP和port。
        if( connect(conn_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0 )
        {
                printf("funtion connect failure:%s\n",strerror(errno));
                return -2;
        }
        printf("connect success\n");

        //向服务器发送请求
        if( rv = write(conn_fd,MSG_STR,strlen(MSG_STR)) < 0 )
        {
                printf("write failure:%s",strerror(errno));
                goto cleanup;
        }
         //接收服务器的回应并且存入buf中
        memset(buf,0,sizeof(buf));
        if( (rv = read(conn_fd,buf,sizeof(buf))) < 0 )//注意括号的个数,因为<优先级比=高。吃一堑长一智
        {
                printf("read failure:%s",strerror(errno));
                goto cleanup;
        }
        else if( rv == 0 )
        {
                printf("read disconnect");
                goto cleanup;
        }

        printf("socket success!!!:[%dbytes],[%s]\n",rv,buf);



cleanup:
        close(conn_fd);


        return 0;
        
 }
  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值