Socket 編程實踐(5) --p2p聊天程序設計與實現
來源:程序員人生 發布時間:2015-01-02 09:56:17 閱讀次數:3265次
1個短連接的client
//短鏈接客戶端
int main()
{
int loopCount = 20;
char sendBuf[BUFSIZ] = {0};
char recvBuf[BUFSIZ] = {0};
for (int i = 0; i < loopCount; ++i)
{
sprintf(sendBuf,"Hello Server %d
",i);
//創建新的套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if (sockfd == ⑴)
{
err_exit("socket error");
}
//填寫好
服務器地址及其端口號
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8002);
//本機測試,填寫127.0.0.1(回環地址)
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//連接server
if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴)
{
err_exit("connect error");
}
//向server發送數據
if (write(sockfd,sendBuf,strlen(sendBuf)) == ⑴)
{
err_exit("write socket error");
}
int readCount = 0;
//從server接收數據
if ((readCount = read(sockfd,recvBuf,sizeof(recvBuf))) < 0)
{
err_exit("read socket error");
}
//將其回寫到終端
write(STDOUT_FILENO,recvBuf,strlen(recvBuf));
close(sockfd);
}
return 0;
}

注:server端基于上1篇博客中的代碼
點對點聊天程序設計與實現
點對點聊天程序功能說明:

