1.Socket地址复用

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之前加入代码(完整代码请參照博文最后):

    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(一条连接一个进程, 我们在多线程那一章中以前提出过一条连接一个线程, 这样的方案相比較而言能够比多进程拥有更高的并发量);

/** 演示样例: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调用会一直堵塞;

//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);
}
//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

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Socket编程实践(4) --更复杂的过程server的更多相关文章

  1. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  2. Socket编程实践(6) --TCPNotes服务器

    僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...

  3. C# socket编程实践

    C# socket编程实践——支持广播的简单socket服务器   在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...

  4. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  5. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  6. Socket编程实践(1) 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  7. C# socket编程实践——支持广播的简单socket服务器

    在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...

  8. Socket编程实践(11) --epoll原理与封装

    常用模型的特点 Linux 下设计并发网络程序,有典型的Apache模型(Process Per Connection,PPC), TPC(Thread Per Connection)模型,以及 se ...

  9. Socket编程实践(12) --UDP编程基础

    UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...

随机推荐

  1. BGP协议学习总结

    BGP学习总结 BGP是目前使用的唯一的自治系统间的路由协议,它是一种矢量路由协议,基于TCP的179号端口,它采用单播增量更新的方式更新路由,与其他的路由协议不同的是,BGP只要TCP可达,就可以建 ...

  2. hdu3652(数位dp)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3652 题意:求1~n含有13且能被13整除的数的个数. 分析:数位dp,dp数组加一维来维护到pos位 ...

  3. 写代码质量改善java计划151建议——导航开始

    2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...

  4. PHP实现冒泡排序、双向冒泡排序算法

    冒泡排序(Bubble Sort),是一种较简单的.稳定的排序算法.冒泡排序算法步骤:比较相邻的元素,如果第一个比第二个大,就交换他们两个的位置:对每对相邻的元素执行同样的操作,这样一趟下来,最后的元 ...

  5. linux--shell script

    下面是最近学习shell script的一些知识点总结 ***博客园-邦邦酱好***   1.介绍shell是一个文字接口,让我们与系统沟通.shell script就是针对shell所写的脚本.它就 ...

  6. jQuery获取url参数值

    $.extend({ getUrlVars: function () { var vars = [], hash; var hashes = window.location.href.slice(wi ...

  7. windows phone 7 通过麦克风录音,并且播放

    原文:windows phone 7 通过麦克风录音,并且播放 //模拟XNA的框架(凡是在wp7中应用xna的都必须先模拟此类) public class XNAAsyncDispatcher : ...

  8. TestThreadPoolExecutor.java

    package           ; import java.io.IOException;import java.io.InputStream;import java.util.List;impo ...

  9. ZooKeeper集群安装

    ZooKeeper是Apache提供的.分布式服务协调系统,应用比較广泛. 由于项目中使用Kafka MQ,而Kafka全然使用ZooKeeper实现Kafka各组件的服务协调,包含Broker.Co ...

  10. Windows Phone开发(24):启动器与选择器之发送短信

    原文:Windows Phone开发(24):启动器与选择器之发送短信 本节我们通过一个简单的发送短信示例来演示一下如果配合使用PhoneNumberChooserTask和SmsComposeTas ...