Socket编程实践(4)

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

完整代码实现:

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 -> terminalelse 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 -> socketelse 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 -> terminalelse 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 -> socketelse 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);}

完整代码实现:

,有多远,走多远,把足迹连成生命线。

Socket编程实践(4)

相关文章:

你感兴趣的文章:

标签云: