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. 关于WHERE后面不能放聚合函数(如SUM(...))的解决办法

    我们在编写SQL语句的时候,常常会遇到需要将SUM()放到WHERE后面作为条件查询,事实证明这样是无法执行的,执行会报异常:聚合不应出现在 WHERE 子句中. 那么如何解决呢,使用HAVING关键 ...

  2. js实现页面重定位的几种方法

    参考地址:http://www.cnblogs.com/super-d2/archive/2011/10/01/2197004.html js实现页面重定向 在现行的网站应用中URL重定向的应用有很多 ...

  3. 解压system.img

    解压: All-Series:~$ simg2img system.img system.img.ext4 All-Series:~$ mkdir tmp All-Series:~$ mount -t ...

  4. JDBC与反射

    什么是JDBC Java定义了一套关于连接使用数据库的规范(接口)叫做JDBC,许多数据库厂商实现了这个规范,所以我们可以通过Java提供的接口编程,使得我们更换数据库的时候不用修改原来的代码,只需要 ...

  5. SQL参数化查询的问题

    最近碰到个问题, SQL语句中的 "... like '%@strKeyword%'"这样写查不出结果, 非的写成 "... like '%" + strKey ...

  6. Multitasking Apps may only use background services for their intended purposes

    2.16 Details Your app declares support for audio in the UIBackgroundModes key in your Info.plist, bu ...

  7. 一篇哥们自己的写的IBM电话面试感想-@大国

    两天没写博了,还是没有养成一个习惯.前天和昨天休息,和哥们几个出去打球,运动一下,放松下脑子.今天就补一篇我哥们自己的写的关于他的IBM电话面试的感想,希望能帮到有需要的人. ------------ ...

  8. J2SE基础:4.面向对象的特性一

    面向对象的特性 封装 继承多态 封装: 定义: 通过对象的封装,实现了模块化和信息隐藏. 通过对类的成员施以一定的訪问权限,实现了类中成员 的信息隐藏 注意点: 对象自已该做的一些事情与方法不能交与其 ...

  9. Oracle SQL Lesson (6) - 使用Join进行联合查询

    使用连接SQL 1999SELECT table1.column, table2.columnFROM table1[NATURAL JOIN table2] |[JOIN table2 USING ...

  10. 王立平--include在Android应用

    它包括一个布局和布局 1.在layout确定activity_other.xml布局 2.代码中的包括例如以下: <LinearLayout xmlns:android="http:/ ...