整理一下老師講的。
頭文件link.h
#ifndef LINK_H
#define LINK_H
#include <stdio.h>
typedef struct Node
{
int iId;
int sockfd;
struct Node *pNext;
}Node;//節(jié)點(diǎn)
typedef struct List
{
int iLen;
Node *pFirstNode;
}List;//用于儲(chǔ)存多個(gè)用戶的鏈表
Node *makeNode();
List *makeList();
void insertList(List *pList, Node *pNode);
Node *findNode(const List *pList, int iId);
void showList(const List *pList);
#endif
頭文件protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <stdio.h>
typedef unsigned int uint;
enum ENUM_MSG_TYPE
{
ENUM_MSG_MIN = 0,
ENUM_MSG_PRIVATE_CHAT, //私聊
ENUM_MSG_GROUP_CHAT, //群聊
ENUM_MSG_REQUEST_USR_LIST, //請(qǐng)求用戶列表
ENUM_MSG_RESPONSE_USR_LIST, //回復(fù)用戶列表
ENUM_MSG_MAX = 0x0fffffff//保持占32位
};
typedef struct PDU
{
uint iPDULen; //整個(gè)協(xié)議數(shù)據(jù)單元的大小
uint iType; //消息類(lèi)型:群聊,私聊,獲取用戶列表
uint iDataLen; //消息的長(zhǎng)度
int iFromId; //消息的發(fā)送者
int iToId; //消息的接收者
char caData[4]; //要發(fā)送的消息
}PDU;
PDU *makePDU(int iDataLen);
uint parsePDU(PDU *pPDU);
#endif
客戶端與服務(wù)器都要和這個(gè)文件一起編譯 protocol.c
#include "protocol.h"
#include <stdlib.h>
#include <string.h>
PDU *makePDU(int iDataLen)//創(chuàng)建數(shù)據(jù)協(xié)議存儲(chǔ)端
{
PDU *pPDU = NULL;
uint iLen = sizeof(PDU) - 4*sizeof(char) + iDataLen;
//四個(gè)成員的大小-4個(gè)可變長(zhǎng)得到前面三個(gè)的長(zhǎng)度+要發(fā)送數(shù)據(jù)的長(zhǎng)度=整個(gè)結(jié)構(gòu)體的大小
pPDU = (PDU *)malloc(iLen);
if (NULL != pPDU)
{
memset(pPDU, 0, iLen);//初始化
pPDU->iPDULen = iLen;//協(xié)議數(shù)據(jù)單元的大小
pPDU->iType = ENUM_MSG_MIN;//消息類(lèi)型初始值
pPDU->iDataLen = iDataLen;
}
return pPDU;
}
uint parsePDU(PDU *pPDU)
{
uint iType = ENUM_MSG_MIN;
if (NULL != pPDU)
{
iType = pPDU->iType;
}
return iType;
}
服務(wù)器 server.c
/*socket()*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include "link.h"
#include "protocol.h"
List *pList = NULL;
void sendPDUToClient(int sockfd, const PDU *pPDU)
{
if (sockfd < 0 || NULL == pPDU)
{
return;
}
int ret = -1;
int iLeft = pPDU->iPDULen;//剩余單元的大小
int iSended = 0;//已發(fā)送的人數(shù)
while (iLeft)
{
ret = write(sockfd, (char *)pPDU+iSended, iLeft);
if (-1 == ret)
{
if (EINTR == errno)
{
continue;
}
perror("write");
return;
}
iSended += ret;
iLeft -= ret;
}
}
void sendUsrListToClient(int sockfd, const List *pList)//把使用者鏈表發(fā)送給客戶端
{
if (sockfd < 0 || NULL == pList)
{
return;
}
int iDataLen = pList->iLen * sizeof(int);
PDU *pPDU = makePDU(iDataLen);//一點(diǎn)一點(diǎn)拼接字節(jié)
if (NULL == pPDU)
{
return;
}
Node *pNode = pList->pFirstNode;
int i = 0;
while (NULL != pNode)
{
memcpy(pPDU->caData+i*sizeof(int)
, &(pNode->iId), sizeof(int));
i++;
pNode = pNode->pNext;
}
pPDU->iType = ENUM_MSG_RESPONSE_USR_LIST;
sendPDUToClient(sockfd, pPDU);//發(fā)送信息給客戶端
}
void updateClientsUsrList(const List *pList)
{
if (NULL == pList)
{
return;
}
PDU *pPDU = makePDU(sizeof(int));
if (NULL == pPDU)
{
return;
}
memcpy(pPDU->caData, &(pList->pFirstNode->iId)
, sizeof(int));
pPDU->iType = ENUM_MSG_RESPONSE_USR_LIST;
Node *pNode = pList->pFirstNode->pNext;
while (NULL != pNode)
{
printf("id:%d, sockfd:%d\n", pNode->iId, pNode->sockfd);
sendPDUToClient(pNode->sockfd, pPDU);
pNode = pNode->pNext;
}
}
PDU *readDataFromClient(int sockfd)
{
int ret = -1;
uint iPDULen = 0;
ret = read(sockfd, &iPDULen, sizeof(uint));
if (-1 == ret)
{
perror("read");
return NULL;
}
PDU *pPDU = makePDU(iPDULen-3*sizeof(int));
if (NULL == pPDU)
{
return NULL;
}
pPDU->iPDULen = iPDULen;
int iLeft = iPDULen - sizeof(int);
int iReaded = sizeof(int);
while (iLeft)
{
ret = read(sockfd, (char *)pPDU+iReaded, iLeft);
if (-1 == ret)
{
if (EINTR == errno)
{
continue;
}
perror("while read");
return NULL;
}
iLeft -= ret;
iReaded += ret;
}
return pPDU;
}
void handleGroupChat(PDU *pPDU)//群發(fā)
{
if (NULL != pPDU)
{
Node *pNode = pList->pFirstNode;
while (NULL != pNode)
{
sendPDUToClient(pNode->sockfd, pPDU);
pNode = pNode->pNext;
}
}
}
void handlePrivateChat(PDU *pPDU)//私聊
{
if (NULL != pPDU)
{
Node *pNode = NULL;
pNode = findNode(pList, pPDU->iToId);
if (NULL != pNode)
{
sendPDUToClient(pNode->sockfd, pPDU);
}
}
}
void *handleClient(void *arg)//處理客戶端發(fā)來(lái)的信息
{
int clientSockfd = (int)arg;
PDU *pPDU = NULL;
uint iType = 0;
while (1)
{
pPDU = readDataFromClient(clientSockfd);
if (NULL != pPDU)
{
iType = parsePDU(pPDU);
switch (iType)
{
case ENUM_MSG_REQUEST_USR_LIST:
break;
case ENUM_MSG_GROUP_CHAT:
handleGroupChat(pPDU);
break;
case ENUM_MSG_PRIVATE_CHAT:
handlePrivateChat(pPDU);
break;
default:
break;
}
}
}
}
int main(void)
{
pList = makeList();
if (NULL == pList)
{
return -1;
}
int iBaseId = 1001;
int sockfd = -1;
//創(chuàng)建socket描述符,用于監(jiān)聽(tīng)接受客戶端的連接
//AF_INET:ipv4
//SOCK_STREAM:tcp協(xié)議
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket");
return -1;
}
int ret = -1;
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; //ipv4
serverAddr.sin_port = htons(8888); //port
//server ip
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//根據(jù)自己電腦的地址,ifconfig查詢
bzero(&(serverAddr.sin_zero), 8);
//將sockfd和地址進(jìn)行綁定
ret = bind(sockfd, (struct sockaddr *)&serverAddr
, sizeof(serverAddr));
if (-1 == ret)
{
perror("bind");
return -1;
}
//監(jiān)聽(tīng)客戶端的連接
//sockfd:監(jiān)聽(tīng)的socket描述符
//5:表示未經(jīng)過(guò)處理的連接請(qǐng)求隊(duì)列可以容納的最大數(shù)目
ret = listen(sockfd, 5);
if (-1 == ret)
{
perror("listen");
return -1;
}
struct sockaddr_in clientAddr;
int iLen = sizeof(clientAddr);
int clientSockfd = -1;
Node *pNode = NULL;
pthread_t thread;
while (1)
{
//阻塞等待客戶端的連接,直到有客戶端連接
//成功返回socket描述符,
//該socket描述符用于服務(wù)器與客戶端進(jìn)行通信
//失敗返回-1
//sockfd:監(jiān)聽(tīng)的socket描述符
//clientAddr:用于保存客戶端的地址等信息
//iLen:地址長(zhǎng)度變量指針
clientSockfd = accept(sockfd
, (struct sockaddr *)&clientAddr
, &iLen);
if (-1 == clientSockfd)
{
perror("accept");
return -1;
}
printf("there is a client connected...\n");
pthread_create(&thread, NULL, handleClient
, (void *)clientSockfd);
pNode = makeNode();
if (NULL == pNode)
{
continue;
}
pNode->iId = iBaseId;
pNode->sockfd = clientSockfd;
iBaseId++;
insertList(pList, pNode);
sendUsrListToClient(clientSockfd, pList);
updateClientsUsrList(pList);
}
return 0;
}
客戶端 client.c
/*socket()*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include "link.h"
#include "protocol.h"
#define MAX_DATA_LEN 64
List *pList = NULL;
uint g_iId = 0;
void getUsrListFromPDU(PDU *pPDU)
{
if (NULL == pPDU || NULL == pList)
{
return;
}
int iNum = pPDU->iDataLen / sizeof(int);
Node *pNode = NULL;
int i = 0;
while (iNum)
{
pNode = makeNode();
if (NULL == pNode)
{
return;
}
memcpy(&(pNode->iId), pPDU->caData+i*sizeof(int)
, sizeof(int));
insertList(pList, pNode);
i++;
iNum--;
}
if (0 == g_iId)
{
pNode = pList->pFirstNode;
while (NULL != pNode->pNext)
{
pNode = pNode->pNext;
}
g_iId = pNode->iId;
}
showList(pList);
}
PDU *readDataFromServer(int sockfd)
{
int ret = -1;
uint iPDULen = 0;
ret = read(sockfd, &iPDULen, sizeof(uint));
if (-1 == ret)
{
perror("read");
return NULL;
}
PDU *pPDU = makePDU(iPDULen-3*sizeof(int));
if (NULL == pPDU)
{
return NULL;
}
pPDU->iPDULen = iPDULen;
int iLeft = iPDULen - sizeof(int);
int iReaded = sizeof(int);
while (iLeft)
{
ret = read(sockfd, (char *)pPDU+iReaded, iLeft);
if (-1 == ret)
{
if (EINTR == errno)
{
continue;
}
perror("while read");
return NULL;
}
iLeft -= ret;
iReaded += ret;
}
return pPDU;
}
void showChatData(PDU *pPDU)
{
if (NULL != pPDU)
{
char caMsg[MAX_DATA_LEN] = {'\0'};
memcpy(caMsg, pPDU->caData, pPDU->iDataLen);
printf("%d says: %s\n", pPDU->iFromId, caMsg);
}
}
void *readThread(void *arg)
{
int sockfd = (int)arg;
PDU *pPDU = NULL;
uint iType = 0;
while (1)
{
pPDU = readDataFromServer(sockfd);
iType = parsePDU(pPDU);
switch (iType)
{
case ENUM_MSG_RESPONSE_USR_LIST:
getUsrListFromPDU(pPDU);
break;
case ENUM_MSG_GROUP_CHAT:
case ENUM_MSG_PRIVATE_CHAT:
showChatData(pPDU);
break;
default:
break;
}
}
return NULL;
}
void sendPDUToServer(int sockfd, const PDU *pPDU)
{
if (sockfd < 0 || NULL == pPDU)
{
return;
}
int ret = -1;
int iLeft = pPDU->iPDULen;
int iSended = 0;
while (iLeft)
{
ret = write(sockfd, (char *)pPDU+iSended, iLeft);
if (-1 == ret)
{
if (EINTR == errno)
{
continue;
}
perror("write");
return;
}
iSended += ret;
iLeft -= ret;
}
}
void readDataFromSTDIN(char caData[MAX_DATA_LEN])
{
memset(caData, '\0', MAX_DATA_LEN);
int ret = read(STDIN_FILENO, caData, MAX_DATA_LEN);
if (-1 == ret)
{
perror("read");
exit(0);
}
if ('\0' != caData[MAX_DATA_LEN-1]
&& '\n' != caData[MAX_DATA_LEN-1])
{
while ('\n' != getchar())
{}
}
caData[MAX_DATA_LEN-1] = '\0';
}
int main(void)
{
pList = makeList();
if (NULL == pList)
{
return -1;
}
int sockfd = -1;
//創(chuàng)建sockfd用于和服務(wù)器通信
//AF_INET: ipv4
//SOCK_STREAM:tcp
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket");
return -1;
}
int ret = -1;
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; //ipv4
serverAddr.sin_port = htons(8888); //port
//server ip
serverAddr.sin_addr.s_addr = inet_addr("192.168.16.193");
bzero(&(serverAddr.sin_zero), 8);
//連接服務(wù)器
ret = connect(sockfd, (struct sockaddr *)&serverAddr
, sizeof(serverAddr));
if (-1 == ret)
{
perror("connect");
return -1;
}
pthread_t thread;
ret = pthread_create(&thread, NULL
, readThread, (void*)sockfd);
if (0 != ret)
{
printf("%s\n", strerror(ret));
return -1;
}
//write data to server
char caData[MAX_DATA_LEN] = {'\0'};
int iLen = 0;
PDU *pPDU = NULL;
int iToId = 0;
char caMsg[MAX_DATA_LEN] = {'\0'};
while (1)
{
//1002 hello
readDataFromSTDIN(caData);
memset(caMsg, '\0', MAX_DATA_LEN);
sscanf(caData, "%d%s", &iToId, caMsg);
iLen = strlen(caMsg);//取長(zhǎng)度
if (iLen > 0)
{
pPDU = makePDU(iLen);
memcpy(pPDU->caData, caMsg, iLen);
pPDU->iFromId = g_iId;
if (0 == iToId)
{
pPDU->iType = ENUM_MSG_GROUP_CHAT;
}
else
{
pPDU->iToId = iToId;
pPDU->iType = ENUM_MSG_PRIVATE_CHAT;
}
sendPDUToServer(sockfd, pPDU);
}
}
return 0;
}
使用時(shí)修改一下客戶端里的ip地址與開(kāi)服務(wù)器的人機(jī)器對(duì)應(yīng),就可以實(shí)現(xiàn)簡(jiǎn)單對(duì)話和群聊。很多知識(shí)點(diǎn)不熟練,雖然看懂了但自己寫(xiě)還是很費(fèi)勁。話說(shuō)很多公司就專(zhuān)門(mén)需要做這方面的人啊,真希望我自己可以從頭到尾寫(xiě)一個(gè)出來(lái)。