linux网络编程之socket编程(二)

今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题:

TCP客户/服务器模型:
 
关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际的例子。
回射客户/服务器:
这个例子的效果就是:客户端从命令行获取一行命令,然后发送给服务器端,当服务端接收到这行命令之后,不做任何操作,将其又回送给客户端,然后客户端进行回显,下面则开始一步步来实现这样的效果,来初步感受下Socket编程:
首先编写服务端:echosrv.c
第一步:创建套接字:
关于第一个参数domain,man帮助中也有说明:
但是,AF_INET等价于PF_INET,这里推荐用后者,因为刚好代表protocol family含义,下面代码如下:
 
第二步:绑定一个地址到套接字上:
 首先准备一下第二个参数,也就是要绑定的地址:
其中绑定地址还有其它两种方式:
另外,其实"servaddr.sin_addr.s_addr = htonl(INADDR_ANY);"这种写法是可以省略掉的,因为它是全0,但这里为了显示说明所以保留。
【提示】:关于上面的写法,可以参考博文:http://www.cnblogs.com/webor2006/p/3905799.html 中的地址转换函数
下面开始进行绑定:
第三步:则开始进行监听:
 
具体代码如下:
其中SOMAXCONN可以从man帮助中查看到:
它代表了socket的并发最大连接个数。
另外还得注意,套接字有被动套接字和主动套接字之分,当调用listen之后,该socket就变动被动套接字了,需要由主动套接字来发起连接,主动套接字是用connect函数来发起连接的。
 
第四步:从已完成连接队列中返回第一个连接:
 
接下来,则进行数据的接收,并将数据回显给客户端:
accept函数会返回一个新的套接字,注意:此时的套接字不再是被动套接字,而变为了主动:
可以通过accept的man手册来得知:
下面,则开始从该套接字中读取客户端发过来的数据:
至此,服务端的代码都已经编写完了,下面则先编译一下:
查看man帮助:
于是在代码中加入头:
再次编译:
还是出错,那IPPPOTO_TCP是在哪定义的呢?
可以通过以下命令进行查找:
于是乎,加上该头文件后再编译:
用同样的办法来进行查找:
于是加入它:
再次编译:
还是报错,对于这里面对应的头文件这里就不具体一个个查找了,不然有点充数的嫌疑,将所有头文件加上再次编译:
 
接下来,开始编写客户端的代码:echocli.c
首先创建一个socket:
第二步开始与服务器进行连接:
 
【说明】:用connect发起连接的套接字是主动套接字。
连接成功之后,就可以向服务器发送数据了:
另外,服务端在使用资源之后,最后也得关闭掉,所以修改服务端程序如下:
这时,客户端程序也已经编写完成,下面编译运行看一下效果:
也就是第一次客户端输入很长的字符串,而第二次输入很短的字符串时,这时就会输出有问题,照理应该是客户端输入什么,服务端就会回显给客户端,也就是打印两条一模一样的语句,产生这样的问题原因是什么呢?
下面来用图来分析一下:
 
所以,解决该问题的思路就是每次循环时将值初始化一下既可,修改代码如下:
 再次编译运行:
关于这个原因,之后会来解决,先占且不关心,等一会就会正常了,正常运行的效果如下:
这样,就实现了客户端与服务器端的socket通讯了,最终的代码如下:
echosrv.c【服务端】:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) 
        do 
        { 
                perror(m); 
                exit(EXIT_FAILURE); 
        } while(0)

int main(void)
{
    int listenfd;
    if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
/*    if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
        ERR_EXIT("socket");

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
    /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("bind");
    if (listen(listenfd, SOMAXCONN) < 0)
        ERR_EXIT("listen");

    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
    if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
        ERR_EXIT("accept");

    char recvbuf[1024];//用来存储客户端发来的数据
    while (1)
    {
        memset(recvbuf, 0, sizeof(recvbuf));
        int ret = read(conn, recvbuf, sizeof(recvbuf));//从套接字中读取数据
        fputs(recvbuf, stdout);//打印到屏幕上
        write(conn, recvbuf, ret);//并且将其又回显给客户端,其第三个参数的长度正好是我们接收到的长度
    }
    close(conn);
    close(listenfd);
    
    return 0;
}

echocli.c【客户端】:

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m) 
        do 
        { 
                perror(m); 
                exit(EXIT_FAILURE); 
        } while(0)

int main(void)
{
    int sock;
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        ERR_EXIT("socket");

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("connect");

    char sendbuf[1024] = {0};
    char recvbuf[1024] ={0};
    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {
        write(sock, sendbuf, strlen(sendbuf));
        read(sock, recvbuf, sizeof(recvbuf));

        fputs(recvbuf, stdout);
        memset(sendbuf, 0, sizeof(sendbuf));
        memset(recvbuf, 0, sizeof(recvbuf));
    }

    close(sock);
    
    return 0;
}

好了,今天的内容学到这,下回见~

原文地址:https://www.cnblogs.com/webor2006/p/3923254.html