Windows和Linux网络编程代码示例-基于《尹圣雨-TCP/IP网络编程》

TCP/IP

[解决c++socket报错](https://blog.csdn.net/weixin_39591031/article/details/115222662):手动编译文件,在编译命令后添加一个参数-lwsock32

windows上无法编译,可以将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,&copy_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,&copy_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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值