Socket实现聊天发文件

来源:互联网 发布:国家电网数据运维 编辑:程序博客网 时间:2024/05/24 04:47

client.c

#define _WINSOCK_DEPRECATED_NO_WARNINGS#define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #include <pthread.h>#include <windowsx.h>#pragma comment(lib, "WS2_32")#define BUFF_LEN 1024/* 预设服务器地址 */#define SERVER "127.0.0.1"/* 预设端口号*/#define PORT 12345enum MSG_TYPE{    MSG_CHAT = 0,    MSG_FILE_NAME,    MSG_FILE_CONTENT,    MSG_FILE_END};void showMenu(){    printf("1. 聊天\n");    printf("2. 发送文件\n");}DWORD WINAPI processRead(LPVOID pM){    SOCKET s = (SOCKET)pM;    int cmd = 0;    while (1)    {        cmd = 0;        showMenu();        scanf("%d", &cmd);        fflush(stdin);        switch (cmd)        {        case 1:        {            char buffer[BUFF_LEN];            printf("请输入聊天信息: \n");            scanf("%s", buffer);            MSG_TYPE type = MSG_CHAT;            int len = strlen(buffer) + 1;            char *msg = (char *)malloc(strlen(buffer) + 1 + 2 * sizeof(int));            //从源src所指的内存地址的起始位置开始拷贝n个字节            //到目标dest所指的内存地址的起始位置中            memcpy(msg, &type, sizeof(type));            memcpy(msg+sizeof(type), &len, sizeof(len));            memcpy(msg + 2*sizeof(type), buffer, len);            //第一个参数指定发送端套接字描述符;            //第二个参数指明一个存放应用程序要发送数据的缓冲区;            //第三个参数指明实际要发送的数据的字节数;            //第四个参数一般置0。            send(s, msg, strlen(buffer) + 1 + 2 * sizeof(int), 0);            free(msg);        }            break;        case 2:        {            char buffer[BUFF_LEN];            printf("请输入传输文件名: \n");            scanf("%s", buffer);            FILE *fp = fopen(buffer, "r");            if (fp == NULL)            {                printf("文件不存在或者发生未知错误\n");                break;            }            MSG_TYPE type = MSG_FILE_NAME;//MSG_FILE_NAME为1            int len = strlen(buffer) + 1;            char *msg = (char *)malloc(strlen(buffer) + 1 + 2 * sizeof(int));            memcpy(msg, &type, sizeof(type));            memcpy(msg + sizeof(type), &len, sizeof(len));            memcpy(msg + 2 * sizeof(type), buffer, len);            send(s, msg, strlen(buffer) + 1 + 2 * sizeof(int), 0);            free(msg);            while (1)            {                if (feof(fp))                {                    Sleep(100);                    type = MSG_FILE_END;//MSG_FILE_END 3                    send(s, (char *)&type, sizeof(type), 0);                    printf("文件发送成功\n");                    break;                }                else                {                     memset(buffer, BUFF_LEN, 0);                    //buffer 用于接收数据的内存地址                    //size   要读的每个数据项的字节数,单位是字节                    //count  要读count个数据项,每个数据项size个字节.                    //stream 输入流                    int ret = fread(buffer, 1, BUFF_LEN - 1, fp);                    if (ret < 0)                    printf("read error\n, %d\n", ret);                    printf("file ret %d\n", ret);                    buffer[ret] = '\0';                    printf("buffer read : %s\n", buffer);                    MSG_TYPE t = MSG_FILE_CONTENT;//MSG_FILE_CONTENT 2                    int ll = strlen(buffer) + 1;                    char *msg = (char *)malloc(ll + 2 * sizeof(int));                    memset(msg, ll, 0);                    memcpy(msg, &t, sizeof(type));                    memcpy(msg + sizeof(type), &ll, sizeof(len));                    memcpy(msg + 2 * sizeof(type), buffer, ll);                    //send(s, msg, strlen(msg) + 1, 0);                    send(s, msg, ll + 2 * sizeof(int), 0);                    free(msg);                }            }        }            break;        default:            break;        }    }    return 0;}DWORD WINAPI processWrite(LPVOID pM){    FILE *fp;    SOCKET s = (SOCKET)pM;    char buffer[BUFF_LEN] = { '\0' };    while (1)    {        memset(buffer, BUFF_LEN, 0);        //第一个参数指定接收端套接字描述符;        //第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;        //第三个参数指明buf的长度;        //第四个参数一般置0。        recv(s, buffer, BUFF_LEN, 0);        MSG_TYPE type;        int len;        memcpy(&type, buffer, sizeof(int));        if (type == MSG_FILE_END)        {            fclose(fp);            printf("文件接收成功\n");            continue;        }        memcpy(&len, buffer + sizeof(int), sizeof(int));        char *msg = (char *)malloc(len);        memset(msg, len, 0);        memcpy(msg, buffer + 2 * sizeof(type), len);        if (type == MSG_CHAT)        {             printf(" : %s\n", msg);         }        else if (type == MSG_FILE_NAME)        {            printf("文件名: %s\n", msg);            fp = fopen(msg, "w");        }        else //if(type == MSG_FILE_CONTENT)        {            printf("写文件\n");            if (fp != NULL)            {                printf("recv : %s\n", msg);                fwrite(msg, 1, len - 1, fp);            }        }        free(msg);    }    return 0;}int main(int argc, char *argv[]){    SOCKET client_sockfd;    int len;    struct sockaddr_in address;    char server[UCHAR_MAX];    int result;    if (argc > 1) {        strcpy(server, argv[1]);    }    else {        strcpy(server, SERVER);    }    // 初始化socket dll     WSADATA wsaData;    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)    {        printf("Init socket dll error!");        exit(1);    }    if ((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {        perror("socket() 失败");        exit(EXIT_FAILURE);    }    address.sin_family = AF_INET;    address.sin_addr.s_addr = inet_addr(SERVER);    address.sin_port = htons(PORT);    len = sizeof(address);    if ((result = connect(client_sockfd, (struct sockaddr *) &address, len)) < 0) {        perror("connect() 呼叫失敗");        closesocket(client_sockfd);        exit(EXIT_FAILURE);    }    else {        printf("连接成功...\n");    }    HANDLE handle1 = CreateThread(NULL, 0, processRead, (LPVOID)client_sockfd, 0, NULL);    HANDLE handle2 = CreateThread(NULL, 0, processWrite, (LPVOID)client_sockfd, 0, NULL);    WaitForSingleObject(handle1, INFINITE);    WaitForSingleObject(handle2, INFINITE);    CloseHandle(handle1);    CloseHandle(handle2);    closesocket(client_sockfd);    //释放winsock库     WSACleanup();    system("pause");    return 0;}

