基于linux系统的简单客户服务器socket协议,及其中用到各种语句的解释

本文详细介绍了在CentOS 6.8系统上使用VMware虚拟机和TCP/IP网络编程原理,通过C语言实现了一个简单的客户端与服务器通信程序,涉及socket编程、地址转换、套接字绑定和监听等关键步骤。
摘要由CSDN通过智能技术生成

软件平台:vmware
系统:CentOS 6.8
参考书籍:TCP/IP网络编程

客户端代码及分析

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

void error_handling(char *message);

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

        struct sockaddr_in serv_addr;  //创建sockaddr_in结构体,用来存储端口号和IP地址
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size;  //和int长度相同

        char message[]="hello,world";

        if(argc!=2){         //argc表示main函数传入的参数数量,初始值为1
                printf("Usage : %s <port>\n",argv[0]);  //agrv[]记录传入的参数,argv[0]为本机地址
                exit(1);
        }

        serv_sock=socket(PF_INET,SOCK_STREAM,0);   //创建套接字,使用ipv4协议,面向连接的套接字,TCP协议
        if(serv_sock==-1)
                error_handling("socket() error");
        memset(&serv_addr,0,sizeof(serv_addr));   //用0给结构体serv_addr进行初始化
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);   //htonl就是把本机字节顺序转化为网络字节顺序,其中l代表的就是长整型,s代表的就是short类型的(16位)
        serv_addr.sin_port=htons(atoi(argv[1]));

        if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
                error_handling("bind() error");

        if(listen(serv_sock,5)==-1)
                error_handling("listen() error");

        clnt_addr_size=sizeof(clnt_addr);
        clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);
        if(clnt_sock==-1)
                error_handling("accept() error");

        write(clnt_sock,message,sizeof(message));
        close(clnt_sock);
        close(serv_sock);

        return 0;
}

void error_handling(char *message){
        fputs(message,stderr);
        fputc('\n',stderr);
        exit(1);
}


客户端:

在这里插入代码片

1库函数:
1.1.stdlib.h库
①int atoi(const char *str)
把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。
②void exit(int status)
使程序正常终止。

1.2.string.h库
①void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
memset() 函数可以说是初始化内存的“万能函数”,通常为新申请的内存进行初始化工作。它是直接操作内存空间,mem即“内存”(memory)的意思。memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。

1.3.unistd.h库
unistd.h是unix std的意思,是POSIX标准定义的unix类系统定义符号常量的头文件,包含了许多UNIX系统服务的函数原型,unistd.h在unix中类似于window中的windows.h。
①ssize_t write(int fd,const void*buf,size_t count);
参数说明:
fd:是文件描述符(write所对应的是写,即就是1)
buf:通常是一个字符串,需要写入的字符串
count:是每次写入的字节数

1.4.arpa/inet.h
①htnol() htons()
htonl就是把本机字节顺序转化为网络字节顺序h—host 本地主机
to 就是to 了
n —net 网络的意思
l 是 unsigned long

1.5sys/socket.h
①socket()
int socket(int af, int type, int protocol);
af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6(也可用PF)。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)。
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
②bind()
int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen)
_________________________返回值:0 – 成功,-1 - 出错
其中:
sockfd是socket函数返回的描述符;
myaddr指定了想要绑定的IP和端口号,均要使用网络字节序-即大端模式;
addrlen是前面struct sockaddr(与sockaddr_in等价)的长度
③accept()
对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了。
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。

1.6stdio.h
①int fputs(const char *s, FILE *stream);
stream 表示向何种流中输出,可以是标准输出流 stdout,也可以是文件流。标准输出流即屏幕输出,printf 其实也是向标准输出流中输出的。
(stdout和stderr的区别:
stdout(标准输出),输出方式是行缓冲。输出的字符会先存放在缓冲区,等按下回车键时才进行实际的I/O操作。
stderr(标准错误),是不带缓冲的,这使得出错信息可以直接尽快地显示出来。
在这里插入图片描述

2.关于argc和agrv
2.1int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。
2.2char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:
argv[0]指向程序运行的全路径名
argv[1]指向在DOS命令行中执行程序名后的第一个字符串
argv[2]指向执行程序名后的第二个字符串
argv[3]指向执行程序名后的第三个字符串
argv[argc]为NULL

3结构体
sockaddr_in
定义如下:
typedef struct sockaddr_in {
short sin_family;
USHORT sin_port;
IN_ADDR sin_addr;
CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
struct in_addr {
in_addr_t s_addr;
};
结构体in_addr 用来表示一个32位的IPv4地址
inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位二进制方式的IP地址(0xC0A80001)。//server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

4关键字
INADDR_ANY
转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。
比如一台电脑有3块网卡,分别连接三个网络,那么这台电脑就有3个ip地址了,如果某个应用程序需要监听某个端口,那他要监听哪个网卡地址的端口呢?
如果绑定某个具体的ip地址,你只能监听你所设置的ip地址所在的网卡的端口,其它两块网卡无法监听端口,如果我需要三个网卡都监听,那就需要绑定3个ip,也就等于需要管理3个套接字进行数据交换,这样岂不是很繁琐?
所以出现INADDR_ANY,你只需绑定INADDR_ANY,管理一个套接字就行,不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值