本书同时介绍Linux C编程和Windows C编程两种网络编程,这里本人也将按照书上的顺序对这两种网络编程一块进行学习
本章主要是对socket编程有一个主要的认识,然后接下来的章节再进入细节进行学习
初见网络编程和套接字
套接字操作常用函数:
#include <sys/socket.h>
// 套接字由socket函数生成,成功时返回文件描述符,失败返回-1
int socket(int domain, int type, int protocol);
// 给套接字分配地址信息,成功返回0,失败返回-1
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// 将套接字转化为可接收连接的状态,成功返回0,失败返回-1
int listen(int sockfd, int backlog);
// 接收套接字连接请求,成功返回文件描述符,失败返回-1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// 发起套接字连接,成功返回0,失败返回-1
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
创建接收请求的套接字流程如下:
- 调用socket函数创建套接字
- 调用bind函数分配端口号和地址
- 调用listen函数将套接字转换为可接受连接状态
- 调用accept函数接收连接请求
客户端向服务端发起连接流程:
- 调用socket函数创建套接字
- 调用connect函数向服务器发起请求
服务端运行完成之后无法立即重新运行,如果需要再次运行需要修改端口号,原因后面会说
实验代码
服务端
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string.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;
sockaddr_in serv_addr;
sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char* message = "hello ,there is selph!";
if (argc != 2)
{
printf("Usage: %s <port>\n", argv[0]);
}
// 创建服务端socket
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,(const sockaddr *)&serv_addr,sizeof(serv_addr)) == -1){
error_handling("bind() error");
}
// 监听地址和端口
if(listen(serv_sock,5) == -1){
error_handling("listen() error");
}
puts("Server is listening...");
// 接收客户端请求套接字
clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock,(sockaddr*)&clnt_addr,&clnt_addr_size);
if(serv_sock == -1)error_handling("accept() error");
// 向套接字写入内容
write(clnt_sock, message,strlen(message));
// 关闭创建的套接字
close(serv_sock);
close(clnt_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
客户端
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
int str_len;
sockaddr_in serv_addr;
char message[30] ={0};
if (argc != 3)
{
printf("Usage: %s <IP> <port>\n", argv[0]);
}
// 创建socket
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,(const sockaddr*)&serv_addr,sizeof(serv_addr)) == -1){
error_handling("connect() error");
}
puts("Connecting...");
// 从套接字读取内容
str_len = read(sock, message,sizeof(message)-1);
if(str_len == -1)error_handling("read() error");
printf("Message From Server(%d byte): %s\n",str_len,message);
// 关闭套接字
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
运行结果
服务端:
selph@selph:~/NetProgramStudy/ch1$ ./hello_server 8888
Server is listening...
客户端:
selph@selph:~/NetProgramStudy/ch1$ ./hello_client 127.0.0.1 8888
Connecting...
Message From Server(22 byte): hello ,there is selph!
基于Linux的文件操作
在Linux下,一切皆文件,所以套接字也是文件的一种,文件由文件标识符(File Descriptor)来标识,基于Linux系统API的文件操作,使用文件标识符来进行操作;类似在Windows上使用的句柄,Windows上句柄是单独一套操作函数,Linux下则都是使用文件操作函数
实验代码
基本的文件操作的使用:文件打开,写入,修改文件指针,读取
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char* argv[]){
int fd;
char w_buf[]="Let's it go o o!\n";
char r_buf[64]={0};
// 以可读可写,不存在就创建的方式打开文件
fd = open("data.txt",O_CREAT | O_RDWR | O_TRUNC);
if(fd == -1)exit(1);
printf("File Descriptor:%d\n",fd);
// 写入buf到文件,会修改文件指
if(write(fd,w_buf,sizeof(w_buf)) == -1)exit(1);
// 移动文件指针到文件头
lseek(fd,0,SEEK_SET);
// 读取文件内容并打印
if(read(fd,r_buf,sizeof(r_buf)) == -1)exit(1);
printf("%s\n",r_buf);
close(fd);
return 0;
}
运行结果
selph@selph:~/NetProgramStudy/ch1$ ./"low_file_op"
File Descriptor:3
Let's it go o o!
基于Windows平台的实现
Windows套接字Winsock,大部分是参考BSD系列UNIX套接字设计的,很多地方跟Linux相似,两大平台的套接字编程很相似,甚至连函数名都很相似
与Linux不同的是,Windows平台套接字编程,需要对winsock进行初始化和注销操作,对socket的IO操作是send和recv,Linux是write和read,其他基本上是一样的就
实验代码
服务端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[]) {
SOCKET hSock;
SOCKADDR_IN servAddr, clntAddr;
WSADATA wsaData;
int szClntAddr;
int str_len;
char message[64] = { 0 };
if (argc != 3) {
exit(1);
}
// winsock初始化
if (WSAStartup(MAKEWORD(2, 2), &wsaData))exit(1);
// 创建socket
hSock = socket(PF_INET, SOCK_STREAM, 0);
if (hSock == INVALID_SOCKET)exit(1);
// 填充服务端地址结构体
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);
servAddr.sin_port = htons(atoi(argv[2]));
// 向服务器发起连接
if(connect(hSock, (const sockaddr*)&servAddr, sizeof(sockaddr)) == SOCKET_ERROR)exit(1);
// 接收信息
str_len = recv(hSock, message, sizeof(message) - 1, 0);
if (str_len == -1)exit(1);
printf("Msg From Server: %s\n",message);
// 关闭套接字句柄
closesocket(hSock);
// 卸载winsock
WSACleanup();
return 0;
}
客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[]) {
SOCKET hSock;
SOCKADDR_IN servAddr, clntAddr;
WSADATA wsaData;
int szClntAddr;
int str_len;
char message[64] = { 0 };
if (argc != 3) {
exit(1);
}
// winsock初始化
if (WSAStartup(MAKEWORD(2, 2), &wsaData))exit(1);
// 创建socket
hSock = socket(PF_INET, SOCK_STREAM, 0);
if (hSock == INVALID_SOCKET)exit(1);
// 填充服务端地址结构体
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);
servAddr.sin_port = htons(atoi(argv[2]));
// 向服务器发起连接
if(connect(hSock, (const sockaddr*)&servAddr, sizeof(sockaddr)) == SOCKET_ERROR)exit(1);
// 接收信息
str_len = recv(hSock, message, sizeof(message) - 1, 0);
if (str_len == -1)exit(1);
printf("Msg From Server: %s\n",message);
// 关闭套接字句柄
closesocket(hSock);
// 卸载winsock
WSACleanup();
return 0;
}
运行结果
PS C:\Users\selph\source\Book\TCPIP\ch1\x64\Debug> .\hello_clnt_win.exe 127.0.0.1 9999
Msg From Server: hello selph
参考资料
- 《TCPIP网络编程》