看了Linux程序设计4中文版,学习了多线程编程和socket编程。本文的程序参考自Linux程序设计4的第15章。

设计了一个客户端程序,一个服务端程序。使用TCP协议进行数据传输。

客户端进程创建了一个客户端使用的socket,一个socket地址结构体。设置这个socket地址结构体的端口和地址为要连接的服务端的端口和ip。然后使用客户端的socket尝试连接服务端(connect),如果连接失败直接退出。如果连接成功,则使用这个连接成功的socket进行数据传输(send和recv)。首先向服务端发送一个字节字符型数据,然后接收服务端102个字节字符型数据,不断循环。如果发送和接受出错被捕获到的话(res=-1),关闭socket,退出进程。

服务端程序使用了一个sever_socket,并把这个socket与本机的ip和监听的端口进行绑定,并监听这个socket,accept远端来的连接。Accept成功一次就会获得一个client_socket。这时创建一个新线程,并把这个这个client_socket作为线程参数传给新线程。一个新线程服务一个远端连接,处理他们的数据传输请求(不断的收发数据,循环)。

gcc client2.c -o client2 -lpthread

gcc server2.c -o server2 -lpthread

这个客户和服务端程序有一个很奇特的现象,就是服务端启动之后,接收客户端的连接,每个连接创建一个新线程进行服务。当有多个客户端程序连接服务端(打开多个终端窗口开程序),所有的客户端都在疯狂与服务端进行数据传输时,把其中一个客户端程序(Ctrl+C)掉,这时服务端的进程也会立刻终止,而服务端的终止会连带把其他正在传输数据的客户端进程一起终止掉。对应的出错的客户端和服务端的代码为如下所示。(PS:结束掉客户端不是每次都让服务端挂掉,有时候会使得服务端recv出错返回-1,被捕获,然后安全退出线程,进程依然安全。)

针对这个现象我做了一些测试。1、把客户端改为只收,服务端改为只发。

(客户端崩掉会导致服务端的send的res=-1,100%被捕获到,从而安全退出线程,不会威胁整个进程。

服务端崩掉会导致客户端recv出错为0,但没有被捕获,使得客户端在收不到数据的情况下一直死循环。客户端没有阻塞,recv的返回值是0。)

2、把客户端改为只发,服务端只收。

(客户端崩掉会导致服务端recv出错为0,但没有被捕获,从而多个线程无法退出,在死循环。

服务端崩掉会导致客户端send出错为-1,被捕获,从而安全退出。)

所以上面的奇特现象出现的原因是:

在对端崩溃之后,send出错只会返回-1,而recv出错有时返回-1,大多数时候返回0。只有在recv出错为0没有被捕获,而又继续send,才会导致进程立刻死亡(此时无法捕获send的错误)。

解决办法是:

两端的程序增加捕获recv的出错为0的情况,具体代码是if((res==-1)||(res==0))。这样无论哪端崩掉,对端都能够捕获出错的情况,处理出错的情况,不会出现进程突然死亡现象。

Client2.C程序:

#include <sys/types.h>

#include <sys/socket.h>

#include <stdio.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdlib.h>

int main(int argc,char* argv[])

{                                          //测试方法,把client2和server2放在相同

int sockfd;                              //或者不同文件夹下都可以

int len,i,res;                             //一个终端运行./client2  另外一个终端

struct sockaddr_in address;                //运行  ./server2 就可以看到结果了

int result;

char ch = 'A';                        //奇怪,一个client运行多个版本,每次获取的

char buff[1024];                     //居然是同一个socket。改个名字也不行

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

printf("socket is %d\n",sockfd);

address.sin_family = AF_INET;

address.sin_addr.s_addr = inet_addr("192.168.131.129");

address.sin_port = htons(9734);

len = sizeof(address);

result = connect(sockfd,(struct sockaddr*)&address,len);

if(result == -1)

{

perror("oops:client1");

exit(-1);

}

memset(buff,0,1024);

i = 0;

//for(i=0;i<100;i++)

for(;;)

{

res = send(sockfd,&ch,1,0);

if(res==-1)

{

printf("send error,res is %d,exiting program\n",res);

close(sockfd);

return(-1);

}

i++;

memset(buff,0,102);

res = recv(sockfd,buff,102,0);

if(res==-1)                         //if((res==-1)||(res==0))

{

printf("recv error,res is %d,exiting program\n",res);

close(sockfd);

return(-1);

}

printf("socket:%d,buff is %s,i is %d\n",sockfd,buff,i);

/**/

}

printf("exiting program\n");

close(sockfd);

return 0;

}

Server2.C程序:

#include <sys/types.h>

#include <sys/socket.h>

#include <stdio.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdlib.h>

#include <pthread.h>

void* thread1(void* arg)

{

int client_sock;

char buff[1024];

char ch;

int i,res;

memset(buff,0,1024);

strncpy(buff,"this is server",14);

client_sock = (int)arg;

printf("para from main thread,socket = %d\n",client_sock);

printf("before sending,buff is:%s\n",buff);

//for(i=0;i<100;i++)

for(;;)

{

//res = read(client_sock,&ch,1);

//if(res==-1)

//{

//      printf("read error\n");

//      close(client_sock);

//      break;

//      //pthread_exit((void*)(-1));

//}

res = recv(client_sock,&ch,1,0);

if(res==-1)

//if((res==-1)||(res==0))

{

printf("recv error!res is %d\n",res);

close(client_sock);

break;

//pthread_exit((void*)(-1));

}

res = send(client_sock,buff,102,0);

if(res==-1)

{

printf("send error,res is %d\n",res);

close(client_sock);

break;

//pthread_exit((void*)(-1));

}

/**/

}

printf("exiting thread\n");

close(client_sock);

return ((void*)0);

}

