Linux网络编程学习笔记(一)

本文详细介绍了Linux环境下GCC编译器工具集的使用方法,包括编译单个文件、多文件编译、生成汇编语言、以及Makefile的编写技巧。同时,也对vim编辑器的基础操作进行了深入讲解,如普通模式和插入模式、移动光标、删除与复制粘贴等常用命令。此外,还介绍了文件的通用操作方法,包括open与creat、close、read、write和lseek函数的基本用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、vim安装学习使用:

1.普通模式和插入模式
2.移动光标h左j下k右l上
3.删除字符x、dd、u、ctrl+r
  删除一个字符x
  删除一行dd
  恢复删除u
  取消一个命令ctrl+r
4.复制粘贴p、y
  vim下的粘贴命令字符是p,它的作用是将内存中的字符复制到光标后面
  具体方法是:用dd命令删除它,然后使用u命令恢复,这时候内存中是dd命令删除的字符串。
  复制命令是y,将指定的字符串复制到内存中,yw命令复制一个单词
  复制一行yy
5.查找字符串
  /xxx
6.跳转到某一行
  :n

二、Linux下GCC编译器工具集

1.编译单个文件成执行文件
  gcc hello.c
  具体过程:GCC编译器可以识别默认文件扩展名,通过检查文件扩展名,GCC知道这是一个C文件。
  采取默认步骤,先将C文件编译成目标文件,然后将目标文件链接成可执行文件,最后删除目标文件,没有指定生成执行文件的名称,生成a.out
  选项-o可以使编译程序生成指定文件名
  gcc -o test hello.c
  ./test
  选项-c用于生成目标文件,这一选项将源文件生成目标文件,而不是生成可执行文件。
  gcc -c hello.c(生成hello.o)


2.多文件编译
  GCC可以自动编译连接多个文件,不管是目标文件还是源文件,都可以使用同一个命令编译到一个可执行文件中。
  例:main.c和string.c
     gcc -o test string.c main.c
./test
  也可以先将源文件编成目标文件,然后在进行链接。
     gcc -c string.c main.c
gcc -o test string.o main.o


3.编译成汇编语言
  生成汇编语言的选项是-S,扩展名是.s
    gcc -S string.c


4.Makefile的编写
  一个Makefile文件主要包含以下几个内容:
    1.由make工具创建的目标体(target),通常是目标文件或可执行文件。
    2.要创建的目标体所依赖的文件(dependency_file)
    3.创建每个目标体时需要运行的命令(command)
http://www.360doc.com/content/09/0109/18/36491_2298945.shtml


    最基本的写法:
main:main.o mytool1.o mytool2.o
         gcc -o main main.o mytool1.o mytool2.o
main.o:main.c mytool1.h mytool2.h
         gcc -c main.c
mytool1.o:mytool1.c mytool1.h
         gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
         gcc -c mytool2.c


clean:
         rm -f *.o main


改进1:使用变量
1.make变量(Makefile中定义的或者是make的环境变量)的引用使用“$(VAR)”格式,无论“VAR”是单字符变量名还是多字符变量名。
2. 出现在规则命令行中shell变量(一般为执行命令过程中的临时变量,它不属于Makefile变量,而是一个shell变量)引用使用shell的“$tmp”格式。
3. 对出现在命令行中的make变量同样使用“$(CMDVAR)” 格式来引用。
OBJ=main.o mytool1.o mytool2.o


make:$(OBJ)
         gcc -o main $(OBJ)
main.o:main.c mytool1.h mytool2.h
         gcc -c main.c
mytool1.o:mytool1.c mytool1.h
         gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
         gcc -c mytool2.c


clean:
         rm -f main $(OBJ)
改进2:使用自动推导
让make自动推导,只要make看到一个.o文件,它就会自动的把对应的.c文件加到依赖文件中,并且gcc -c .c也会被推导出来,所以Makefile就简化了。
CC = gcc
OBJ = main.o mytool1.o mytool2.o


make: $(OBJ)
         $(CC) -o main $(OBJ)


main.o: mytool1.h mytool2.h
mytool1.o: mytool1.h
mytool2.o: mytool2.h


.PHONY: clean
clean:
         rm -f main $(OBJ)
改进三:自动变量($^ $< $@)的应用


