第一章 理解网络编程和套接字
重点是套接字的使用:几个函数
socket() bind() listen() accept() connect()
几个函数的使用:
socke函数的使用:
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
成功返回文件描述符,失败返回-1
bind函数的使用
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr *myaddr,socklen_taddrlen);
成功返回0,失败返回-1
listen函数的使用
#include <sys/socket.h>
int listen(int sockfd,int backlog);
成功返回0,失败返回-1
accept函数的使用
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
connect的函数的使用
#include <sys/socket.h>
int connect(int sockfd,struct sockaddr* aerv_addr,socklen_t addrlen);
成功返回0,失败返回-1
需要掌握的知识点:
需要重点记忆的俩概念
网络编程:编写程序是两台联网的结算及相互交换数据
套接字(socket):网络数据传输用的软件设备
以接电话为例讲述构建套接字的过程:
1.调用socket函数(安装电话机):下面的函数创建的就是相当于电话机的套接字
#include <sys/socket.h>
int socket(int domain,int type,int protocol); 成功返回文件描述符,失败返回-1
2.调用bind函数(分配电话号码):用下面函数给创建好的套接字分配地址信息(IP地址和端口号
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr *myaddr,socklen_taddrlen);
成功返回0,失败返回-1
3.调用listen函数(连接电话线):一旦连接电话线,表示电话机为可接听状态,这是其他人可以拨打电话请求连接到该电话机,同样,这个函数就是把套接字转换成可接受连接的状态
#include <sys/socket.h>
int listen(int sockfd,int backlog);
成功返回0,失败返回-1
4.调用accept函数 接受对方的连接请求
调用accept函数(拿起话筒):意味着接受了对方的连接请求,套接字同样如此,如果有人为了完成数据传输而请求连接,就需要调用以下的函数进行受理
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
重点:
总结一下:网络编程中接受连接请求的套接字创建过程如下
调用socket函数创建套接字
调用bind函数分配ip地址和端口号
调用listen函数转为可接受请求状态
调用accept函数受理连接请求
测试程序 敲一遍感受一下建立套接字的过程:
1 服务器端 hello_server.c
#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;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "Hello world";
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == -1){
error_handling("socket() error");
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
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);
}
2 客户端程序 hello_client.c
#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 sock;
struct sockaddr_in serv_addr;
char message[] = "Hello world";
int str_len;
if(argc != 3){
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1){
error_handling("socket() error");
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]))
if(connect(sock,(struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){
error_handling("bind() error");
}
str_len = read(sock,message,sizeof(message) - 1);
if(str_len == -1){
error_handling("read() error!");
}
printf("message from server : %s\n",message );
close(sock);
return 0;
}
void error_handling(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
在这里插入代码片
========================================================================================================
基于Linux的文件操作:
主要内容:
文件描述符:系统分配给文件或套接字的整数。
标准输入 输出 错误 的文件描述符分别为:0,1,2
文件IO函数: open close write read
几个函数的具体实现:
open函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* path,int flag);
成功返回文件描述符,失败返回-1
close函数:
#include <unistd.h>
int close(int fd); //fd为文件 、 套接字的描述符
成功返回0,失败返回-1
write函数:
// fd:数据传输对象的文件描述符;buf:保存要传输数据的缓冲地址;nbytes:要传输数据的字节数
// 其中size_t: typedef unsigned int size_t; ssize_t: typedef signed int ssize_t
#include <unistd.h>
ssize_t write(int fd,const void * buf,size_t nbytes)
read函数
#include <unistd.h>
/** fd:显示数据接受对象的文件描述 buf:要保存的数据的缓冲地址值 nbytes:要接收数据的最大字节数*/
ssize_t read(int fd, void* buf,size_t nbytes);
成功时返回接收的字节数,(但遇到文件结尾则返回0),失败返回-1
-------------------------------------------------------------
基础知识:
文件描述符:系统分配给文件或套接字的整数。
文件描述符(文件句柄)(在LInux下为描述符,Win下称作句柄)
1.2.2 打开文件
需要两个参数:第一个是目标文件的文件名路径信息path,第二个是文件打开模式flag
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* path,int flag);
成功返回文件描述符,失败返回-1
1.2.3 关闭文件
#include <unistd.h>
int close(int fd); //fd为文件 、 套接字的描述符
成功返回0,失败返回-1
在这里插入代码片
注意:此函数可以关闭文件,也可以关闭套接字,再次体现了Linux系统不区分文件和套接字的特点
1.2.4 将数据写入文件
// fd:数据传输对象的文件描述符;buf:保存要传输数据的缓冲地址;nbytes:要传输数据的字节数
// 其中size_t: typedef unsigned int size_t; ssize_t: typedef signed int ssize_t
#include <unistd.h>
ssize_t write(int fd,const void * buf,size_t nbytes)
知识扫盲:以_t为后缀的数据类型
一般为了给基本数据类型赋别名,会在头文件 sys/types.h 中大量 typedef 声明,为了同程序员定义的新数据类型区分,操作系统定义的数据类型后会添加_t
通过以下实例来理解write函数
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void error_handling(char* message);
int main(void)
{
/* code */
int fd;
char buf[] = "Let's go!\n"; //写入的字符串内容
fd = open("data.txt",O_CREAT|O_WRONLY|O_TRUNC); //打开文件,创建|只写|清空
if(fd == -1){
error_handling("open() error!"); //抛出异常
}
printf("file descriptor: %d\n",fd);
if(write(fd,buf,sizeof(buf)) == -1){ //向文件fd写入,字符串buf,字节数sizeof
error_handling("write() error!");
}
close(fd); //关闭文件
return 0;
}
void error_handling(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
1.2.5 读取文件中的数据
read函数的使用
#include <unistd.h>
/** fd:显示数据接受对象的文件描述 buf:要保存的数据的缓冲地址值 nbytes:要接收数据的最大字节数*/
ssize_t read(int fd, void* buf,size_t nbytes);
成功时返回接收的字节数,(但遇到文件结尾则返回0),失败返回-1
通过以下实例来理解:
文件名字;low_read.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 100
void error_handling(char* message);
int main(void)
{
int fd;
char buf[BUF_SIZE];
fd = open("data.txt",O_RDONLY); //打开想要读取的文件 并返回文件描述符fd
if(fd == -1){
error_handling("open() error\n"); //如果打开失败,抛出异常
}
printf("file descriptor : %d\n",fd );
if(read(fd,buf,sizeof(buf)) == -1){ //读取文件fd的内容,存入buf中,sizeof字节数
error_handling("read() error!");
}
printf("file data: %s\n",buf);
close(fd); //关闭文件
return 0;
}
void error_handling(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
1.2.7 文件描述符和套接字
下面将同时创建文件和套接字,并用整数形态比较返回的文件描述符值
文件名:fd_seri.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
int main(void)
{
int fd1,fd2,fd3;
fd1 = socket(PF_INET,SOCK_STREAM,0); //这里创建一个文件和两个套接字
fd2 = open("test.dat",O_CREAT|O_WRONLY|O_TRUNC);
//套接字其实在Linux系统中和文件真的是一样的,在下面还要关闭它,sokect就是打开了文件
fd3 = socket(PF_INET,SOCK_DGRAM,0);
printf("file descriptor 1: %d\n",fd1);
printf("file descriptor 2: %d\n",fd2);
printf("file descriptor 3: %d\n",fd3);
close(fd1);close(fd2);close(fd3);
return 0;
}
========================================================================================================