int main(int argc,char* argv[])

{

int result,re;

pthread_t a_thread;

int server_sockfd,client_sockfd;

int server_len,client_len;

struct sockaddr_in server_address;

struct sockaddr_in client_address;

server_sockfd = socket(AF_INET,SOCK_STREAM,0);

server_address.sin_family = AF_INET;

server_address.sin_addr.s_addr = inet_addr("192.168.131.129");

server_address.sin_port = htons(9734);         //注意这里的sin_port是两个字节的

server_len = sizeof(server_address);

result = bind(server_sockfd,(struct sockaddr*)&server_address,server_len);

if(result!=0)

{

printf("bind failed\n");

exit(1);

}

result = listen(server_sockfd,5);

if(result!=0)

{

printf("listen failed\n");

exit(1);

}

while(1)

{

char ch;

printf("server waiting\n");

client_len = sizeof(client_address);

client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);

printf("accept one client,socket:%d\n",client_sockfd);

re = pthread_create(&a_thread,NULL,thread1,(void*)client_sockfd);

if(re!=0)

{

printf("create thread failed\n");

exit(EXIT_FAILURE);

}

//read(client_sockfd,&ch,1);

//ch++;

//write(client_sockfd,&ch,1);

//close(client_sockfd);

}

return 0;

}

多线程编程以及socket编程_Linux程序设计4chapter15的更多相关文章

  1. 第九章:Python高级编程-Python socket编程

    第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...

  2. Linux高级编程--10.Socket编程

    Linux下的Socket编程大体上包括Tcp Socket.Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较 ...

  3. Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 & Socket编程基础)

    FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/ ...

  4. iOS网络编程笔记——Socket编程

    一.什么是Socket通信: Socket是网络上的两个程序,通过一个双向的通信连接,实现数据的交换.这个双向连路的一端称为socket.socket通常用来实现客户方和服务方的连接.socket是T ...

  5. C#网络编程:Socket编程

    套接字简介:套接字最早是Unix的,window是借鉴过来的.TCP/IP协议族提供三种套接字:流式.数据报式.原始套接字.其中原始套接字允许对底层协议直接访问,一般用于检验新协议或者新设备问题,很少 ...

  6. 【Linux编程】socket编程

    套接字是通信端点的抽象.文件描写叙述符用open函数创建,而套接字描写叙述符用socket函数创建.socket函数原型例如以下: int socket(int domain, int type, i ...

  7. 【IOS网络编程】socket编程 - Asyncsocket

    Phone的标准推荐是CFNetwork 库编程,其封装好的开源库是 cocoa AsyncSocket库,用它来简化CFNetwork的调用,它提供了异步操作 主要特性有: 队列的非阻塞的读和写,而 ...

  8. Linux C Socket 编程

    1 Socket 是什么 Socket(套接字),就是对 网络上进程通信 的 端点 的 抽象.一个 Socket 就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制. 从所处的位置来 ...

  9. 【Socket编程】Java通信是这样炼成的

    简介 网络无处不在,移动互联时代也早已到来,单机版程序慢慢的已没有生命力,所有的程序都要能够访问网络,比如 QQ 网络聊天程序.迅雷下载程序等,这些程序都要同网络打交道,本次将与各位小伙伴们分享的就是 ...

随机推荐

  1. 使用本地计划任务定时关闭azure虚拟机

    本文包含以下内容 前提条件 如何实现定时关闭虚拟机 前提条件 Controller 机器上必须安装 Azure PowerShell,并且要在 PowerShell 里登录一次 Azure, 请参见: ...

  2. SQL Server现有表上自增属性增删原理研究

    项目需求:线上有一张表,数据类型为int类型,现在由于项目变更,需要这一列添加自增属性,而且,为了保证能尽快完成,希望使用脚本来实现,而不是在表设计中通过GUI窗口来实现. 问题来了:SQL Serv ...

  3. <context:property-placeholder>配置资源文件

    直接在 spring 配置文件里面加上 <context:property-placeholder file-encoding="UTF-8" location=" ...

  4. Orchard Core 文档翻译 (七)Contents

    CMS Modules »Contents Contents (OrchardCore.Contents) 此模块提供内容管理服务. Liquid 您可以使用“content ”属性从liquid 视 ...

  5. Orchard Core 文档翻译 (四)CMS ModulesTitle (OrchardCore.Title)

    Title (OrchardCore.Title) 标题模块提供Title Part ,允许用户定义内容项的标题.它还定义了ContentItemMetadata方面的DisplayText属性 Th ...

  6. March 31 2017 Week 13 Friday

    Sometimes, you think the sky is falling down, actually, that is just because you stand slanting. 有时候 ...

  7. ABAP git客户端

    Jerry习惯把自己写的小程序放到自己的github上:https://github.com/i042416 对于写的ABAP程序,需要先把SAPGUI里的代码手动拷贝到本地,然后用git客户端pus ...

  8. 浏览器下出现net::ERR_BLOCKED_BY_CLIENT的解决办法

    转发网址:https://www.cnblogs.com/wenzheshen/p/7724065.html 当我们在做开发时,调试页面图片会出现部分图片无法正常显示,并且确认图片的地址正确: 按F1 ...

  9. idea 一次性自动导包

    当复制粘贴一段纯文本代码时,许多类需要导包. 如图 使用optimize imports了,发还是没有办法导入未导入的包,只能删除未使用导包,这跟eclipse不一样,让人很不习惯,查了好多资料都没有 ...

  10. 深度优先搜索(深搜)——Deep First Search【例题:迷宫】

           深度优先搜索 基本思想:先选择一种可能情况向前探索,在探索过程中,一点那发现原来的选择是错误的,就退回一步重新选择,继续向前探索,(回溯)反复进行. [例题]迷宫问题           ...