西宁建设工程官方网站,wordpress woocommerce 主题,榆林网站建设公司,wordpress static page《TCP/IP网络编程》学习笔记 | Chapter 1#xff1a;理解网络编程和套接字 《TCP/IP网络编程》学习笔记 | Chapter 1#xff1a;理解网络编程和套接字基本概念服务端客户端 基于 Linux 平台的 Hello world! 服务端和客户端基于 Linux 的文件操作打开文件关闭文件… 《TCP/IP网络编程》学习笔记 | Chapter 1理解网络编程和套接字 《TCP/IP网络编程》学习笔记 | Chapter 1理解网络编程和套接字基本概念服务端客户端 基于 Linux 平台的 Hello world! 服务端和客户端基于 Linux 的文件操作打开文件关闭文件写文件读文件 基于 Windows 平台的实现Winsock 的初始化注销 Winsock 相关库基于 Windows 的套接字相关函数创建基于 Windows 的服务端和客户端基于 Windows 的 I/O 函数 习题1套接字在网络编程中的作用是什么为什么称它为套接字2在服务器端创建套接字后会依次调用listen函数和accept函数。请比较并说明两者作用。3Linux中对套接字数据进行I/O时可以直接使用I/O相关函数而在Windows中则不可以。原因为何4创建套接字后一般会给它分配地址为什么为了完成地址分配需要调用哪些函数5Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。6底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别 《TCP/IP网络编程》学习笔记 | Chapter 1理解网络编程和套接字
基本概念
网络编程是什么
编写程序使两台联网的计算机相互交换数据。
服务端
步骤 1创建套接字
int socket(int domain, int type, int protocol);成功时返回文件描述符失败时返回-1。
步骤 2绑定IP地址和端口号
int bind(int socket, const struct sockaddr *address, socklen_t address_len);成功时返回0失败时返回-1
步骤 3使转化为可接受请求状态
int listen(int sockfd, int backlog);成功时返回文件描述符失败时返回-1。
步骤 4受理请求连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);成功时返回文件描述符失败时返回-1。
客户端
步骤 1创建socket
步骤 2发起连接请求
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);成功时返回0失败返回-1。
基于 Linux 平台的 “Hello world!” 服务端和客户端
服务端程序
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.hvoid error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];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_porthtons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)serv_addr,sizeof(serv_addr))-1){error_handling(connect() 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);
}客户端程序
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.hvoid error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];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_porthtons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)serv_addr,sizeof(serv_addr))-1){error_handling(connect() 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 的文件操作
Linux中一切皆文件socket自然也是文件。
每当生成文件或socket操作系统都将返回分配给它们的整数文件描述符文件描述符只不过是为了方便的称呼操作系统创建的文件或socket而赋予的数。
分配给标准输入输出及标准错误的文件描述符。
文件描述符对象0标准输入 Standard Input1标准输出 Standard Ouput2标准错误 Standard Error
其他的文件描述符从3开始的。
打开文件
#include sys/stat.h
#include fcntl.hint open(const char *path, int fla);成功时返回文件描述符失败时返回-1。
参数 path: 文件名的字符串地址 flag: 文件打开模式信息如需传递多个参数应通过 | 运算符组合。
打开模式含义O_CREAT必要时创建文件O_TRUNC删除全部现有数据O_APPEND维持现有数据保存到其后面O_RDONLY只读打开O_WRONLY只写打开O_RDWR读写打开
关闭文件
#include unistd.hint close(int fildes);成功时返回0失败时返回-1。
参数
fildes 需要关闭的文件或socket的文件描述符
写文件
#include unistd.hssize_t write(int fildes, const void *buf, size_t nbyte);成功时返回写入的字节数失败时返回-1。
参数 fildes 显示数据传输对象的文件描述符 buf保存要传输数据的缓冲地址值 nbyte 要传输数据的字节数 size_t : unsigned intssize_tsigned int这些都是元数据类型由操作系统定义通过 typedef 声明。 示例程序
#include stdio.h
#include stdlib.h
#include fcntl.h
#include unistd.hvoid error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(void)
{int fd;char buf[] Lets 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)error_handling(write() error!);close(fd);return 0;
}运行结果 注意Windows 端可以用 type 命令打印 txt 文件的内容。 读文件
#include unistd.hssize_t read(int fildes, void *buf, size_t nbyte);成功时返回接收的字节数但遇到文件结尾则返回0失败时返回-1。
参数 fildes显示数据接收对象的文件描述符 buf要保存接收数据的缓冲地址值 nbytes要接收数据的最大字节数
示例程序
#include stdio.h
#include stdlib.h
#include fcntl.h
#include unistd.h#define BUF_SIZE 100void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(void)
{int fd;char buf[BUF_SIZE];fd open(data.txt, O_RDONLY);if (fd -1)error_handling(open() error!);printf(file descriptor: %d\n, fd);if (read(fd, buf, BUF_SIZE) -1)error_handling(read() error!);printf(file data: %s, buf);close(fd);return 0;
}
运行结果 基于 Windows 平台的实现
Windows 套接字简称 Winsock大部分参考 BSD 系列 UNIX 套接字设计和 Linux 套接字类似。
Winsock 的初始化
进行Winsock编程时首先调用 WSAStartup 函数设置程序中用到的Winsock版本并初始化相应版本的库。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);参数
wVersionRequestedWinsock版本信息lpWSADataWSADATA结构体变量的地址值
Winsock中存在多个版本应准备WORDtypedef unsigned int WORD;类型的套接字版本信息若版本为1.2则1是主版本号2是副版本号应传递 0x0201
0x0201 高8位为副版本号低8位为主版本号。这里借助 MAKEWORD 宏函数来传递版本信息号。
MAKEWORD(1,2); // 主版本号是1副版本号是2返回0x0201int main(int argc,char* argv[])
{WSADATA wsaData;....if(WSAStartup(MAKEWORD(1,2), wsaData) ! 0){....}return 0;
}注销 Winsock 相关库
#include Winsock2.hint WSACleanup(void);成功时返回0失败时返回 SOCKET_ERROR。
基于 Windows 的套接字相关函数
创建socket
SOCKET socket(int af,int type,int protocol);成功时返回套接字句柄对应linux中的文件描述符失败返回 INVALID_SOCKET。
绑定IP和端口号
int bind(SOCKET s,const struct sockaddr* name,int namelen);成功返回0失败返回SOCKET_ERROR。
将服务端设置为可接受请求状态
int listen(SOCKET s,int backlog);成功返回0失败返回SOCKET_ERROR。
受理请求连接accept
SOCKET accept(SOCKET s,struct sockaddr* addr,int* addrlen);成功时返回套接字句柄失败时返回INVALID_SOCKET。
从客户端发起请求连接
int connect(SOCKET s,const struct sockaddr* name,int namelen);成功时返回0失败时返回SOCKET_ERROR。
关闭套接字
int closesocket(SOCKET s);成功时返回0失败时返回SOCKET_ERROR。 Windows中的句柄相当于linux中的文件描述符但是Windows中的文件句柄和套接字句柄是有区别的。 创建基于 Windows 的服务端和客户端
服务端
#include stdio.h
#include stdlib.h
#include winsock2.hvoid ErrorHanding(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServerSock, hClientSock;SOCKADDR_IN serverAddr, clientAddr;int szClientAddr;char message[] Hello World!;if (argc ! 2){printf(Usage: %s port\n, argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), wsaData) ! 0)ErrorHanding(WSAStartup() error!);hServerSock socket(PF_INET, SOCK_STREAM, 0);if (hServerSock INVALID_SOCKET)ErrorHanding(socket() error!);memset(serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family AF_INET;serverAddr.sin_addr.s_addr htonl(INADDR_ANY);serverAddr.sin_port htons(atoi(argv[1]));if (bind(hServerSock, (SOCKADDR *)serverAddr, sizeof(serverAddr)) SOCKET_ERROR)ErrorHanding(bind() error!);if (listen(hServerSock, 5) SOCKET_ERROR)ErrorHanding(listen() error!);szClientAddr sizeof(clientAddr);hClientSock accept(hServerSock, (SOCKADDR *)clientAddr, szClientAddr);if (hClientSock INVALID_SOCKET)ErrorHanding(accept() error!);send(hClientSock, message, sizeof(message), 0);closesocket(hClientSock);closesocket(hServerSock);WSACleanup();return 0;
}客户端
#include stdio.h
#include stdlib.h
#include winsock2.hvoid ErrorHanding(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN serverAddr;char message[30];int strLen;if (argc ! 3){printf(Usage: %s IP port\n, argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), wsaData) ! 0)ErrorHanding(WSAStartup() error!);hSocket socket(PF_INET, SOCK_STREAM, 0);if (hSocket INVALID_SOCKET)ErrorHanding(socket() error!);memset(serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family AF_INET;serverAddr.sin_addr.s_addr inet_addr(argv[1]);serverAddr.sin_port htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)serverAddr, sizeof(serverAddr)) SOCKET_ERROR)ErrorHanding(connect() error!);strLen recv(hSocket, message, sizeof(message) - 1, 0);if (strLen -1)ErrorHanding(recv() error!);printf(Message from server: %s\n, message);closesocket(hSocket);WSACleanup();return 0;
}编译遇到报错 参考 【三种解决方法】undefined reference to __imp_WSAStartup‘ 给出的解决办法手动添加编译参数 -lwsock32
gcc hello_server_win.c -lwsock32 -o hServerWingcc hello_client_win.c -lwsock32 -o hClientWin编译就可以成功了 我们运行服务端程序监听 9190 端口
hServerWin 9190再运行客户端程序
hClientWin 127.0.0.1 9190得到输出
Message from server: Hello World!基于 Windows 的 I/O 函数
Linux中socket也是文件因此可以通过文件I/O函数read和write进行数据传输。而Windows严格区分文件I/O函数和套接字I/O函数。
int send(SOCKET s,const char* buf,int len,int flags);成功时返回传输字节数失败时返回SOCKET_ERROR。
参数
s表示数据传输对象连接的套接字句柄值buf保存待传输数据的缓冲地址值len要传输的字节数flags传输数据时用到的多种选项信息
int recv(SOCKET s, const char* buf, int len, int flags);成功时返回接收的字节数收到EOF时为0失败返回SOCKET_ERROR。
s表示数据接收对象连接的套接字句柄值buf保存待接收数据的缓冲地址值len要接收的字节数flags接收数据时用到的多种选项信息
习题
1套接字在网络编程中的作用是什么为什么称它为套接字
网络编程就是编写程序让两台联网的计算机相互交换数据。在我们不需要考虑物理连接的情况下我们只需要考虑如何编写传输软件。操作系统提供了名为“套接字”套接字是网络传输传输用的软件设备。
socket英文原意是插座我们把插头插到插座上就能从电网获得电力供给同样为了与远程计算机进行数据传输需要连接到Internet,而变成中的“套接字”就是用来连接该网络的工具。
2在服务器端创建套接字后会依次调用listen函数和accept函数。请比较并说明两者作用。
listen将套接字转为可接受连接方式监听套接字
accept受理连接请求并且在没有连接请求的情况调用该函数不会返回阻塞。直到有连接请求为止。二者存在逻辑上的先后关系。
3Linux中对套接字数据进行I/O时可以直接使用I/O相关函数而在Windows中则不可以。原因为何
Linux把套接字也看作是文件所以可以用文件I/O相关函数而Windows要区分套接字和文件所以设置了特殊的函数。
4创建套接字后一般会给它分配地址为什么为了完成地址分配需要调用哪些函数
要在网络上区分来自不同机器的套接字所以需要地址信息。分配地址是通过bind()函数实现。
5Linux中的文件描述符与Windows的句柄实际上非常类似。请以套接字为对象说明他们的含义。
Linux的文件描述符是为了区分指定文件而赋予文件的整数值相当于编号。Windows的文件描述符其实也是套接字的整数值其目的也是区分指定套接字。
6底层文件I/O函数与ANSI标准定义的文件I/O函数之间有何区别
ANSI标准定义的输入、输出函数是与操作系统内核无关的以C标准写成的函数相反底层文件I/O函数是操作系统直接提供的。标准I/O分为全缓冲行缓冲不缓冲三种形式文件I/O为不带缓冲的I/O。文件I/O主要针对文件操作它操作的是文件描述符标准I/O针对的是控制台它操作的是字符流。对于不同设备得特性不一样必须有不同API访问才最高效。