server.c

#define _CRT_SECURE_NO_WARNINGS#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #include <Ws2tcpip.h>#define PORT 12345 //端口#pragma comment(lib, "WS2_32") //库#define BUFF_LEN 1024int main(int argc, char *argv[]) {    WSADATA wsaData; //windows下结构信息    SOCKET server_sockfd, client_sockfd;     int on = 1;    int server_len, client_len;    struct sockaddr_in server_address; //数据结构    struct sockaddr_in client_address;    int result;    fd_set readfds, testfds;//select    int fdmax;    char buf[BUFF_LEN];    char tmp[BUFF_LEN];    char msg[BUFF_LEN];    timeval tv;    tv.tv_sec = 0; //秒    tv.tv_usec = 500; // 微秒 100 500    int j;    // 初始化socket dll ,请求2.2的版本    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)    {        printf("Init socket dll error!");        exit(1);    }    // 创建socket     // 第一个参数,指定地址簇(TCP/IP只能是AF_INET,也可写成PF_INET)      // 第二个,选择套接字的类型(流式套接字),第三个,特定地址家族相关协议(0为自动)    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);    if (server_sockfd == INVALID_SOCKET)    {        printf("socket() 失败");        exit(EXIT_FAILURE);    }    u_long u1 = 1;    //控制套接口的模式,    //FIONBIO 非阻塞设置,非零;阻塞设置,零;    ioctlsocket(server_sockfd, FIONBIO, (u_long*)& u1);    //sin_family表示地址族,对于IP地址,sin_family成员将一直是AF_INET。      //sin_port指定将要分配给套接字的端口。      //sin_addr给出套接字的主机IP地址。     //将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。      server_address.sin_family = AF_INET;    server_address.sin_addr.s_addr = htonl(INADDR_ANY);    server_address.sin_port = htons(PORT);    server_len = sizeof(server_address);    //绑定socket和服务端(本地)地址     if (bind(server_sockfd, (sockaddr *)&server_address, server_len) == SOCKET_ERROR)    {        printf("Server Bind Failed: %d", WSAGetLastError());        exit(1);    }    //监听     if (listen(server_sockfd, 10) == -1)    {        printf("Server Listen Failed: %d", WSAGetLastError());        exit(1);    }    FD_ZERO(&readfds); //将 sockset 清空    FD_SET(server_sockfd, &readfds);//把 sockfd 加入到 sockset 集合中    FD_ZERO(&testfds);//将 sockset 清空    memset(&msg, BUFF_LEN, 0);    memset(&tmp, BUFF_LEN, 0);    /* 记录目前fd的数量 */    fdmax = server_sockfd;    printf("服务器已经启动,等待客户上线\n");    for (;;) {        int fd = 0;        /* 复制编号 */        testfds = readfds;//每次在调用select前一定要更新一次           /* 使用 select() 实现多人聊天 */        //这里只监控了读取功能        result = select(0, &testfds, NULL, NULL, &tv);        if (result < 0) {            perror("服务器发生问题\n");            exit(EXIT_FAILURE);        }        /* 遍历 fd_set  扫描所有的文件描述符*/        for (fd = 0; fd < (int)testfds.fd_count; fd++) {            if (testfds.fd_array[fd] == server_sockfd)             {                   /*找到相关文件描述符*/                  if (FD_ISSET(testfds.fd_array[fd], &testfds)) //有新的连接                {                    client_len = sizeof(client_address);                    client_sockfd = accept(server_sockfd, (struct sockaddr *) &client_address, &client_len);                    FD_SET(client_sockfd, &readfds);//将客户端socket加入到集合中                      char ipBuf[20] = { 0 };//                  inet_ntop(AF_INET, (void*)&client_address.sin_addr, ipBuf, 16);                    inet_ntoa(client_address.sin_addr);//将sin_addr储存的IP(数值)转换成字符串形式(127.0.0.1)                    printf("%s: 新连接%s 到 socket#%d\n", argv[0], inet_ntoa(client_address.sin_addr), client_sockfd);                    break;                }            }            else             {   //客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据                //第一个参数指定接收端套接字描述符                if (recv(testfds.fd_array[fd], buf, BUFF_LEN - 1, 0) > 0)                {                    printf("buf = %s\n", buf);                    for (j = 0; j < (int)readfds.fd_count; j++)                    {                        if ((readfds.fd_array[j] != testfds.fd_array[fd]) && (readfds.fd_array[j] != server_sockfd))    //不发送给自己                            send(readfds.fd_array[j], buf, sizeof(buf), 0);                    }                }            }        }    }    closesocket(server_sockfd);    //释放winsock库     WSACleanup();    return 0;}
0 0