代碼實現與說明
//serever端完全代碼與說明
#include "commen.h"
int main()
{
//安裝信號接收函數
signal(SIGUSR1,onSignal);
//創建監聽套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if (sockfd == ⑴)
{
err_exit("socket error");
}
//添加地址復用
int optval = 1;
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == ⑴)
{
err_exit("setsockopt SO_REUSEADDR error");
}
//將監聽套接字綁定本機
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; //該行可以刪除
serverAddr.sin_port = htons(8001);
serverAddr.sin_addr.s_addr = INADDR_ANY; //綁定本機的任意1個IP地址
if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴)
{
err_exit("bind error");
}
//啟用監聽套接字
//1旦調用了listen,則sockfd編程被動套接字 -> 等待客戶真個連接(只能接受連接,不能發送連接)
if (listen(sockfd,SOMAXCONN) == ⑴)
{
err_exit("listen error");
}
//如果沒有客戶端鏈接的來到,則該系統調用會1直阻塞
struct sockaddr_in peerAddr;
socklen_t peerLen = sizeof(peerAddr);
int peerSockfd = accept(sockfd, (struct sockaddr *)&peerAddr,&peerLen);
if (peerSockfd == ⑴)
{
err_exit("accept error");
}
//客戶端來到
cout << "Client Connected:" << endl;
cout << " peerAddr.sin_addr: " << inet_ntoa(peerAddr.sin_addr) << endl;
cout << " peerAddr.sin_port: " << ntohs(peerAddr.sin_port) << endl;
//創建子進程
pid_t pid = fork();
if (pid == ⑴)
{
err_exit("fork error");
}
//父進程:從客戶端接收數據 -> 打印到屏幕
if (pid > 0)
{
close(STDIN_FILENO); //關閉沒用的文件描寫符
char recvBuf[BUFSIZ] = {0};
int readCount = 0;
//從客戶端接收數據
while ((readCount = read(peerSockfd,recvBuf,sizeof(recvBuf))) > 0)
{
recvBuf[readCount] = 0;
//將其打印到屏幕
fprintf(stdout,"%s",recvBuf);
memset(recvBuf,0,sizeof(recvBuf));
}
//對端已關閉
if (readCount == 0)
{
close(peerSockfd); //關閉通訊結點
cout << "peer connect closed" << endl;
//發送SIGUSR1信號給子進程,將子進程殺死
kill(pid,SIGUSR1);
_exit(0);
}
//接收毛病
else if (readCount == ⑴)
{
close(peerSockfd);
//發送SIGUSR1信號給子進程,將子進程殺死
kill(pid,SIGUSR1);
err_exit("read error");
}
}
else if (pid == 0) //子進程:從鍵盤接收數據 -> 寫到客戶端
{
close(STDOUT_FILENO);
char sendBuf[BUFSIZ] = {0};
//從鍵盤接收數據
while (true)
{
if (fgets(sendBuf,sizeof(sendBuf),stdin) == NULL)
{
break;
}
else if (strncmp(sendBuf,"quit",4) == 0)
{
close(peerSockfd);
kill(getppid(),SIGUSR2);
break;
}
//發送到client端
if (write(peerSockfd,sendBuf,strlen(sendBuf)) == ssize_t(⑴))
{
close(peerSockfd);
err_exit("write socket error");
}
memset(sendBuf,0,sizeof(sendBuf));
}
}
close(sockfd);
return 0;
}
//client端完全代碼與說明
#include "commen.h"
int main()
{
signal(SIGUSR1,onSignal);
//創建新的套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if (sockfd == ⑴)
{
err_exit("socket error");
}
//填寫server端IP地址及其端口號
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(8001);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//連接到server
if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == ⑴)
{
err_exit("connect error");
}
pid_t pid = fork();
if (pid == ⑴)
{
err_exit("fork error");
}
//父進程:從鍵盤接收數據 -> 將之發送給server端
if (pid > 0)
{
close(STDOUT_FILENO);
char sendBuf[BUFSIZ] = {0};
//從鍵盤接收數據
while (true)
{
if (fgets(sendBuf,sizeof(sendBuf),stdin) == NULL)
{
break;
}
else if (strncmp(sendBuf,"quit",4) == 0)
{
close(sockfd);
//發送SIGUSR1信號給子進程,將其關閉
kill(pid,SIGUSR1);
_exit(0);
}
//發送到server端
if (write(sockfd,sendBuf,strlen(sendBuf)) == ssize_t(⑴))
{
close(sockfd);
err_exit("write socket error");
}
memset(sendBuf,0,sizeof(sendBuf));
}
}
else if (pid == 0) //子進程:從server端接收數據,將其打印到屏幕
{
close(STDIN_FILENO);
char recvBuf[BUFSIZ] = {0};
int readCount = 0;
//從server端接收數據
while ((readCount = read(sockfd,recvBuf,sizeof(recvBuf))) > 0)
{
recvBuf[readCount] = 0;
//將其打印到屏幕
fprintf(stdout,"%s",recvBuf);
memset(recvBuf,0,sizeof(recvBuf));
}
if (readCount == 0)
{
close(sockfd);
cout << "peer connect closed" << endl;
//發送SIGUSR2信號給父進程,將其關閉
kill(getppid(),SIGUSR2);
_exit(0);
}
else if (readCount == ⑴)
{
close(sockfd);
err_exit("read error");
}
}
return 0;
}

可以看出,此時既沒有端口占用,也沒有僵尸進程!

附-commen.h
#ifndef COMMEN_H_INCLUDED
#define COMMEN_H_INCLUDED
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void err_exit(std::string str)
{
perror(str.c_str());
exit(EXIT_FAILURE);
}
struct SendStruct
{
int type;
char text[BUFSIZ];
};
void onSignal(int signalNumber)
{
switch (signalNumber)
{
case SIGUSR1:
cout << "child receive SIGUSR1" << signalNumber << endl;
_exit(0);
case SIGUSR2:
cout << "parent receive SIGUSR2: " << signalNumber << endl;
_exit(0);
default:
cout << "RECV OTHRER SIGNAL" << endl;
}
}
#endif // COMMEN_H_INCLUDED
附-Makefile
CC = g++
CPPFLAGS = -Wall -g -pthread
BIN = server client
SOURCES = $(BIN.=.cpp)
.PHONY: clean all
all: $(BIN)
$(BIN): $(SOURCES)
clean:
-rm -rf $(BIN) bin/ obj/ core
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