Makefile 有三个非常有用的变量,分别是$@、$^、$<。代表的意义分别是:
$@--目标文件,
$^--所有的依赖文件,
$<--第一个依赖文件。


CC = gcc
OBJ = main.o mytool1.o mytool2.o


main: $(OBJ)
         $(CC) -o $@ $^


main.o: main.c mytool1.h mytool2.h
         $(CC) -c $<
mytool1.o: mytool1.c mytool1.h
         $(CC) -c $<
mytool2.o: mytool2.c mytool2.h
         $(CC) -c $<


.PHONY: clean
clean:
         rm -f main $(OBJ)


三.文件的通用操作方法
1.open与creat
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
int creat(const char *pathname,mode_t mode);
打开或建立一个文件。对于open函数,仅当创建文件时,才用到第三个参数
打开成功,返回一个文件描述符的值,出错时返回-1
文件的打开标志flags用于设置文件打开后允许的操作方式:
只读O_RDONLY
只写O_WRONLY
读写O_RDWR
可选参数
O_APPEND 每次对文件进行写操作都追加到文件的尾端
O_CREAT  如果文件不存在则创建它,当使用此选项时,第三个参数mode需要同时设定,用来说明新文件的权限
O_EXCL   查看文件是否存在,如果同时制定了O_CREAT,而文件已经存在,则会报错


O_TRUNC  如果此文件存在,而且为只读或读写成功打开,则将其长度截短为0。
O_NOCTTY 如果pathname指的是终端设备,则不将该设备分配作为此进程的控制终端。
O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的I/O操作设置非阻塞模式


mode:用下列一个或多个常量进行“或”运算构成mode参数(这些常量定义在sys/stat.h)

S_IRWXU:00700 拥有者读写执行权限

S_IRUSR: 00400 拥有者读权限
S_IWUSR:00200  拥有者写权限


fd = open(filename,O_RDWR);
if(-1==fd)
printf("Open file %s failure!,fd:%d\n",filename,fd);//文件不存在
else{
printf("Open file %s sucess!,fd:%d\n",filename,fd);
}


建立一个空的test.txt
echo "">test.txt
注意:OCREAT与OEXCL结合使用可以编写容错的程序


