目录
前言
记录一下ubuntu系统下,多线程与socket通讯的知识。
多线程
一、多线程注意事项
-
编译需导入pthread库,加上-lpthread,即
gcc 文件名 -o 目标文件名 -lpthread
-
线程中,禁止使用exit函数,会导致进程内所有线程全部退出,应使用
void pthread_ex it(void *retval);
一般retval=NULL。
二、线程创建
-
预留分配线程ID;
pthread_t tid; -
使用函数
int pthread_create( 参数1:传出参数,保存系统为我们分配好的线程ID。pthread_t类型 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。 参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。void *类型 参数4:线程主函数执行期间所使用的参数。传入下面线程体的void *arg)
-
线程体,本质上是一个函数
void* thr(void *arg){函数体}
三、其他
-
输出打印到屏幕,另一种写法:
fprintf(stderr,"stderr!");
-
把一个描述性错误消息输出到标准错误 stderr:
perror("simplex-talk:connect");
socket通讯
一、hostent结构体
-
来源:
hostent结构体是gethostbyname的返回值,而gethostbyname(char *host)是用解析域名的函数,其中host为域名,一般由外部给定(可以用主函数参数传入) -
结构:
struct hostent { char *h_name; //正式主机名 char **h_aliases; //主机别名 int h_addr; //主机IP地址类型:IPV4-AF_INET int h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位 char **h_addr_list; //主机的IP地址列表 };
是结构体,其中变量可以直接访问。
二、sockaddr_in结构体
-
初始化:
struct sockaddr_in sin; bzero((char*)&sin,sizeof(sin)); //归零 sin.sin_family=AF_INET; //IP地址类型,AF_INET为一个系统预定义的数 sin.sin_port=htons(23); //??23为任意指定数 bcopy(hostent.h_addr,(char*)&sin.sin_addr,hostent.h_length);//或者sin.sin_addr.s_addr= inet_addr("132.241.5.10");
一个sockaddr_in对应一个通讯者,记录通讯者信息(信息来自gethostbyname(IP)的分析结果hostent)
-
结构:
struct sockaddr_in { short int sin_family; //IP地址类型,=AF_INET即可 unsigned short int sin_port; unsigned char sin_zero[8]; struct in_addr sin_addr; };
是结构体,其中变量可以直接访问。
三、客户端(发信息)流程
-
由IP地址获取通讯者信息,由gethostbyname(IP)分析存入hostent中;
-
把分析结果hostent填入sockaddr_in中( 具体要填的见上方sockaddr_in初始化),建立代码层面的通讯者;
-
套接字s
socket()函数来创建套接字s(初始化s):int s =socket(PF_INET,SOCK_STREAM,0)
connect()函数来连接s与通讯者sin:
connect(s,(struct sockaddr*)&sin,sizeof(sin)
-
send就好。
send(s,字符串,字符串长度(加'\0'),0)
四、服务器端(收信息)流程
-
以本地地址INADDR_ANY,初始化一个sockaddr_in,记为sin;可用方法:
sin.sin_addr.s_addr=INADDR_ANY;
-
套接字s
socket()函数来创建套接字s(初始化s):int s =socket(PF_INET,SOCK_STREAM,0)
bind()函数来绑定s与服务器sin:
bind(s,(struct sockaddr*)&sin,sizeof(sin)
-
侦听功能将套接字置于侦听传入连接的状态
listen(s,MAX_PENDING);
其中MAX_PENDING为一个int,是挂起的连接队列的最大长度;也可写为SOMAXCONN(自动决定)
-
循环,尝试接受客户端的请求
new_s=accept(s,(struct sockaddr*)&sin,&len)
客户端被记录为new_s
-
接受客户端new_s的信息
len = recv(new_s,buf,sizeof(buf),0)
信息记录在字符串buf里,读出来的字节大小为len,读完了客户端阻塞的,当客户端下线,返回0,所以一般这样用
while (len = recv(new_s,buf,sizeof(buf),0)) { fputs(buf,stdout); }
-
关闭new_s
close(new_s)
五、虚拟机与本机互ping确认IP地址
-
利用ipconfig命令在命令行(终端)获取主机和虚拟机的ip信息。
-
在主机(windows)找当前连接网络的IPv4(我的是10.130.55.240)
-
在虚拟机终端里
ping 2中查到的对应ip地址
ping通会有返回数据
-
在虚拟机运行
ifconfig -a
找inet(我的是192.168.0.129),可能会报错,一般是没有安装包,安装即可
sudo apt install net-tools
-
在主机(windows)命令行,直接ping对应ip即可
六、c语言持续从键盘获取字符串
#define MAX_LINE 256
char buf[MAX_LINE];
while(fgets(buf,sizeof(buf),stdin))
{
buf[MAX_LINE-1]='\0';
len =strlen(buf)+1;
}
七、其他
- 具体程序请参考这里https://gitee.com/chengyunqi/robot-software-engineering/tree/master/ch2,存在一些循环关系需要注意。
- 需要在循环内创建线程,可以通过pthread_kill(tid,0)判断线程是否已经被创建,从而避免重复创建线程。
- ubuntu系统下测试程序时,可以分别开两个终端,一个充当客户端,一个充当服务器。
- 跨设备进行socket通信时,需要注意防火墙的问题