文章标题

linux socket c/c++ 聊天

client_chat.c

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>
#define MAXDATASIZE 256 //
#define SERVPORT 4444  //服务器监听端口号
#define STDIN 0  //标准输入文件描述符

int main(int argc,char *argv[])
{
    char addr[30];
    int sockfd;
    struct sockaddr_in serv_addr;//Internet套接字地址结构
    char buf[MAXDATASIZE];     //用于处理输入的缓冲区
    char name[MAXDATASIZE];
    char send_str[MAXDATASIZE]; //最多发送的字符不能超过256
    int recvbytes;
    fd_set rfd_set,wfd_set,efd_set; //select()监视读、写、异常处理的文件描述符集合  
    struct timeval timeout; //本次select()的超时结束时间
    int ret; //与server连接的结果

    if(argc<2)
    {
        printf("请输入服务器IP\n");
        fgets(addr,256,stdin);
        argv[1] = (char *)malloc(sizeof(argv[1]));
        strcpy(argv[1],addr);
    }

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socker error!");
        exit(1);
    }
    //填充sockaddr结构
    bzero(&serv_addr,sizeof(struct sockaddr_in));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVPORT);
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

    if(connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1)
    {
        perror("connect error!");
        exit(1);
    }
    printf("已成功连接到服务器 %s\n",inet_ntoa(serv_addr.sin_addr));
    fcntl(sockfd,F_SETFD,O_NONBLOCK);//服务器设为非阻塞
    printf("要聊天首先要输入你的名字:");
    scanf("%s",name);
    name[strlen(name)] = '\0';
    printf("%s:",name);
    fflush(stdout);
    send(sockfd,name,strlen(name),0);//发送用户名到sockfd

    while(1)
    {
        //将select()监视的读,写,异常文件描述符清除
        FD_ZERO(&rfd_set);
        FD_ZERO(&wfd_set);
        FD_ZERO(&efd_set);
        //将标准输入文件描述符加到select()监视的读文件描述符集合中
        FD_SET(STDIN,&rfd_set);
        //添加新建的描述符加到select()监视的文件描述符中
        FD_SET(sockfd,&rfd_set);
    //  FD_SET(sockfd,&wfd_set);
        FD_SET(sockfd,&efd_set);
        //设置select在被监视窗口等待的时间
        timeout.tv_sec = 10; //秒
        timeout.tv_usec = 0; //微妙
        ret = select(sockfd+1,&rfd_set,&wfd_set,&efd_set,&timeout);
        if(ret==0)
            continue;
        if(ret<0)
        {
            perror("select error!");
            exit(-1);
        }
        //判断是否已将标准输入文件描述符加到select()监视的读的文件描述符集合中
        if(FD_ISSET(STDIN,&rfd_set))
        {
            fgets(send_str,256,stdin);//读取键盘输入的内容
            send_str[strlen(send_str)-1] = '\0';
            if(strncmp("quit",send_str,4)==0)
            {
                close(sockfd);
                exit(0);
            }
            send(sockfd,send_str,strlen(send_str),0);
        }
        //判断是否已将新建的描述符加到select()监视的读的文件描述符集合中
        if(FD_ISSET(sockfd,&rfd_set))
        {
            recvbytes = recv(sockfd,buf,MAXDATASIZE,0);
            if(recvbytes==0)
            {
                close(sockfd);
                exit(0);
            }
            buf[recvbytes] = '\0';
            printf("Server: %s\n",buf);
            printf("%s: ",name);
            fflush(stdout);
        }
        //异常
        if(FD_ISSET(sockfd,&efd_set))
        {
            close(sockfd);
            exit(0);
        }
    }

    return 0;
}

server_chat.c

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

#define MAXDATASIZE 256 //
#define SERVPORT 4444  //服务器监听端口号
#define BACKLOG 10 //最大连接请求数
#define STDIN 0  //标准输入文件描述符

