linux下的套接字编程

编写一个linux下的聊天室。
分析:需要服务器接收消息并转发给每一个连接上的套接字。需要用多线程,后台线程接收,前台线程发送。

client.c

#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/un.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>


char recv_buf[1500],send_buf[1024];
void pthread_function(int sockfd)
{
    int recvbytes;
    while(1)
    {
        memset(recv_buf,1500,0);
        if(recvbytes=recv(sockfd,recv_buf,1500,0)==-1)
        {
            perror("recv error!\n");
            exit(1);
        }
        else
        {
            printf("%s\n",recv_buf);
        }
    }
}//新的线程一直接收数据并输出,直到接收失败(linux里面recv接收到的字符串没有\0)

int main(void)
{
    //char ip[20];
    //printf("please input ip");
    //gets(ip);

    pthread_t id;
    int sockfd;
    struct sockaddr_in server_addr ;
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(12345);
    server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        perror("socket error!\n");
        exit(1);
    }
    if(connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
    {
        perror("connect error!\n");
        exit(1);
    }
    char name[20];
    printf("input your name:");
    scanf("%s",name);
    send(sockfd,name,strlen(name),0);//发送名字
    pthread_create(&id,NULL,(void *)pthread_function,(int *)sockfd);//开启新的线程运行接收函数
    while(1)
    {
        memset(send_buf,1500,0);
        gets(send_buf);
        if(send(sockfd,send_buf,strlen(send_buf),0)==-1)
        {
            perror("send error!\n");
            exit(1);
        }
        sleep(1);
    }
    //关闭套接字,并取消线程
    close(sockfd);
    pthread_cancel(id);
    return 0;
}//后台线程负责接收,前台线程负责发送。

server.c

#include<sys/types.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/un.h>
#include<sys/time.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<netinet/in.h>
#include<pthread.h>

#define COUNT 5//同时在线人数5
//保存socket
int socket_fd[COUNT];

void pthread_function(int client_fd)
{
    char message[1500];
    char buf[1024];
    int i,recvbytes;
    char name[20];

    //首次连接时保存名字
    memset(name,20,0);
    recvbytes=recv(client_fd,name,20,0);//接收名字
    //接收name
    name[recvbytes]=':';
    //构造接收内容字符串

    while(1)
    {
        memset(buf,1024,0);
        if((recvbytes=recv(client_fd,buf,1024,0))==-1)
        {
            printf("recv error!\n");
            exit(1);
        }
        if(recvbytes==0)
        {
            printf("%sbye!\n",name);//recv函数返回值为0表示
            break;
        }
        for(i=0;i<COUNT;i++)
        {
            if(socket_fd[i]==-1)
                continue;//对应的socket为-1表示连接已经断开
            else
            {
                memset(message,1500,0);

                strcpy(message,name);
                strcat(message,buf);
                if(send(socket_fd[i],message,strlen(message),0)==-1)//接收完消息把消息的内容发送给每个套接字
                {
                    perror("send error!\n");
                    exit(1);
                }
            }
        }
    }
    close(client_fd);
    for(i=0;i<COUNT;i++)
    {
        if(socket_fd[i]==client_fd)
        {
            socket_fd[i]=-1;
        }
    }
    //关闭套接字并把sock_fd设置为-1
    pthread_exit(NULL);
    //关闭线程
}
//新的线程函数,直到接收不到消息才会断开连接 ,否则会一直接收并把收到的消息发送给每一个套接字

int main()
{
    int i;
    for(i=0;i<COUNT;i++)
    {
        socket_fd[i]=-1;
    }//初始化socket_fd 字符串

    pthread_t id;
    int sockfd,client_fd;
    socklen_t sin_size;
    struct sockaddr_in my_addr;
    struct sockaddr_in remote_addr;
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)//创建套接字
    {
        perror("socket error!\n");
        exit(1);
    }//套接字就是一个int型的标识
    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(12345);
    my_addr.sin_addr.s_addr=INADDR_ANY;
    bzero(&(my_addr.sin_zero),8);
    if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))==-1)
        {
            perror("bind error!\n");
            exit(1);
        }
    if(listen(sockfd,10)==-1)
        {
            perror("listen failed!\n");
            exit(1);
        }
    i=0;
    while(1)
    {
        sin_size=sizeof(struct sockaddr_in);
        client_fd=accept(sockfd,(struct sockaddr*)&remote_addr,&sin_size);
        if(client_fd==-1)
        {
            perror("accept error!\n");
            exit(1);
        }
        //找到一个可用的socket位置
        while(socket_fd[i]!=-1)
        {
            i=(i+1)%COUNT;
        }
        //保存socket并开启线程处理
        socket_fd[i]=client_fd;
        pthread_create(&id,NULL,(void *)pthread_function,(int *)client_fd);

    }
}

服务器端维护一个客户端连接的套接字数组。客户端需要连接。
注意:

        if((client_fd=accept(sockfd,(struct sockaddr*)&remote_addr,&sin_size))==-1)
        {
            perror("accept error!\n");
            exit(1);
        }

这样写看似没问题但经过编译器优化后就不一样了它会把套接字client_fd设置为0,仔细用gdb跟一下就能得出结论。(我这里又找不到错误了,就不演示了)。
发现:接收失败或者是发送失败,看一下函数没问题就要思考套接字有问题了。可能套接字不知道什么时候被清零了(linux下的套接字是int型,容易被改变)。

这里为了调试方便,编译的时候把保护全部关闭,并且不让编译器进行太多优化

gcc -O0 -fno-stack-protector -z execstack -z norelro -no-pie server-l.c -lpthread -o server

关于关闭各种保护机制:

gcc -z norelro   norelro

gcc编译时,关闭DEP和栈保护,-fno-stack-protector-z execstack这两个参数会分别关掉DEP和Stack Protector
gcc -fno-stack-protector -z execstack -o level1 level1.c

-no-pie  关掉pie

这里附上链接了解更多详情https://blog.csdn.net/nibiru_holmes/article/details/61209297

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/80837231