linux下socket编程的一些学习经历(一)

来源:互联网 发布:手机开淘宝店怎么注册 编辑:程序博客网 时间:2024/05/22 02:05
首次接触socket编程,通过在linux下实现一个简单聊天程序的客户端和服务端,加深对socket和linux的一些了解.
本篇先实现服务端的一些功能.

首先创建socket,在服务端socket需要绑定本地端口,对其进行相应初始化
///定义sockfd///流式Socket(SOCK_STREAM)针对于面向连接的TCP///数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDPint server_create = socket(AF_INET,SOCK_STREAM, 0);if(server_create == -1){perror("socket create false");exit(1);}///定义sockaddr_instruct sockaddr_in server_sockaddr;server_sockaddr.sin_family = AF_INET;server_sockaddr.sin_port = htons(SERVERPORT);server_sockaddr.sin_addr.s_addr = inet_addr(SERVERIP);

socket结构体的相应细节许多人之前也都已经提到过,这里不再赘述.

socket在创建完成后,需要进行绑定

///bind,成功返回0,出错返回-1///struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,///二者长度一样,都是16个字节.二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr,///一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中.if(bind(server_create,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1){perror("bind");exit(1);}
接下来将socket设置为监听

///sten,成功返回0,出错返回-1///socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,///等待客户的连接请求.if(listen(server_create,QUEUE) == -1){perror("listen");exit(1);}
至此socket就可以监听其他socket所发送的connect请求了,但是由于服务端需要同时处理多个用户的请求,所以此处使用多线程进行相应处理.

思路大概为本socket每监听到一个请求,便创建一个新的线程来处理.

使用一个while来实现