int main(void)
{
    FILE *fp;
    int sockfd,client_fd;
    int sin_size;
    struct sockaddr_in my_addr,remote_addr;//本机地址信息,客户机地址信息
    char buf[256];     //用于聊天的缓冲区
    char buff[256];         //用于输入用户名的缓冲区
    char send_str[256]; //最多发送的字符不能超过256
    int recvbytes;
    fd_set rfd_set,wfd_set,efd_set; //select()监视读、写、异常处理的文件描述符集合  
    struct timeval timeout; //本次select()的超时结束时间
    int ret; //与client连接的结果

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socker error!");
        exit(1);
    }
    //填充sockaddr结构
    bzero(&my_addr,sizeof(struct sockaddr_in));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(SERVPORT);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //解决服务器关掉,启动“Address already in use”的情况
    int on=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))==-1)
    {
        perror("bind error!");
        exit(1);
    }   
    if(listen(sockfd,BACKLOG)==-1)
    {
        perror("listen error!");
        exit(1);
    }
    sin_size = sizeof(struct sockaddr_in);
    if((client_fd=accept(sockfd,(struct sockaddr*)&remote_addr,&sin_size))==-1)
    {
        perror("accept error!");
        exit(1);
    }
    printf("收到一个连接来自: %s\n",inet_ntoa(remote_addr.sin_addr));
    fcntl(client_fd,F_SETFD,O_NONBLOCK);//服务器设为非阻塞
    recvbytes = recv(client_fd,buff,MAXDATASIZE,0);
    buff[recvbytes] = '\0';
    fflush(stdout);
    if((fp=fopen("name.txt","a+"))==NULL)
    {
        printf("cannot open file,exit...\n");
        return -1;
    }
    fprintf(fp,"%s\n",buff);//将用户名写入到name.txt中

    while(1)
    {
        //将select()监视的读,写,异常文件描述符清除
        FD_ZERO(&rfd_set);
        FD_ZERO(&wfd_set);
        FD_ZERO(&efd_set);
        //将标准输入文件描述符加到select()监视的读文件描述符集合中
        FD_SET(STDIN,&rfd_set);
        //添加新建的描述符加到select()监视的文件描述符中
        FD_SET(client_fd,&rfd_set);
        FD_SET(client_fd,&wfd_set);
        FD_SET(client_fd,&efd_set);
        //设置select在被监视窗口等待的时间
        timeout.tv_sec = 10; //秒
        timeout.tv_usec = 0; //微妙
        ret = select(client_fd+1,&rfd_set,&wfd_set,&efd_set,&timeout);
        if(ret==0)
            continue;
        if(ret<0)
        {
            perror("select error!");
            exit(-1);
        }
        //判断是否已将标准输入文件描述符加到select()监视的读的文件描述符集合中
        if(FD_ISSET(STDIN,&rfd_set))
        {
            fgets(send_str,256,stdin);//读取键盘输入的内容
            send_str[strlen(send_str)-1] = '\0';
            if(strncmp("quit",send_str,4)==0)
            {
                close(client_fd);
                close(sockfd);
                exit(0);
            }
            send(client_fd,send_str,strlen(send_str),0);
        }
        //判断是否已将新建的描述符加到select()监视的读的文件描述符集合中
        if(FD_ISSET(client_fd,&rfd_set))
        {
            recvbytes = recv(client_fd,buf,MAXDATASIZE,0);
            if(recvbytes==0)
            {
                close(client_fd);
                close(sockfd);
                exit(0);
            }
            buf[recvbytes] = '\0';
            printf("%s: %s\n",buff,buf);
            printf("Server: ");
            fflush(stdout);
        }
        //异常
        if(FD_ISSET(client_fd,&efd_set))
        {
            close(client_fd);
            exit(0);
        }
    }

}

makefile

all: client serv
.PHONY:all

# make client:
client : client_chat.o
    gcc -o client client_chat.o
client_chat.o : client_chat.c
    gcc -c client_chat.c

# make serv:
serv : server_chat.o
    gcc -o serv server_chat.o
server_chat.o : server_chat.c
    gcc -c server_chat.c

# make clean:
clean :
    rm -rf *.o client
    rm -rf *.o serv
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值