/*O_CREAT与O_EXCL结合使用*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd = -1;
char filename[]="test.txt";
//打开文件,如果文件不存在,则报错
fd = open(filename,O_RDWR|O_CREAT|O_EXCL,S_IRWXU);
if(-1==fd)//文件存在
{
printf("File %s exist!,reopen it",filename);
fd=open(filename,O_RDWR);
printf("fd:%d\n",fd);
}
else//文件不存在,创建并打开
{
printf("Open file %s success,fd:%d\n",filename,fd);
}
return 0;
}


2.close
#include <unistd.h>
int close(int fd);
close 关闭一个文件描述符,关闭以后此文件描述符不在指向任何文件,从而描述符可以再次使用,成功返回0,错误返回-1.
系统打开第一个文件的文件描述符的值为3


3.read
用read()函数从打开文件中读取数据,用户可以对读入的数据进行操作。
#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);
read()函数从文件描述符fd对应的文件中读取count字节,放到buf开始的缓冲区。
读取成功后,文件对应的读取位置指针,向后移动位置,大小为成功读取的字节数。返回读取的字节数。


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int fd=-1,i;
    ssize_t size = -1;
    char buf[10];
    char filename[]="test.txt";
    fd=open(filename,O_RDONLY);
    if(-1==fd)
        printf("Open file %s failure,fd:%d\n",filename,fd);
    else{
        printf("Open file %s success,fd:%d\n",filename,fd);
    }
    while(size)
    {
        size = read(fd,buf,10);//every time read 10 ge zi jie data
        if(-1 == size){
            close(fd);
            printf("read file error occurs\n");
            return -1;
        }
        else{
            if(size > 0){
                printf("read %d bytes:",size);
                printf("\"");
                for(i=0;i<size;i++)
                    printf("%c",*(buf+i));
                printf("\"\n");
            }
            else{
                printf("mowei size = %d",size);
                printf("reach the end of file\n");
            }
        }
    }
    return 0;
}


4.write
向打开的文件中写入数据,将用户的数据保存到文件中
#include <unistd.h>
ssize_t write(int fd,const void *buf,size_t count);
当操作的对象是普通文件时,写文件的位置从文件的当前开始,操作成功后,写的位置会增加写入字节数的值。
如果在打开文件的时候指定了O_APPEND项,每次写操作之前,会将写操作的位置移到文件的结尾处。




cat主要有三大功能:
1.一次显示整个文件。$ cat filename
2.从键盘创建一个文件。$ cat > filename  
   只能创建新文件,不能编辑已有文件.
3.将几个文件合并为一个文件: $cat file1 file2 > file


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>


int main()
{
    int fd = -1,i;
    ssize_t size = -1;
    int input=0;
    char buf[]="quick brown fox jumps over the lazy dog";
    char filename[]="s.txt";
    fd = open(filename ,O_RDWR);
    if(-1==fd)
        printf("Open file %s failure,fd:%d\n",filename,fd);
    else
        printf("Open file %s success,fd:%d\n",filename,fd);
    size = write(fd,buf,strlen(buf));
    printf("write %d bytes to file %s\n",size,filename);
    close(fd);
    return 0;
}


注意:要在写入前对文件进行清空,可以使用open()函数的O_TRUNC选项,将打开的函数修改为如下形式:
fd = open(filename , O_RDWR | O_TRUNC );


5.lseek()
可以设置文件偏移量的位置
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fildes,off_t offset,int whence);对文件操作符fildes所代表的文件,按照操作模式whence和偏移量的大小offset,重新设定文件的偏移量
操作成功返回新偏移量的值,失败返回-1。
文件偏移量可以为负值


参数whence和offset结合使用:
如果whence为SEEK_SET,则offset为相对文件开始处的值,即将该文件偏移量设为距文件开始处offset个字节
如果whence为SEEK_CUR, 则offset为相对当前位置的值,即将该文件的偏移量设为当前值加offset
如果whence为seek_end, 则offset为相对文件结尾的值,即将该文件的偏移量设置为文件长度加offset


获得当前的偏移量
off_t cur_pos = lseek(fd, 0, SEEK_CUR);
netstat 命令 netstat是用来显示网络的连接,路由表和接口统计等网络的信息.netstat有许多的 选项 我们常用的选项是 -an 用来显示详细的网络状态.至于其它的选项我们可以使用帮 助手册获得详细的情况. telnet telnet是个用来远程控制的程序,但是我们完全可以用这个程序来调试我们的服务端程 序的. 比如我们的服务器程序在监听 8888 端口,我们可以用 telnet localhost 8888来查 看服务端的状况. TCP(Transfer Control Protocol)传输控制协议是种面向连接的协议,当我们的网络程 序使用 这个协议的时候,网络可以保证我们的客户端和服务端的连接是可靠的,安全的. UDP(User Datagram Protocol)用户数据报协议是种非面向连接的协议,这种协议并不能保证 我们的网络程序的连接是可靠的,所以我们现在编写的程序般是采用 TCP协议的 socket int socket(int domain, int type,int protocol) domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和AF_INET 等). AF_UN IX 只能够用于单的 Unix 系统进程间通信,而 AF_INET 是针对Internet的,因而可以允许在 远程 主机之间通信(当我们 man socket 时发现 domain 可选项是 PF_*而不是AF_*,因为 glibc 是 posix 的实现 所以用 PF代替了 AF,不过我们都可以使用的). type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等) SOCK_STREAM 表明 我们用的是 TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是 UDP协议,这样只会提供定长的,不可靠,无连接的通信. protocol:由于我们指定了 type,所以这个地方我们般只要用 0 来代替就可以了 sock et 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错 的详细情况. bind int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由socket调用返回的文件描述符. addrlen:是 sockaddr结构的长度. my_addr:是个指向 sockaddr的指针. 在<linux/socket.h>;中有 sockaddr的定义 struct sockaddr{ unisgned short as_family; char sa_data[14]; }; 不过由于系统的兼容性,我们般不用这个头文件,而使用另外个结构(struct sock addr_in) 来代替.在<linux/in.h>;中有 sockaddr_in 的定义 struct sockaddr_in{ unsigned short sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; 我们主要使用 Internet所以 sin_family 般为 AF_INET,sin_addr设置为 INADDR_ANY 表 示可以 和任何的主机通信,sin_port 是我们要监听的端口号.sin_zero[8]是用来填充的 .. bind 将本地的端口同 socket返回的文件描述符捆绑在起.成功是返回 0,失败的情况和 socket样 listen int listen(int sockfd,int backlog) sockfd:是 bind 后的文件描述符. backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示 可以介绍的排队长度. listen函数将 bind 的文件描述符变为监听套接字.返回的情况和 b ind 样. accept int accept(int sockfd, struct sockaddr *addr,int *addrlen) sockfd:是 listen后的文件描述符. addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,li sten和 accept是服务器端用的函数,accept调用时,服务器端的程序会直阻塞到有个 客户程序发出了连接. accept 成功时返回最后的服务器端的文件描述符,这个时候服务 器端可以向该描述符写信息了. 失败时返回-1 connect int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) sockfd:socket返回的文件描述符. serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址 addrlen:serv_addr的长度 connect函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服务端通讯的文件 描述符 失败时返回-1. 总的来说网络程序是由两个部分组成的--客户端和服务器端.它们的建立步骤般是: 服务器端 socket-->;bind-->;listen-->;accept 客户端 socket-->;connect 字节转换函数 在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的, 比如 i386 芯 片是低字节在内存地址的低端,高字节在高端,而 alpha 芯片却相反. 为了统起来,在 Li nux 下面,有专门的字节转换函数. unsigned long int htonl(unsigned long int hostlong) unsigned short int htons(unisgned short int hostshort) unsigned long int ntohl(unsigned long int netlong) unsigned short int ntohs(unsigned short int netshort) 在这四个转换函数中,h 代表 host, n 代表 network.s 代表 short l 代表 long 第个函 数的意义是将本机器上的 long 数据转化为网络上的 long. 其他几个函数的意义也差不多 .. IP 和域名的转换 在网络上标志台机器可以用 IP或者是用域名.那么我们怎么去进行转换呢? struct hostent *gethostbyname(const char *hostname) struct hostent *gethostbyaddr(const char *addr,int len,int type) 在<netdb.h>;中有 struct hostent的定义 struct hostent{ char *h_name; /* 主机的正式名称 */ char *h_aliases; /* 主机的别名 */ int h_addrtype; /* 主机的地址类型 AF_INET*/ int h_length; /* 主机的地址长度 对于IP4 是 4 字节 32 位*/ char **h_addr_list; /* 主机的 IP地址列表 */ } #define h_addr h_addr_list[0] /* 主机的第个 IP地址*/ gethostbyname 可以将机器名(linux.yessun.com)转换为个结构指针.在这个结构里 面储存了域名的信息 gethostbyaddr可以将个 32 位的 IP地址(C0A80001)转换为结构指针. 这两个函数失败时返回 NULL 且设置 h_errno 错误变量,调用 h_strerror()可以得到详细的 出错信息 字符串的 IP 和 32位的 IP 转换. 在网络上面我们用的 IP都是数字加点(192.168.0.1)构成的, 而在 struct in_addr结构中 用的是 32 位的 IP, 我们上面那个 32 位IP(C0A80001)是的 192.168.0.1 为了转换我们可以 使用下面两个函数 int inet_aton(const char *cp,struct in_addr *inp) char *inet_ntoa(struct in_addr in) 函数里面 a 代表 ascii n 代表 network.第个函数表示将 a.b.c.d 的 IP转换为 32 位的 I P,存储在 inp 指针里面.第二个是将 32 位 IP转换为 a.b.c.d 的格式 服务信息函数 在网络程序里面我们有时候需要知道端口.IP和服务信息.这个时候我们可以使用以下几 个函数 int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen) int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen) struct servent *getservbyname(const char *servname,const char *protoname) struct servent *getservbyport(int port,const char *protoname) struct servent { char *s_name; /* 正式服务名 */ char **s_aliases; /* 别名列表 */ int s_port; /* 端口号 */ char *s_proto; /* 使用的协议 */ } 般我们很少用这几个函数.对应客户端,当我们要得到连接的端口号时在 connect调用成 功后使用可得到 系统分配的端口号.对于服务端,我们用 INADDR_ANY 填充后,为了得到连 接的 IP我们可以在 accept 调用成功后 使用而得到IP地址. 在网络上有许多的默认端口和服务,比如端口 21 对 ftp80 对应 WWW.为了得到指定的端口号 的服务 我们可以调用第四个函数,相反为了得到端口号可以调用第三个函数.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值