mi = 0;while(1){struct conMySocket my_consok;socklen_t length = sizeof(my_consok.clientSocket);///成功返回非负描述字,出错返回-1///accept在此处为阻塞函数,提取出所监听套接字的等待连接队列中第一个连接请求my_consok.con = accept(server_create, (struct sockaddr*)&(my_consok.clientSocket), &length);if(my_consok.con < 0){perror("connect");exit(1);}if(pthread_create(&thrid[mi], NULL, (void *)client_mythrdeal, &my_consok)){printf ("Create pthread error!\n");exit(1);}mi++;}
服务端的主要作用是为接入的用户提供其他用户的IP及端口号等相应信息,用户同用户的交流由客户端实现.

至此服务端主线程已经实现得差不多了,接下来是监听后创建的单独处理的线程所要实现的功能.

int client_mythrdeal(void* pmy_consok){char *ip = NULL;struct conMySocket myConSok = *(struct conMySocket*)pmy_consok;struct user_info nowUserInfo;memset(&nowUserInfo, 0, sizeof(struct user_info));//inet_ntoa要求的参数只需要到sin_addr,而sin_addr下的s_addr不需要strcpy(nowUserInfo.user_ipadrs, inet_ntoa(myConSok.clientSocket.sin_addr));sprintf(nowUserInfo.user_port, "%d", ntohs(myConSok.clientSocket.sin_port));ip = inet_ntoa(myConSok.clientSocket.sin_addr);printf("IP:%s:%d\n", ip, myConSok.clientSocket.sin_port);int nowId = 0;while(1){char buffer[BUFFER_SIZE];memset(buffer, 0, BUFFER_SIZE * sizeof(char));recv(myConSok.con, buffer, BUFFER_SIZE * sizeof(char), 0);printf("%s\n", buffer);//若为测试连接标志,发送确定标志返回if(strcmp(buffer, TESTSIGN) == 0){send(myConSok.con, TESTSIGNSUCS, sizeof(TESTSIGNSUCS), 0);continue;}//若为发送端口标志,发送确定标志并准备接收端口号并对nowUserInfo.user_serverport赋值if(strcmp(buffer, SENDPORTSIGN) == 0){send(myConSok.con, TESTSIGNSUCS, sizeof(TESTSIGNSUCS), 0);memset(buffer, 0, BUFFER_SIZE * sizeof(char));recv(myConSok.con, buffer, BUFFER_SIZE * sizeof(char), 0);strcpy(nowUserInfo.user_serverport, buffer);}//若为登陆核对标志,则调用账户密码核对函数if(strcmp(buffer, CHECKSIGN) == 0){nowId = checkAccountAndPassword(myConSok.con, nowUserInfo.user_ipadrs, nowUserInfo.user_port, nowUserInfo.user_serverport);continue;}//若为请求在线用户信息标志,则调用在线用户信息反馈函数if(strcmp(buffer, ONLINEUSERINFOREQUEST) == 0){onlineUserInfoRet(myConSok.con, nowId);continue;}//若为请求其他用户IP和绑定的端口号,则调用tagUserIPAndPort函数if(strcmp(buffer, OTHERUSERSOCKETREQUEST) == 0){tagUserIPAndPort(myConSok.con);continue;}}close(myConSok.con);return 0;}

接下来是相应的功能函数实现

//账户密码核对int checkAccountAndPassword(int requestSocket, char *user_ipadrs, char *user_port, char *user_serverport){char userAccount[SHORT_STRING], userPassword[SHORT_STRING];memset(userAccount, 0, SHORT_STRING * sizeof(char));memset(userPassword, 0, SHORT_STRING * sizeof(char));send(requestSocket, TESTSIGNSUCS, sizeof(TESTSIGNSUCS), 0);recv(requestSocket, userAccount, SHORT_STRING * sizeof(char), 0);recv(requestSocket, userPassword, SHORT_STRING * sizeof(char), 0);int i = 0;//计数用while(userlist[i] != NULL){if((strcmp((*userlist[i]).user_account, userAccount) == 0) && (strcmp((*userlist[i]).user_password, userPassword) == 0)){//若账户密码核对正确,则将用户当前客户端所具信息存储到用户信息表中,并将设为用户设为在线状态strcpy((*userlist[i]).user_ipadrs, user_ipadrs);strcpy((*userlist[i]).user_port, user_port);strcpy((*userlist[i]).user_serverport, user_serverport);(*userlist[i]).yn_online = 1;send(requestSocket, LOGINSUCCESSRET, sizeof(LOGINSUCCESSRET), 0);return i;}i++;}send(requestSocket, LOGINFAILURERET, sizeof(LOGINFAILURERET), 0);return 0;}
//在线用户信息反馈int onlineUserInfoRet(int requestSocket, int selfId){int i = 0;char onlineUserInfoStr[MAXINFOSTRLENGTH];memset(onlineUserInfoStr, 0, MAXINFOSTRLENGTH * sizeof(char));send(requestSocket, TESTSIGNSUCS, sizeof(TESTSIGNSUCS), 0);while(userlist[i] != NULL){if((*userlist[i]).yn_online == 1 && i != selfId){char id[10];memset(id, 0, 10 * sizeof(char));sprintf(id, "%d", (*userlist[i]).id_num);strcat(onlineUserInfoStr, "  ");strcat(onlineUserInfoStr, id);}i++;}if(strlen(onlineUserInfoStr))send(requestSocket, onlineUserInfoStr, sizeof(onlineUserInfoStr), 0);elsesend(requestSocket, NOBODYONLIEN, sizeof(NOBODYONLIEN), 0);}

//将其他用户IP和端口号反馈给请求用户int tagUserIPAndPort(int requestSocket){int i = 0;char tagUserIdStr[SHORT_STRING];memset(tagUserIdStr, 0, SHORT_STRING * sizeof(char));send(requestSocket, TESTSIGNSUCS, sizeof(TESTSIGNSUCS), 0);recv(requestSocket, tagUserIdStr, SHORT_STRING * sizeof(char), 0);while(userlist[i] != NULL){if((*userlist[i]).id_num == atoi(tagUserIdStr)){//发送用户存在标志后,继续发送用户IP和用户所在客户端的服务端绑定的端口号send(requestSocket, USEREXIST, sizeof(USEREXIST), 0);send(requestSocket, (*userlist[i]).user_ipadrs, SHORT_STRING * sizeof(char), 0);send(requestSocket, (*userlist[i]).user_serverport, SHORT_STRING * sizeof(char), 0);return 0;}i++;}send(requestSocket, USERNOEXIST, sizeof(USERNOEXIST), 0);return 1;}

服务端的主要代码至此已经编写的差不多了,接下来总结一下需要注意的细节吧.

首先是pthread的线程函数的参数传递,若要传递多个参数,可先创建一个结构体,然后将结构体的指针作为线程函数的参数传递进去.

另外在gcc下貌似是没有itoa函数的,可以用sprintf来代替.

由于用到了pthread,在编译的时候需加上-pthread

服务端的总结大概就到这吧,接下来是客户端的实现.

0 0