您的位置:首页 > 编程语言

Socket编程实践(4) --多进程并发server

2016-01-04 12:26 281 查看
1.Socket地址复用

[cpp] view
plaincopy





int getsockopt(int sockfd, int level, int optname,

void *optval, socklen_t *optlen);

int setsockopt(int sockfd, int level, int optname,

const void *optval, socklen_t optlen);

服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).

可以在bind之前添加代码(完整代码请参照博文最后):

[cpp] view
plaincopy





int on = 1;

if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,

&on,sizeof(on)) == -1)

err_exit("setsockopt SO_REUSEADDR error");

用以支持地址复用.

2.process-per-connecton

我们的echo服务器最大的缺点就是无法支持多客户连接,即使客户端能够连接到服务器上(client端connect时并没有出错返回), 服务器也不为该客户做服务,(直接没什么反应),虽然链接是有的(也就是说,客户端是已经连接到服务器上的了,但是服务器就是不搭理你....), 我们提出的改进方案是process-per-connection(一条连接一个进程, 我们在多线程那一章中曾经提出过一条连接一个线程, 这种方案相比较而言能够比多进程拥有更高的并发量);

[cpp] view
plaincopy





/** 示例:echo server改进, 多进程模型(client并未更改)**/

void echo(int clientfd);

int main()

{

int listenfd = socket(AF_INET, SOCK_STREAM, 0);

if (listenfd == -1)

err_exit("socket error");

int on = 1;

if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,

&on,sizeof(on)) == -1)

err_exit("setsockopt SO_REUSEADDR error");

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(8001);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)

err_exit("bind error");

if (listen(listenfd, SOMAXCONN) == -1)

err_exit("listen error");

struct sockaddr_in clientAddr;

//谨记: 此处一定要初始化

socklen_t addrLen = sizeof(clientAddr);

while (true)

{

int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);

if (clientfd == -1)

err_exit("accept error");

//打印客户IP地址与端口号

cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)

<< ", " << ntohs(clientAddr.sin_port) << endl;

pid_t pid = fork();

if (pid == -1)

err_exit("fork error");

else if (pid > 0)

close(clientfd);

//子进程处理链接

else if (pid == 0)

{

close(listenfd);

echo(clientfd);

//子进程一定要exit, 否则的话, 该子进程也会回到accept处

exit(EXIT_SUCCESS);

}

}

close(listenfd);

}

void echo(int clientfd)

{

char buf[512] = {0};

int readBytes;

while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)

{

cout << buf;

if (write(clientfd, buf, readBytes) == -1)

err_exit("write socket error");

memset(buf, 0, sizeof(buf));

}

if (readBytes == 0)

{

cerr << "client connect closed..." << endl;

close(clientfd);

}

else if (readBytes == -1)

err_exit("read socket error");

}

完整代码实现:

http://download.csdn.net/detail/hanqing280441589/8458053

3. P2P聊天程序设计与实现

server端与client都有两个进程:

(1)父进程负责从socket中读取数据将其写至终端, 由于父进程使用的是read系统调用的阻塞版本, 因此如果socket中没有数据的话, 父进程会一直阻塞; 如果read返回0, 表示对端连接关闭, 则父进程会发送SIGUSR1信号给子进程, 通知其退出;

(2)子进程负责从键盘读取数据将其写入socket, 如果键盘没有数据的话, 则fgets调用会一直阻塞;

[cpp] view
plaincopy





//serever端代码与说明

int main()

{

int listenfd = socket(AF_INET, SOCK_STREAM, 0);

if (listenfd == -1)

err_exit("socket error");

int on = 1;

if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,

&on,sizeof(on)) == -1)

err_exit("setsockopt SO_REUSEADDR error");

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(8001);

addr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)

err_exit("bind error");

if (listen(listenfd, SOMAXCONN) == -1)

err_exit("listen error");

struct sockaddr_in clientAddr;

socklen_t addrLen = sizeof(clientAddr);

int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);

if (clientfd == -1)

err_exit("accept error");

close(listenfd);

//打印客户IP地址与端口号

cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)

<< ", " << ntohs(clientAddr.sin_port) << endl;

char buf[512] = {0};

pid_t pid = fork();

if (pid == -1)

err_exit("fork error");

//父进程: socket -> terminal

else if (pid > 0)

{

int readBytes;

while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)

{

cout << buf;

memset(buf, 0, sizeof(buf));

}

if (readBytes == 0)

cout << "client connect closed...\nserver exiting..." << endl;

else if (readBytes == -1)

err_exit("read socket error");

//通知子进程退出

kill(pid, SIGUSR1);

}

//子进程: keyboard -> socket

else if (pid == 0)

{

signal(SIGUSR1, sigHandler);

while (fgets(buf, sizeof(buf), stdin) != NULL)

{

if (write(clientfd, buf, strlen(buf)) == -1)

err_exit("write socket error");

memset(buf, 0, sizeof(buf));

}

}

close(clientfd);

exit(EXIT_SUCCESS);

}

[cpp] view
plaincopy





//client端代码与说明

int main()

{

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd == -1)

err_exit("socket error");

//填写服务器端口号与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");

if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)

err_exit("connect error");

char buf[512] = {0};

pid_t pid = fork();

if (pid == -1)

err_exit("fork error");

//父进程: socket -> terminal

else if (pid > 0)

{

int readBytes;

while ((readBytes = read(sockfd, buf, sizeof(buf))) > 0)

{

cout << buf;

memset(buf, 0, sizeof(buf));

}

if (readBytes == 0)

cout << "server connect closed...\nclient exiting..." << endl;

else if (readBytes == -1)

err_exit("read socket error");

kill(pid, SIGUSR1);

}

//子进程: keyboard -> socket

else if (pid == 0)

{

signal(SIGUSR1, sigHandler);

while (fgets(buf, sizeof(buf), stdin) != NULL)

{

if (write(sockfd, buf, strlen(buf)) == -1)

err_exit("write socket error");

memset(buf, 0, sizeof(buf));

}

}

close(sockfd);

exit(EXIT_SUCCESS);

}

完整代码实现:

http://download.csdn.net/detail/hanqing280441589/8460013
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: