linux网络编程之server端和client端的通信实例

server端程序

进行测试时:

运行server程序

新开一个终端,输入命令 : nc 127.0.0.1 9527    //端口号为9527

 在命令行中输入  abc  即可回传得到大写的ABC
/*server.c */
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<error.h>
#include<pthread.h>
#include<netinet/in.h>

#define SERV_PORT 9527

void sys_err(const char *str)
{
        perror(str);
        exit(1);
}

int main()
{
        int sfd = 0,cfd = 0;
        int res = 0,i = 0;
        char buf[BUFSIZ]; //BUFSIZ大小为8192字节
        struct sockaddr_in serv_addr,clit_addr;
        socklen_t clit_addr_len;

        /*设置 struct sockaddr_in 里面的内容 采用IPv4 端口号为9527 IP号由INADDR_ANY生成的二进制的IP地址   */
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        /*生成套接字,并校验*/
        sfd = socket(AF_INET,SOCK_STREAM,0);
        if(sfd == -1){
                sys_err("socket error");
        }

        /*绑定端口号+IP地址  */
        bind(sfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

        /*设置监听的上限        */
        listen(sfd,20);

        /*监听,阻塞至有客户端连接,并返回一个新的套接字与客户端建立通讯 */
        clit_addr_len = sizeof(clit_addr);

        cfd = accept(sfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
        if(cfd == -1){
                sys_err("accpet error");
        }

        /*读取客户端发来的信息,并转化为大写    */
        while(1){
                res = read(cfd,buf,sizeof(buf));
                write(STDOUT_FILENO,buf,res);

                for(i = 0;i < res;i++)
                        buf[i] = toupper(buf[i]);

                write(cfd,buf,res);
        }

        close(sfd);
        close(cfd);

        return 0;
}

在服务端可以用INADDR_ANY表示本地的IP地址,直到与客户端建立联系时才确定下来真正的IP

如何在服务端把客户端的IP及端口号打印出来

在上面程序的基础上:

                将【struct sockaddr_in】结构体里面的clit_addr.sin.port(端口号)和clit_addr.sin_addr.s_addr(网络字节顺序并存储为二进制                  的IP地址)打印出来。

                其中打印端口号时只要将网络字节顺序转化为本地字节顺序ntohs(clit_addr.sin.port);

                打印IP地址时不但要将网络字节顺序转化为本地字节顺序,同时要将二进制转化为点分十进制,所以采用专门的函数inet_ntop()

 

/*方法一 */
char client_IP[1024];

printf("client IP :%s,port :%d",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));

/*方法二  */
printf("\n已连接了客户 : %s:%d \n", inet_ntoa(clit_addr.sin_addr), ntohs(clit_addr.sin_port));

在上述的方法中方法一有时候在Ubuntu中无法实现,在centos中可以实现;
有时候在centos中可以单独实现方法二,不能单独实现方法一;把两种方法同时写在一起的时候能够实现。

后发现解决办法:printf()输出缓冲区里面的内容需要达到一定的条件的例如 碰到 \n 会刷新输出,而方法一中没有达到满足要求的情况下是不会输出的,可以在后面加上:fflush(stdout)函数。强制刷新。

详细请见博客:https://blog.csdn.net/zqixiao_09/article/details/50234733

 

/*======================================================================================================

此处参考:https://blog.csdn.net/zyy617532750/article/details/58595700/ 

1.把ip地址转化为用于网络传输的二进制数值
int inet_aton(const char *cp, struct in_addr *inp);

inet_aton() 转换网络主机地址ip(如192.168.1.10)为二进制数值,并存储在struct in_addr结构中,即第二个参数*inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。(这个转换完后不能用于网络传输,还需要调用htons或htonl函数才能将主机字节顺序转化为网络字节顺序

in_addr_t inet_addr(const char *cp);

inet_addr函数转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp无效,函数返回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回-1,255.255.255.255是一个有效的地址,不过inet_addr无法处理;


2.将网络传输的二进制数值转化为成点分十进制的ip地址

char *inet_ntoa(struct in_addr in);

inet_ntoa 函数转换网络字节排序的地址标准的ASCII以点分开的地址,该函数返回指向点分开的字符串地址(如192.168.1.10)的指针,该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖),所以如果需要保存该串最后复制出来自己管理!

 
我们如何输出一个点分十进制的IP呢?我们来看看下面的程序:

#include <stdio.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
#include <string.h>   
int main()   
{   
    struct in_addr addr1,addr2;   
    ulong l1,l2;   
    l1= inet_addr("192.168.0.74");   
    l2 = inet_addr("211.100.21.179");   
    memcpy(&addr1, &l1, 4);   
    memcpy(&addr2, &l2, 4);   
    printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意这一句的运行结果   
    printf("%s\n", inet_ntoa(addr1));   
    printf("%s\n", inet_ntoa(addr2));  
    return 0;   
}   

实际运行结果如下: 
192.168.0.74 : 192.168.0.74          //从这里可以看出,printf里的inet_ntoa只运行了一次。  

192.168.0.74  

211.100.21.179  

inet_ntoa返回一个char *,而这个char *的空间是在inet_ntoa里面静态分配的,所以inet_ntoa后面的调用会覆盖上一次的调用。第一句printf的结果只能说明在printf里面的可变参数的求值是从右到左的,仅此而已。

3.新型网路地址转化函数inet_pton和inet_ntop
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。

#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);     //将点分十进制的ip地址转化为用于网络传输的数值格式
        返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
 
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);     //将数值格式转化为点分十进制的ip地址格式
        返回值:若成功则为指向结构的指针,若出错则为NULL

(1)这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
(2)第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0.

(3)inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。

====================================================================================================*/
 

client端程序

/*client.c    */
#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<error.h>
#include<pthread.h>
#include<netinet/in.h>

#define SERVPORT 9527  /*定义服务端的端口号为9527*/

void sys_err(const char *err)
{
        perror(err);
        exit(1);
}

int main(int argc,int *argv[])
{
        struct sockaddr_in server_addr;
        int cfd = 0,i=10 ;
        char buf[10] = "hello\n";

        /*设置服务端的struct sockaddr */
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVPORT);
        inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr.s_addr);/*将127.0.0.1转换为网络字节序,并转化为二进制,回写给【server_addr.sin_addr.s_addr】*/

        /*建立套接字 */
        cfd  = socket(AF_INET,SOCK_STREAM,0);
        if(cfd == -1){
                sys_err("socket error");
        }
         
        /*连接服务端,进行connect操作时,系统会隐性地把客户端的IP+PORT生成并绑定    */
        int ret = connect(cfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
        if(ret == -1)
                sys_err("connect error");

        while(i--){
                /*写给服务端套接字的接受缓冲区    */
                write(cfd,"hello\n",6);

                /*从服务端套接字的发送缓冲区读取数据,并写在屏幕上    */
                ret = read(cfd,buf,sizeof(buf));
                write(STDOUT_FILENO,buf,ret);
                sleep(1);
        }

        close(cfd);

        return 0;

}

在两个终端中分别运行这两个程序,运行效果如下:

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值