TCP/IP
[解决c++socket报错](https://blog.csdn.net/weixin_39591031/article/details/115222662):手动编译文件,在编译命令后添加一个参数-lwsock32windows上无法编译,可以将gcc改成g++
C++简易网络编程
string和char数组之间的相互转换
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
char a[1024];
string str="hello world";
strcpy(a,str.c_str());
cout<<a<<endl;
string b="";
b=a;
cout<<b<<endl;
system("pause");
return 0;
}
Windows TCP示例
服务端
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 100
int main(){
WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0);
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
//非具体IP地址
//sockAddr.sin_addr.s_addr=inet_addr(argv[1]);
//sockAddr.sin_addr.s_addr=htonl(INADDR_ANY)
sockAddr.sin_port = htons(1234); //端口
//输入参数第一个
//servAddr.sin_port=htons(atoi(argv[1]));
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进入监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
char buffer[BUF_SIZE]; //缓冲区
int strLen = recv(clntSock, buffer, BUF_SIZE, 0); //接收客户端发来的数据
send(clntSock, buffer, strLen, 0); //将数据原样返回
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 100
int main(){
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(1234);
connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//获取用户输入的字符串并发送给服务器
char bufSend[BUF_SIZE] = {0};
printf("Input a string: ");
scanf("%s", bufSend);
send(sock, bufSend, strlen(bufSend), 0);
//接收服务器传回的数据
char bufRecv[BUF_SIZE] = {0};
recv(sock, bufRecv, BUF_SIZE, 0);
//输出接收到的数据
printf("Message form server: %s\n", bufRecv);
//关闭套接字
closesocket(sock);
//终止使用 DLL
WSACleanup();
system("pause");
return 0;
}
Windows TCP简易计算器
服务端
#include <iostream>
#include <WinSock2.h>
#include <string.h>
using namespace std;
int calculate(int count,int *operand,char *ope)
{
int res=operand[0];
switch (ope[0])
{
case '+':
for(int i=1;i<count;i++)
res+=operand[i];
break;
case '-':
for(int i=1;i<count;i++)
res-=operand[i];
break;
case '*':
for(int i=1;i<count;i++)
res*=operand[i];
break;
case '/':
for(int i=1;i<count;i++)
res/=operand[i];
break;
}
return res;
}
int main(int argc, char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = htons(atoi(argv[1]));
bind(sock, (sockaddr *)&sock_addr, sizeof(sock_addr));
listen(sock, 5);
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
for (int i = 0; i < 5; i++)
{
SOCKET clntSock = accept(sock, (SOCKADDR *)&clntAddr,&nSize);
// char count[4];
char count[1];
recv(clntSock,count,1,0);
int operand_count=count[0];
char operand[50];
for(int i=0;i<operand_count;i++)
{
recv(clntSock,&operand[i*4],4,0);
// cout<<operand[i]<<endl;
}
char ope[1];
recv(clntSock,ope,1,0);
int result=calculate(operand_count,(int*)operand,ope);
char sendMsg[4];
//int类型强制转换char*类型
send(clntSock,(char*)&result,sizeof(result),0);
closesocket(clntSock);
}
closesocket(sock);
WSACleanup();
return 0;
}
客户端
#include<iostream>
#include<WinSock2.h>
#include<string.h>
#define BUFSIZE 50
#define OPSZ 4
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
char msg[BUFSIZE];
SOCKET sock=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN sock_addr;
memset(&sock_addr,0,sizeof(sock_addr));
sock_addr.sin_family=AF_INET;
sock_addr.sin_addr.s_addr=inet_addr(argv[1]);
sock_addr.sin_port=htons(atoi(argv[2]));
connect(sock,(sockaddr*)&sock_addr,sizeof(sock_addr));
cout<<"connected..."<<endl;
int operand_count;
cout<<"Operand count: ";
cin>>operand_count;
msg[0]=(char)operand_count;//注意范围为0=127
for(int i=0;i<operand_count;i++)
{
cout<<"Operand "<<i+1<<": ";
//将msg4个位作为一个数字的存储空间传输数值地址
int *p=(int*)&msg[i*OPSZ+1];
cin>>*p;
}
cout<<"Operator: ";
cin>>msg[operand_count*OPSZ+1];
send(sock,msg,operand_count*OPSZ+2,0);
char res[4];
recv(sock,res,OPSZ,0);
int *res_num=(int*)res;//res得到的是数值的地址,需要先转换为(int*)类型,注意该类型为int数组指针
cout<<"Operation result: "<<*res_num<<endl;
closesocket(sock);
WSACleanup();
system("pause");
return 0;
}
Windows UDP示例
服务端
#include<iostream>
#include<WinSock2.h>
#include<string.h>
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
SOCKADDR clntAddr;
int nSize=sizeof(SOCKADDR);
char buf[50];
while(true)
{
int str_len=recvfrom(sock,buf,nSize,0,&clntAddr,&nSize);
sendto(sock,buf,str_len,0,&clntAddr,nSize);
}
closesocket(sock);
WSACleanup();
return 0;
}
客户端(无套接字版本)
#include<iostream>
#include<WinSock2.h>
#include<string.h>
#define bufSize 50
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET sock=socket(AF_INET,SOCK_DGRAM,0);
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
SOCKADDR fromAddr;
int addr_len=sizeof(fromAddr);
while(1)
{
char buf[bufSize];
cout<<"Input a string(q to quit): ";
cin.getline(buf,bufSize);
if(!strcmp(buf,"Q")||!strcmp(buf,"q")) break;
sendto(sock,buf,bufSize,0,&fromAddr,addr_len);
int strLen=recvfrom(sock,buf,bufSize,0,&fromAddr,&addr_len);
buf[strLen]=0;
cout<<"Message from server: "<<buf<<endl;
}
closesocket(sock);
WSACleanup();
return 0;
}
客户端(有套接字版本)
#include<iostream>
#include<WinSock2.h>
#include<string.h>
#define bufSize 50
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET sock=socket(AF_INET,SOCK_DGRAM,0);
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
SOCKADDR fromAddr;
int addr_len=sizeof(fromAddr);
connect(sock,(sockaddr*)&addr,sizeof(addr));
while(1)
{
char buf[bufSize];
cout<<"Input a string(q to quit): ";
cin.getline(buf,bufSize);
if(!strcmp(buf,"Q")||!strcmp(buf,"q")) break;
// sendto(sock,buf,bufSize,0,&fromAddr,addr_len);
// int strLen=recvfrom(sock,buf,bufSize,0,&fromAddr,&addr_len);
send(sock,buf,strlen(buf),0);
int strLen=recv(sock,buf,bufSize,0);
buf[strLen]=0;
cout<<"Message from server: "<<buf<<endl;
}
closesocket(sock);
WSACleanup();
return 0;
}
Windows传输文件
- 需要设定一个结束符,否则会卡死
- 注意fstream只有ios::out下会创建文件,其它模式下会报错
服务器
#include<iostream>
#include<WinSock2.h>
#include<fstream>
#include<string.h>
#include<string>
const static int bufSize=50;
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET sock=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr,clntAddr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
bind(sock,(sockaddr*)&addr,sizeof(addr));
listen(sock,5);
char buf[bufSize];
memset(&buf,0,bufSize);
fstream file;
file.open("1.txt",ios::in|ios::binary);
int clntAddr_size=sizeof(sockaddr);
SOCKET clntSock=accept(sock,(sockaddr*)&clntAddr,&clntAddr_size);
while(true)
{
if(file.eof()){
send(clntSock,"\n",8,0);
break;
}
file.getline(buf,bufSize);
send(clntSock,buf,bufSize,0);
}
recv(clntSock,buf,bufSize,0);
cout<<"Message from client: "<<buf<<endl;
file.close();
closesocket(clntSock);
closesocket(sock);
WSACleanup();
return 0;
}
客户端
#include<iostream>
#include<fstream>
#include<WinSock2.h>
#include<string.h>
static const int bufSize=50;
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
SOCKET sock=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
connect(sock,(sockaddr*)&addr,sizeof(addr));
char buf[bufSize];
fstream file("2.txt",ios::out|ios::binary);
while(true)
{
recv(sock,buf,bufSize,0);
if(!strcmp(buf,"\n")) break;
file<<buf<<endl;
}
cout<<"Copy succeed!"<<endl;
send(sock,"Thanks for file!",17,0);
file.close();
closesocket(sock);
WSACleanup();
return 0;
}
Windows回声I/O复用
服务端
#include<iostream>
#include<WinSock2.h>
#include<string.h>
#define BUFSIZE 1024
using namespace std;
void ErrorHandle(string msg)
{
cerr<<msg<<endl;
exit(1);
}
int main(int argc,char **argv)
{
WSADATA wsa;
if(WSAStartup(MAKEWORD(2,2),&wsa)!=0)
ErrorHandle("WSAStartup() error!");
TIMEVAL timeout;
fd_set rd,cpy_rd;
SOCKET sock,clntSock;
sockaddr_in addr,clntAddr;
char msg[BUFSIZE];
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(bind(sock,(sockaddr*)&addr,sizeof(addr))==SOCKET_ERROR)
ErrorHandle("bind() error");
if(listen(sock,5)==-1)
ErrorHandle("listen() error");
FD_ZERO(&rd);
FD_SET(sock,&rd);//×¢²ásock
int strLen,fdNum,i,adr_sz;
while(1)
{
cpy_rd=rd;
timeout.tv_sec=5;
timeout.tv_usec=5000;
fdNum=select(0,&cpy_rd,0,0,&timeout);
if(-1==fdNum)
break;
if(0==fdNum)
continue;
for(i=0;i<rd.fd_count;i++)
{
if(FD_ISSET(rd.fd_array[i],&cpy_rd))
{
if(rd.fd_array[i]==sock)
{
adr_sz=sizeof(clntAddr);
clntSock=accept(sock,(sockaddr*)&clntAddr,&adr_sz);
FD_SET(clntSock,&rd);
cout<<"connected client: "<<clntSock<<endl;
}
else
{
strLen=recv(rd.fd_array[i],msg,BUFSIZE-1,0);
if(0==strLen)
{
FD_CLR(rd.fd_array[i],&rd);
closesocket(cpy_rd.fd_array[i]);
cout<<"closed client: "<<cpy_rd.fd_array[i]<<endl;
}
else
{
send(rd.fd_array[i],msg,strLen,0);
}
}
}
}
}
closesocket(sock);
WSACleanup();
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#define BUF_SIZE 1024
void ErrorHandling(char *message);
int main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET hSocket;
char message[BUF_SIZE];
int strLen;
SOCKADDR_IN servAdr;
if(argc!=3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
ErrorHandling("WSAStartup() error!");
hSocket=socket(PF_INET, SOCK_STREAM, 0);
if(hSocket==INVALID_SOCKET)
ErrorHandling("socket() error");
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family=AF_INET;
servAdr.sin_addr.s_addr=inet_addr(argv[1]);
servAdr.sin_port=htons(atoi(argv[2]));
if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
ErrorHandling("connect() error!");
else
puts("Connected...........");
while(1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
break;
send(hSocket, message, strlen(message), 0);
strLen=recv(hSocket, message, BUF_SIZE-1, 0);
message[strLen]=0;
printf("Message from server: %s", message);
}
closesocket(hSocket);
WSACleanup();
return 0;
}
void ErrorHandling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
Linux TCP示例
服务器
#include <stdio.h>
#include <string.h>//memset
#include <stdlib.h>
#include <unistd.h>//write和close
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//任意IP地址
//servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port = htons(1234);
//输入参数第一个
//servAddr.sin_port=htons(atoi(argv[1]));
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
//向客户端发送数据
char str[] = "Hello World!";
write(clnt_sock, str, sizeof(str));
//关闭套接字
close(clnt_sock);
close(serv_sock);
return 0;
}
客户端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(){
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
addr.sin_family = AF_INET; //使用IPv4地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
addr.sin_port = htons(1234); //端口
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
//读取服务器传回的数据
char buffer[40];
read(sock, buffer, sizeof(buffer));
printf("Message form server: %s\n", buffer);
//关闭套接字
close(sock);
return 0;
}
第四章代码示例:Linux TCP回声示例
服务端
#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#define bufSize 1024
int main(int argc,char **argv)
{
int servSock=socket(AF_INET,SOCK_STREAM,0);
char message[bufSize];
sockaddr_in servAddr,clntAddr;
memset(&servAddr,0,sizeof(sockaddr_in));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
servAddr.sin_port=htons(atoi(argv[1]));
bind(servSock,(sockaddr*)&servAddr,sizeof(servAddr));
listen(servSock,5);
socklen_t clntAddrSize=sizeof(clntAddr);
int strLen,clntSock;
for(int i=0;i<5;i++)
{
clntSock=accept(servSock,(sockaddr*)&clntAddr,&clntAddrSize);
while((strLen=read(clntSock,message,bufSize))!=0)
{
write(clntSock,message,strLen);
}
close(clntSock);
}
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len, recv_len, recv_cnt;
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
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("connect() error");
else
puts("Connected...........");
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
str_len = write(sock, message, strlen(message)); // 发送了 str_len 个字节
recv_len = 0;
while (recv_len < str_len)
{
recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
if (recv_cnt == -1)
error_handling("read() error!");
recv_len += recv_cnt;
}
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
第五章代码示例:LINUX TCP简易计算器
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 50
#define OPSZ 4
void error_handling(char *message);
int calculate(int operand_count, int operands[], char ope);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_addr, clnt_addr;
char message[BUF_SIZE];
if (argc != 2)
{
printf("Usage : %s <port>", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
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");
socklen_t clnt_addr_sz = sizeof(clnt_addr);
for (int i = 0; i < 5; i++)
{
if ((clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_sz)) == -1)
error_handling("accept() error");
int operand_count;
read(clnt_sock, &operand_count, 1); // 首先读取第 1 个字节,获取操作数的数量
char operands[BUF_SIZE];
for (int i = 0; i < operand_count; i++)
{
read(clnt_sock, &operands[i * OPSZ], OPSZ); // 根据操作数数量,依次读取操作数
}
char ope;
read(clnt_sock, &ope, 1); // 读取运算符
int result = calculate(operand_count, (int *)operands, ope);
write(clnt_sock, (char *)&result, sizeof(result)); // 发送计算结果
close(clnt_sock);
}
close(serv_sock);
return 0;
}
int calculate(int operand_count, int operands[], char ope)
{
int result = operands[0];
switch (ope)
{
case '+':
for (int i = 1; i < operand_count; i++)
result += operands[i];
break;
case '-':
for (int i = 1; i < operand_count; i++)
result -= operands[i];
break;
case '*':
for (int i = 1; i < operand_count; i++)
result *= operands[i];
break;
}
return result;
}
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.h>
#include<iostream>
using namespace std;
#define BUF_SIZE 50
#define OPSZ 4 // 定义每个操作数在 TCP 报文中占用的字节数
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char opmsg[BUF_SIZE]; // opmsg 用来存储要发送的数据,注意是 char 类型数组
struct sockaddr_in serv_addr;
int operand_count, result;
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)))
error_handling("connect() error");
else
puts("Connecting..........\n");
fputs("Operand count: ", stdout);
// scanf("%d", &operand_count);
cin>>operand_count;
opmsg[0] = (char)operand_count; // 数据的第一个字节存储操作数的数量,注意要将变量类型转换为 char。
for (int i = 0; i < operand_count; i++)
{
printf("Operand %d: ", i + 1);
// scanf("%d", (int *)&opmsg[i * OPSZ + 1]);
// 从第二个字节开始每四个字节存储一个操作数,向数组存数据时先取地址再转换类型。
//相当于在opmsg上放入p指针的地址
int *p=(int*)&opmsg[i*OPSZ+1];
cin>>*p;
}
fgetc(stdin);
fputs("Operator: ", stdout);
// scanf("%c", &opmsg[operand_count * OPSZ + 1]); // 再用一个字节存储运算符
cin>>opmsg[operand_count*OPSZ+1];
// cout<<"before write"<<endl;
write(sock, opmsg, operand_count * OPSZ + 2); // 发送数据
read(sock, &result, OPSZ); // 接收运算结果:运算结果是一个 4 字节的操作数
printf("Operation result: %d\n", result);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
第六章代码示例:Linux UDP示例
服务端
#include<iostream>
#include<string.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define BUFSIZE 30
using namespace std;
int main(int argc,char **argv)
{
int servSock=socket(AF_INET,SOCK_DGRAM,0);
char msg[BUFSIZE];
sockaddr_in servAddr,clntAddr;
memset(&servAddr,0,sizeof(servAddr));
servAddr.sin_family=AF_INET;
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
servAddr.sin_port=htons(atoi(argv[1]));
bind(servSock,(sockaddr*)&servAddr,sizeof(servAddr));
socklen_t clnt_adr_sz;
int str_len;
while(true)
{
clnt_adr_sz=sizeof(clntAddr);
str_len=recvfrom(servSock,msg,BUFSIZE,0,(sockaddr*)&clntAddr,&clnt_adr_sz);
sendto(servSock,msg,str_len,0,(sockaddr*)&clntAddr,clnt_adr_sz);
}
close(servSock);
return 0;
}
客户端(无套接字版本)
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUFSIZE 30
using namespace std;
int main(int argc,char **argv)
{
int sock=socket(AF_INET,SOCK_DGRAM,0);
sockaddr_in addr,clntAddr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
char msg[BUFSIZE];
while(true)
{
cout<<"Insert message(q to quit): ";
cin.getline(msg,BUFSIZE);
if(!strcmp(msg,"q")||!strcmp(msg,"Q"))
break;
sendto(sock,msg,strlen(msg),0,(sockaddr*)&addr,sizeof(addr));
socklen_t addrSize=sizeof(clntAddr);
int strLen=recvfrom(sock,msg,BUFSIZE,0,(sockaddr*)&clntAddr,&addrSize);
cout<<"Message from server: "<<msg<<endl;
}
close(sock);
return 0;
}
客户端(有套接字版本)
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUFSIZE 50
using namespace std;
int main(int argc,char **argv)
{
int sock=socket(AF_INET,SOCK_DGRAM,0);
char msg[BUFSIZE];
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
socklen_t clntSize;
connect(sock,(sockaddr*)&addr,sizeof(addr));
while(true)
{
cout<<"Insert message(q to quit):";
cin.getline(msg,sizeof(msg));
if(!strcmp(msg,"Q")||!strcmp(msg,"q")) break;
//strlen表示msg目前存储字符串的大小
write(sock,msg,strlen(msg));
//
int str_len=read(sock,msg,sizeof(msg)-1);
msg[str_len]=0;
cout<<"Message from server :"<<msg<<endl;
}
close(sock);
return 0;
}
第七章代码示例:Linux 基于半关闭的文件传输程序
服务端
#include<iostream>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fstream>
const int bufsize=50;
using namespace std;
int main(int argc,char **argv)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr,clntAddr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
fstream file("1.txt",ios::in|ios::binary);
bind(sock,(sockaddr*)&addr,sizeof(addr));
listen(sock,5);
socklen_t clntAddr_size=sizeof(clntAddr);
int clntSock=accept(sock,(sockaddr*)&clntAddr,&clntAddr_size);
char buf[bufsize];
while(1)
{
if(file.eof()) break;
file.getline(buf,bufsize);
write(clntSock,buf,bufsize);
}
shutdown(clntSock,SHUT_WR);
read(clntSock,buf,bufsize);
cout<<"Message from client:"<<buf<<endl;
file.close();
close(clntSock);
close(sock);
return 0;
}
客户端
#include<iostream>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<fstream>
#define bufsize 50
using namespace std;
int main(int argc,char **argv)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
fstream file;
file.open("2.txt",ios::out|ios::binary);
connect(sock,(sockaddr*)&addr,sizeof(addr));
char buf[bufsize];
memset(&buf,0,bufsize);
while(1)
{
if(read(sock,buf,bufsize)==0) break;
// cout<<buf<<endl;
file<<buf<<endl;
}
cout<<"copy file succeed"<<endl;
write(sock,"Thank you for file!",20);
file.close();
close(sock);
return 0;
}
第八章代码示例:Linux+Windows 域名解析
gethostbyname
使用方式:./hostname www.naver.com
//Linux版本
#include<iostream>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netdb.h>
using namespace std;
void error_handling(const char *mes);
int main(int argc,char **argv)
{
int i;
hostent *host;
host=gethostbyname(argv[1]);
if(!host) error_handling("gethost...errot");
cout<<"Official name: "<<host->h_name<<endl;
for(i=0;host->h_aliases[i]!=NULL;i++)
cout<<"Aliases "<<i+1<<" :"<<host->h_aliases[i]<<endl;
cout<<"Address type: ";
string str=(host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6";
cout<<str<<endl;
for(i=0;host->h_addr_list[i];++i)
cout<<"IP addr "<<i+1<<": "
<<inet_ntoa(*(in_addr*)host->h_addr_list[i])<<endl;
return 0;
}
void error_handling(const char *mes)
{
cerr<<mes<<endl;
exit(1);
}
//windwos版本
#include<iostream>
#include<WinSock2.h>
using namespace std;
int main(int argc,char **argv)
{
WSADATA wsa;
WSAStartup(MAKEWORD(2,2),&wsa);
int i;
hostent *host=gethostbyname(argv[1]);
cout<<"Official name : "<<host->h_name<<endl;
for(i=0;host->h_aliases[i]!=NULL;i++)
cout<<"Aliases "<<i+1<<" :"<<host->h_aliases[i]<<endl;
cout<<"Address type: ";
string str=(host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6";
cout<<str<<endl;
for(i=0;host->h_addr_list[i];++i)
cout<<"IP addr "<<i+1<<": "
<<inet_ntoa(*(in_addr*)host->h_addr_list[i])<<endl;
WSACleanup();
system("pause");
return 0;
}
gethostbyaddr
问题:如果ip不成功,则需要将不成功的ip和对应域名添加到/etc/hosts
中
网址连接:https://blog.csdn.net/FrankAx/article/details/112375649
#include<iostream>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
using namespace std;
int main(int argc,char **argv)
{
int i;
hostent *host;
sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr=inet_addr(argv[1]);
host=gethostbyaddr((char*)&addr.sin_addr,4,AF_INET);
if(!host) cerr<<"gethost...error"<<endl;
cout<<"Office name : "<<host->h_name<<endl;
for(i=0;host->h_aliases[i]!=NULL;i++)
cout<<"Aliases "<<i+1<<" :"<<host->h_aliases[i]<<endl;
cout<<"Address type: ";
string str=(host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6";
cout<<str<<endl;
for(i=0;host->h_addr_list[i];++i)
cout<<"IP addr "<<i+1<<": "
<<inet_ntoa(*(in_addr*)host->h_addr_list[i])<<endl;
return 0;
}
第十章代码示例:Linux多进程回声服务器的实现
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
void read_childproc(int sig); // SIGCHLD 的信号处理函数
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
pid_t pid;
struct sigaction act; // 结构体 sigaction 用于存储信号处理函数
socklen_t adr_sz;
int str_len, state;
char buf[BUF_SIZE];
if (argc != 2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
act.sa_handler = read_childproc; // 设置信号处理函数
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0); // 绑定信号 SIGCHLD 与信号处理函数
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("bind() error");
if (listen(serv_sock, 5) == -1)
error_handling("listen() error");
while (1)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
if (clnt_sock == -1)
continue;
else
puts("new client connected...");
pid = fork();
if (pid == -1)
{
close(clnt_sock);
continue;
}
if (pid == 0)
{
close(serv_sock);
while ((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0) // 子进程会一直提供回声服务,直到收到 EOF,即客户端调用了 close()。
write(clnt_sock, buf, str_len);
close(clnt_sock);
puts("client disconnected...");
return 0;
}
else
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void read_childproc(int sig)
{
pid_t pid;
int status;
pid = waitpid(-1, &status, WNOHANG); // 此函数一次最多回收一个子进程
printf("removed proc id: %d \n", pid);
}
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.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
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("connect() error");
else
puts("Connected...........");
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
分割I/O的回声客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
void read_routine(int sock, char *buf);
void write_routine(int sock, char *buf);
int main(int argc, char *argv[])
{
int sock;
pid_t pid;
char buf[BUF_SIZE];
struct sockaddr_in serv_adr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
serv_adr.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("connect() error!");
pid = fork();
if (pid == 0)
write_routine(sock, buf); // 子进程发送数据
else
read_routine(sock, buf); // 父进程接收数据
close(sock);
return 0;
}
void read_routine(int sock, char *buf)
{
while (1)
{
int str_len = read(sock, buf, BUF_SIZE);
if (str_len == 0) // 循环读取直到读到文件结束符
return;
buf[str_len] = 0;
printf("Message from server: %s", buf);
}
}
void write_routine(int sock, char *buf)
{
while (1)
{
fgets(buf, BUF_SIZE, stdin);
if (!strcmp(buf, "q\n") || !strcmp(buf, "Q\n"))
{
shutdown(sock, SHUT_WR); // 优雅地关闭套接字:只关闭输出流。
return;
}
write(sock, buf, strlen(buf));
}
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
第十一章:Linux 进程间通信
相比第十章的服务端,添加了将回声客户端传输的字符串按序保存到文件中
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include<iostream>
#include<fstream>
using namespace std;
#define BUF_SIZE 100
void error_handling(char *message);
void read_childproc(int sig);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
int fds[2];
pid_t pid;
struct sigaction act;
socklen_t adr_sz;
int str_len, state;
char buf[BUF_SIZE];
if(argc!=2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
act.sa_handler=read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
state=sigaction(SIGCHLD, &act, 0);
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
pipe(fds);
pid=fork();
if(pid==0)
{
// FILE * fp=fopen("echomsg.txt", "wt");
fstream file("echomsg.txt",ios::out);
char msgbuf[BUF_SIZE];
int i, len;
for(i=0; i<3; i++)
{
memset(&msgbuf,0,sizeof(msgbuf));
len=read(fds[0], msgbuf, BUF_SIZE);
cout<<msgbuf<<endl;
cout<<strlen(msgbuf)<<endl;
// fwrite((void*)msgbuf, 1, len, fp);
file<<msgbuf;
}
// fclose(fp);
file.close();
return 0;
}
while(1)
{
adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
if(clnt_sock==-1)
continue;
else
puts("new client connected...");
pid=fork();
if(pid==0)
{
close(serv_sock);
while((str_len=read(clnt_sock, buf, BUF_SIZE))!=0)
{
write(clnt_sock, buf, str_len);
write(fds[1], buf, str_len);
}
close(clnt_sock);
puts("client disconnected...");
return 0;
}
else
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void read_childproc(int sig)
{
pid_t pid;
int status;
pid=waitpid(-1, &status, WNOHANG);
printf("removed proc id: %d \n", pid);
}
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.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
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("connect() error");
else
puts("Connected...........");
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
第十二章select函数I/O复用的回声
服务端
#include<iostream>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<sys/time.h>
#define BUFSIZE 100
using namespace std;
void error_handling(const char* msg);
int main(int argc,char **argv)
{
int sock=socket(PF_INET,SOCK_STREAM,0);
int clntSock;
sockaddr_in addr,clntAddr;
timeval timeout;
fd_set rd,copy_rd;
socklen_t adr_sz;
char buf[BUFSIZE];
if(argc!=2)
{
cout<<"Usage: "<<argv[0]<<" <port>\n";
exit(1);
}
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr==htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(-1==bind(sock,(sockaddr*)&addr,sizeof(addr)))
error_handling("bind() error");
if(-1==listen(sock,5))
error_handling("listen() error");
FD_ZERO(&rd);
FD_SET(sock,&rd);
int fd_max=sock;//记录sock指向的文件描述符
int fd_num,str_len,i;
while(1)
{
copy_rd=rd;
//设置超时5s+5000ms=10s
timeout.tv_sec=5;
timeout.tv_usec=5000;
//fd_max+1是因为监视对象文件描述符是从0开始的
fd_num=select(fd_max+1,©_rd,0,0,&timeout);
if(fd_num==-1)//监视错误
break;
if(0==fd_num)//超时
continue;
//遍历检查发生变化的文件描述符
for(i=0;i<=fd_max;i++)
{
if(FD_ISSET(i,©_rd))
{
//需要建立连接
if(i==sock)
{
adr_sz=sizeof(clntAddr);
//accept调用会将clntSock=sock+1
clntSock=accept(sock,(sockaddr*)&clntAddr,&adr_sz);
//在rd中注册文件描述符clntSock信息
FD_SET(clntSock,&rd);
//更新最大的fd文件描述符
if(fd_max<clntSock)
fd_max=clntSock;
cout<<"connected client :" <<clntSock<<endl;
}
//读取数据
else
{
str_len=read(i,buf,BUFSIZE);
//读取结束
if(0==str_len)
{
FD_CLR(i,&rd);
close(i);
cout<<"closed client: "<<i<<endl;
}
else
{
write(i,buf,str_len);//回声部分
}
}
}
}
}
close(sock);
return 0;
}
void error_handling(const char* msg)
{
cerr<<msg<<endl;
exit(1);
}
客户端
本客户端为回声客户端的代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
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("connect() error");
else
puts("Connected...........");
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
第十七章 基于epoll的回声服务器
服务端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#define BUFSIZE 100
#define EPOLL_SIZE 50
void error_handling(const char*buf);
int main(int argc,char **argv)
{
int sock,clntSock;
sockaddr_in addr,clntAddr;
socklen_t adr_sz;
int str_len,i;
char buf[BUFSIZE];
epoll_event *ep_events;
epoll_event event;
int epfd,event_cnt;
if(argc!=2)
{
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(bind(sock,(sockaddr*)&addr,sizeof(addr))==-1)
error_handling("bind() error");
if(listen(sock,5)==-1)
error_handling("listen() error");
//申请创建保存文件描述符的空间
epfd=epoll_create(EPOLL_SIZE);
//申请EPOLL_SIZE*epoll_event结构大小的空间
int ep_sz=sizeof(struct epoll_event)*EPOLL_SIZE;
ep_events=(epoll_event*)malloc(ep_sz);
//注册监视对象文件的描述符
event.events=EPOLLIN;//需要读取数据的情况
event.data.fd=sock;
//EPOLL_CTL_ADD:将文件描述符注册到epoll例程
//epoll例程epfd中注册文件描述符sock,主要目的是监视even中的事件
epoll_ctl(epfd,EPOLL_CTL_ADD,sock,&event);
while(1)
{
//epoll_wait类似于select函数,主要是为了得到文件描述符数量
//-1表示一直等待到事件发生
event_cnt=epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
//成功返回发生事件的文件描述符数量,-1表示失败
if(-1==event_cnt)
{
puts("epoll_wait() error");
break;
}
for(i=0;i<event_cnt;i++)
{
if(ep_events->data.fd==sock)
{
adr_sz=sizeof(clntAddr);
clntSock=accept(sock,(sockaddr*)&clntAddr,&adr_sz);
event.events=EPOLLIN;
event.data.fd=clntSock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clntSock,&event);
printf("connect client: %d \n",clntSock);
}
else
{
str_len=read(ep_events[i].data.fd,buf,BUFSIZE);
if(0==str_len)
{
//从epoll例程中删除文件描述符
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("closed clint: %d \n",ep_events[i].data.fd);
}
else
{
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
close(sock);
close(epfd);
return 0;
}
void error_handling(const char*buf)
{
fputs(buf,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.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
struct sockaddr_in serv_addr;
if (argc != 3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
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("connect() error");
else
puts("Connected...........");
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
write(sock, message, strlen(message));
str_len = read(sock, message, BUF_SIZE - 1);
message[str_len] = 0;
printf("Message from server: %s", 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.h>
#include<sys/epoll.h>
//将调用read函数时使用的缓冲大小所见为4个字节
//减少缓冲大小是为了阻止服务器端一次性读取接收的数据。
//也就是说,调用read函数后,输入缓冲中仍有数据读取,
//而且会注册新的事件并从epoll_wait函数返回时将循环输出"return epoll_wait"字符串。
#define BUFSIZE 4
#define EPOLL_SIZE 40
void error_handling(const char* buf)
{
fputs(buf,stderr);
fputc('\n',stderr);
exit(1);
}
int main(int argc,char **argv)
{
int sock,clntSock;
sockaddr_in addr,clntAddr;
socklen_t adr_sz;
int str_len,i;
char buf[BUFSIZE];
epoll_event *ep_events;
epoll_event event;
int epfd,event_cnt;
if(2!=argc)
{
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(-1==bind(sock,(sockaddr*)&addr,sizeof(addr)))
error_handling("bind() error");
if(-1==listen(sock,5))
error_handling("listen() error");
epfd=epoll_create(EPOLL_SIZE);
ep_events=(epoll_event*)malloc(sizeof(epoll_event)*EPOLL_SIZE);
event.events=EPOLLIN;
event.data.fd=sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,sock,&event);
while(1)
{
event_cnt=epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
if(-1==event_cnt)
{
puts("epoll_wait() error");
break;
}
//验证epoll_wait函数调用此书
puts("return epoll_wait");
for(i=0;i<event_cnt;i++)
{
if(sock==ep_events[i].data.fd)
{
adr_sz=sizeof(clntAddr);
clntSock=accept(sock,(sockaddr*)&clntAddr,&adr_sz);
event.events=EPOLLIN;
event.data.fd=clntSock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clntSock,&event);
printf("connected client: %d\n",clntSock);
}
else
{
str_len=read(ep_events[i].data.fd,buf,BUFSIZE);
if(0==str_len)
{
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("closed client: %d\n",ep_events[i].data.fd);
}
else
{
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
close(sock);
close(epfd);
return 0;
}
第十七章 边缘触发服务器
服务端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#define BUFSIZE 4
#define EPOLL_SIZE 50
void setnonblockingmode(int fd);
void error_handling(const char* buf);
int main(int argc,char **argv)
{
int sock,clntSock;
sockaddr_in addr,clntAddr;
socklen_t adr_sz;
int str_len,i;
char buf[BUFSIZE];
epoll_event *ep_events;
epoll_event event;
int epfd,event_count;
if(2!=argc)
{
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(-1==bind(sock,(sockaddr*)&addr,sizeof(addr)))
error_handling("bind() error");
if(-1==listen(sock,5))
error_handling("listen() error");
epfd=epoll_create(EPOLL_SIZE);
ep_events=(epoll_event*)malloc(sizeof(epoll_event)*EPOLL_SIZE);
//将sock变为非阻塞模式
setnonblockingmode(sock);
event.events=EPOLLIN;
event.data.fd=sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,sock,&event);
while(1)
{
event_count=epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
if(-1==event_count)
{
puts("epoll_wait() error");
break;
}
puts("return epoll_wait");
for(i=0;i<event_count;i++)
{
if(ep_events[i].data.fd==sock)
{
adr_sz=sizeof(clntAddr);
clntSock=accept(sock,(sockaddr*)&clntAddr,&adr_sz);
setnonblockingmode(clntSock);
//设置读取数据|以边缘触发的方式得到事件通知
event.events=EPOLLIN|EPOLLET;
event.data.fd=clntSock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clntSock,&event);
printf("connected client: %d\n",clntSock);
}
else
{
while(1)
{
str_len=read(ep_events[i].data.fd,buf,BUFSIZE);
if(0==str_len)
{
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("closed client: %d\n",ep_events[i].data.fd);
break;
}
//表示读取了输入缓冲中的全部数据
else if(str_len<0)
{
if(errno==EAGAIN)
break;
}
else
{
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
}
close(sock);
close(epfd);
return 0;
}
void setnonblockingmode(int fd)
{
//获取之间设置的属性信息
int flag=fcntl(fd,F_GETFL,0);
//添加非阻塞标志
fcntl(fd,F_SETFL,flag|O_NONBLOCK);
}
void error_handling(const char*buf)
{
fputs(buf,stderr);
fputc('\n',stderr);
exit(1);
}
第十九章 多线程并发服务
服务端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE 100
#define MAX_CLNT 256
void* handle_clnt(void *arg);
void send_msg(char *msg,int len);
void error_handling(const char*msg);
int clnt_cnt=0;//客户端数量
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutex;
int main(int argc,char **argv)
{
int sock,clntSock;
sockaddr_in addr,clntAddr;
socklen_t clnt_sz;
pthread_t id;
if(2!=argc)
{
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
//NULL表示不需要指定互斥量的属性
pthread_mutex_init(&mutex,NULL);
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(argv[1]));
if(bind(sock,(sockaddr*)&addr,sizeof(addr))==-1)
error_handling("bind() error");
if(listen(sock,5)==-1)
error_handling("listen() error");
while(1)
{
clnt_sz=sizeof(clntAddr);
clntSock=accept(sock,(sockaddr*)&clntAddr,&clnt_sz);
pthread_mutex_lock(&mutex);
clnt_socks[clnt_cnt++]=clntSock;
pthread_mutex_unlock(&mutex);
pthread_create(&id,NULL,handle_clnt,(void*)&clntSock);
pthread_detach(id);
printf("Connected client IP: %s\n",inet_ntoa(clntAddr.sin_addr));
}
close(sock);
return 0;
}
void* handle_clnt(void *arg)
{
int clntSock=*((int*)arg);
int str_len=0;
char msg[BUFSIZE];
while((str_len=read(clntSock,msg,sizeof(msg)))!=0)
{
send_msg(msg,str_len);
}
pthread_mutex_lock(&mutex);
//删除断开连接的客户端
for(int i=0;i<clnt_cnt;i++)
{
if(clntSock==clnt_socks[i])
{
while(i++<clnt_cnt-1)
clnt_socks[i]=clnt_socks[i+1];
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutex);
close(clntSock);
return NULL;
}
void send_msg(char *msg,int len)
{
//向所有客户端发送
pthread_mutex_lock(&mutex);
for(int i=0;i<clnt_cnt;i++)
{
write(clnt_socks[i],msg,len);
}
pthread_mutex_unlock(&mutex);
}
void error_handling(const char*msg)
{
fputs(msg,stderr);
fputc('\n',stderr);
exit(1);
}
客户端
#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUFSIZE 100
#define NAMESIZE 20
char name[NAMESIZE]="[DEFAULT]";
char msg[BUFSIZE];
void error_handling(const char*msg);
void* send_msg(void *arg);
void* recv_msg(void *arg);
int main(int argc,char **argv)
{
int sock;
sockaddr_in addr;
void *thread_ret;//得到线程返回的信息
pthread_t send_thread,recv_thread;
if(argc!=4)
{
printf("Usage : %s <IP> <port> <name>\n",argv[0]);
_exit(1);
}
sprintf(name,"[%s]",argv[3]);
sock=socket(PF_INET,SOCK_STREAM,0);
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(argv[1]);
addr.sin_port=htons(atoi(argv[2]));
if(-1==connect(sock,(sockaddr*)&addr,sizeof(addr)))
error_handling("connect() error");
pthread_create(&send_thread,NULL,send_msg,(void*)&sock);
pthread_create(&recv_thread,NULL,recv_msg,(void*)&sock);
pthread_join(send_thread,&thread_ret);
pthread_join(recv_thread,&thread_ret);
close(sock);
return 0;
}
void* send_msg(void *arg)
{
int sock=*((int*)arg);
char name_msg[NAMESIZE+BUFSIZE];
while(1)
{
fgets(msg,BUFSIZE,stdin);
if(!strcmp(msg,"q\n")||!strcmp(msg,"Q\n"))
{
close(sock);
exit(0);
}
sprintf(name_msg,"%s %s",name,msg);
write(sock,name_msg,strlen(name_msg));
}
return NULL;
}
void* recv_msg(void* arg)
{
int sock=*((int*)arg);
char name_msg[NAMESIZE+BUFSIZE];
int str_len;
while(1)
{
str_len=read(sock,name_msg,NAMESIZE+BUFSIZE-1);
if(-1==str_len)
{
return (void*)-1;
}
name_msg[str_len]=0;
fputs(name_msg,stdout);
}
return NULL;
}
void error_handling(const char*msg)
{
fputs(msg,stderr);
fputc('\n',stderr);
_exit(1);
}
代码解释
WSAStartup()函数以及DLL的加载
头文件winsock2.h
编译时加载#pragma comment (lib,"ws2_32.lib")
主程序写法
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
MAKEWORD(1, 2); //主版本号为1,副版本号为2,返回 0x0201
MAKEWORD(2,2); //主版本号为2,副版本号为2,返回 0x0202
WinSock 编程的第一步就是加载 ws2_32.dll,然后调用 WSAStartup() 函数进行初始化,并指明要使用的版本号。
socket函数创建套接字
头文件<sys/socket.h>
原型int socket(int af,int type, int protocol)
SOCKET sock=socket(AF_INET,SOCK_STREAM,0);//Windows
int sock=sokcet(AD_INET,SOCK_STREAM,IPPROTO_TCP);//Linux
bind函数和connect函数(类似)
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//创建sockaddr_in结构体变量
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1234); //端口
//将套接字和IP、端口绑定
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
问题:bind()第二个参数类型为sockaddr,而代码中却使用sockaddr_in然后强制转换为sockaddr?
回答:sockaddr 是一种通用的结构体,可以用来保存多种类型的IP地址和端口号但是混合在一个char数组,而 sockaddr_in 是专门用来保存 IPv4 地址的结构体,IP地址和端口号单独出来了。
问题:bind()和connect()区别?
回答:bind()将套接字和其本地地址相关联(这就是服务器端绑定的原因,以便客户端可以使用该地址连接到服务器);connect()用于连接到远程地址,一般用于客户端。bind需要一个未使用的端口(以便它可以声明它并成为服务器),而connect需要一个已经在使用的端口(以便它可以连接到该端口并与服务器通信)。
listen和accept函数
int listen(int sock, int backlog); //Linux
int listen(SOCKET sock, int backlog); //Windows
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
如果将 backlog 的值设置为 SOMAXCONN
,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。
当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误,对于 Windows,客户端会收到 WSAECONNREFUSED 错误。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字。
listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
socket数据的发送和接收
//Linux
#include<unistd.h>
write(int fd, const void *buf, size_t nbytes)
read(int fd, void *buf, size_t nbytes)
//Windows,flags一般为0或NULL
send(SOCKET sock, const char *buf, int len, int flags)
recv(SOCKET sock, char *buf, int len, int flags)
sock——要发送数据的套接字
buf——要发送数据的缓冲区地址
len——要发送的数据的字节数
flags——一般设置为0或NULL