Linux-socket程式設計-1對1和1對多聊天
阿新 • • 發佈:2018-11-03
1對1聊天
通過select新增可讀事件的監聽實現。
服務端:
#include "header.h" int main(void) { int listenfd = -1; int connfd = -1; struct sockaddr_in server, client; int on = 1; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(8888); server.sin_addr.s_addr = htonl(INADDR_ANY); if (0 > (listenfd = socket(AF_INET, SOCK_STREAM, 0))) err_exit("socket"); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (0 > bind(listenfd, (struct sockaddr *)&server, sizeof(server))) err_exit("bind"); listen(listenfd, 1024); printf("listen...\n"); memset(&client, 0, sizeof(client)); socklen_t len = sizeof(client); if (0 > (connfd = accept(listenfd, (struct sockaddr *)&client, &len))) err_exit("accept"); printf("client's ip is:%s, port is:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); char recv_buf[BUFSIZ]; char send_buf[BUFSIZ]; fd_set readfds; int maxfd = -1; int ret=-1; int i; while(1) { FD_ZERO(&readfds);//所有位置零 FD_SET(0, &readfds);//設定0位元位為1,用於傳送資料訊息通知 maxfd = maxfd > 0 ? maxfd : 0; FD_SET(connfd, &readfds);//設定connfd位元位為1,用於接受資料訊息通知, maxfd = maxfd > connfd ? maxfd : connfd; if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))//設定被監聽的檔案描述符的總數為maxfd + 1,readfds可讀事件,且無限等待 err_exit("select"); for (i=0; i<=maxfd; i++) { if (FD_ISSET(i, &readfds)) //select返回後,用FD_ISSET測試給定位是否置位,通過獲取那個bit位就知道是那個連線有資料 { if (i == 0)//有資料需要傳送 { fgets(send_buf, sizeof(send_buf), stdin); if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0)) err_exit("send"); } else if (i == connfd)//有資料需要接收 { if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0))) err_exit("recv"); else if (0 == ret) { printf("client quit!\n"); exit(0); } recv_buf[ret] = '\0'; printf("client: %s\n", recv_buf); } } } } close(listenfd); close(connfd); exit(0); }
客戶端:
#include "header.h" int main(void) { int connfd = -1; struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(8888); server.sin_addr.s_addr = inet_addr("192.168.1.50"); if (0 > (connfd = socket(AF_INET, SOCK_STREAM, 0))) err_exit("socket"); if (0 > connect(connfd, (struct sockaddr *)&server, sizeof(server))) err_exit("connect"); printf("connect success!\n"); char send_buf[BUFSIZ]; char recv_buf[BUFSIZ]; int maxfd= -1; fd_set readfds; int ret=-1; int i; while(1) { FD_ZERO(&readfds); FD_SET(0, &readfds); maxfd = maxfd > 0 ? maxfd : 0; FD_SET(connfd, &readfds); maxfd = maxfd > connfd ? maxfd : connfd; if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL)) err_exit("select"); for (i=0; i<=maxfd; i++) { if (FD_ISSET(i, &readfds)) { if (0 == i) { fgets(send_buf, sizeof(send_buf), stdin); if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0)) err_exit("send"); } else if (connfd == i) { if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0))) err_exit("recv"); else if (0 == ret) { printf("server quit!\n"); exit(0); } recv_buf[ret] = '\0'; printf("server: %s\n", recv_buf); } } } } close(connfd); exit(0); }
1對多聊天
服務端:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <sys/select.h> struct peer_node { //連結串列結構體 int id; //id號 int connfd; //描述符 struct peer_node *next; //指標 }; static struct peer_node *head; //結構體指標 int server_init(short port) //server伺服器初始化函式 { int sockfd; int on = 1; struct sockaddr_in self; if(0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) { //建立套接字 fprintf(stderr, "socket : %s\n", strerror(errno)); return -errno; //返回出錯 } if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))) { //允許重用本地址和埠 fprintf(stderr, "setsockopt : %s\n", strerror(errno)); exit(1); } memset(&self, 0, sizeof(self)); //初始化 self.sin_family = AF_INET; //ipv4 self.sin_port = htons(port); //埠 self.sin_addr.s_addr = INADDR_ANY; //本地ip if(0 > bind(sockfd, (struct sockaddr *)&self, sizeof(self))) { //繫結本地資訊 fprintf(stderr,"socket bind error:%s\n",strerror(errno)); return -errno; } printf("listen...\n"); //啟動監聽 listen(sockfd,1024); return sockfd; //返回描述符 } int find_id_by_connfd(int connfd) //在連結裡找id { struct peer_node *node; //節點 node = head->next; while(node) { if(node->connfd == connfd) return node->id; //返回id node = node->next; } return -1; } int show_message(int skfd) //列印資訊 { char buf[1024]; struct sockaddr_in client; socklen_t len; int ret; len = sizeof(client); memset(&client, 0, sizeof(client)); ret = recv(skfd,buf,1023,0); //收 if(ret == 0) { return 0; } buf[ret] = 0; if(0 > getpeername(skfd, (struct sockaddr *)&client, &len)) { //或得資訊 fprintf(stderr, "getpeername : %s\n", strerror(errno)); exit(1); } printf("message received from id:%d [%s:%d] : %s", find_id_by_connfd(skfd),inet_ntoa(client.sin_addr),ntohs(client.sin_port), buf); return 1; //列印 id ip 埠 資訊 } int add_peerlist(int skfd) //把id從尾壓入連結串列 { struct peer_node *node; //節點 static int id = 1; struct peer_node *tail; //尾 node = (struct peer_node *) malloc(sizeof(struct peer_node)); //開闢 堆 if(node == NULL) { perror("malloc"); return -1; } node->id = id; node->connfd = skfd; node->next = NULL; tail = head; while(tail->next) { tail = tail->next; } tail->next = node; id++; //id 自加 return (node->id); } int del_peerlist(int skfd) //刪除 { struct peer_node *node; struct peer_node *tmp; int id; node = head; while(node->next->connfd != skfd) { node = node->next; } tmp = node->next; id = tmp->id; node->next = tmp->next; free(tmp); return id; } int show_peerlist(void) //列印id列表 { struct peer_node *node; struct sockaddr_in client; int ret; socklen_t len = sizeof(client); node = head->next; printf("=========================\n"); while(node) { ret = getpeername(node->connfd, (struct sockaddr *)&client,&len); if(ret < 0) { perror("getpeername"); } printf("%d:%s:%d\n",node->id, inet_ntoa(client.sin_addr),ntohs(client.sin_port)); //id ip 埠 node = node->next; } printf("=========================\n"); return 0; } int find_connfd_by_id(int id) // 找檔案描述符 { struct peer_node *node; node = head->next; while(node) { if(node->id == id) return node->connfd; //返回 node = node->next; } return -1; } int init_peerlist(void) //初始化連結串列 { head = (struct peer_node *) malloc(sizeof(struct peer_node)); if (NULL == head) { fprintf(stderr, "malloc : %s\n", strerror(errno)); exit(1); } head->connfd = -1; head->next = NULL; return 0; } int send_message(void) //傳送資訊 { char buf[1024]; char *ptr = NULL; int skfd, id; char id_str[10]; //存放id fgets(buf,1024,stdin); if (!strncasecmp(buf, "quit", 4)) { printf("server close!\n"); exit(1); } if(0 == strncmp(buf,"list",4)) { //list 顯示列表 show_peerlist(); return 0; } if(NULL == (ptr = strstr(buf,":"))) { //找出“:”位置指標 fprintf(stderr,"please usage \"ID:Message\" format!\n"); return 0; } memset(id_str, 0, 10); memcpy(id_str, buf, (ptr - buf)); //拷貝記憶體內容 把1:***** 字元1拷貝到 id_str if (0 >= (id = atoi(id_str))) { //轉換成整數 fprintf(stderr, "the id is not valid!\n"); return -1; } skfd = find_connfd_by_id(id); if(skfd < 0) { fprintf(stderr,"Not found valid user!!!\n"); return -1; } if (0 > send(skfd, ptr+1, strlen(ptr + 1) + 1, 0)) { //傳送:後面內容 fprintf(stderr,"send : %s\n", strerror(errno)); return -1; } return 0; } int main(int argc, char *argv[]) //主函式 { if (2 > argc) { //判斷 fprintf(stderr, "Usage : %s + server portnumber\n", argv[0]); exit(1); } int listenfd; int newfd; int ret,maxfd,i,id; fd_set current_fds,bak_fds; char buf[1024]; init_peerlist(); short portnumber; if (0 >= (portnumber = atoi(argv[1]))) { //埠號輸入 fprintf(stderr, "port number is invalid!\n"); exit(1); } listenfd = server_init(portnumber); //呼叫前面伺服器函式 if(listenfd < 0) exit(-listenfd); printf("type \"list\" for the client list!\n"); FD_ZERO(¤t_fds); FD_ZERO(&bak_fds); FD_SET(0, ¤t_fds); FD_SET(listenfd, ¤t_fds); maxfd = listenfd > 0 ? listenfd : 0; while(1) { bak_fds = current_fds; ret = select(maxfd+1, &bak_fds, NULL, NULL, NULL); //多路複用 if(ret < 0) { fprintf(stderr,"select error:%s\n",strerror(errno)); return errno; } for(i = 0; i <= maxfd; i++) { if(FD_ISSET(i, &bak_fds)) { if(0 == i) { send_message(); } else if (i == listenfd){ if (0 > (newfd = accept(listenfd, NULL, NULL))) { fprintf(stderr, "accept : %s\n", strerror(errno)); exit(1); } FD_SET(newfd, ¤t_fds); if(newfd > maxfd) maxfd = newfd; id = add_peerlist(newfd); snprintf(buf, sizeof(buf), "you id is %d\n", id); send(newfd, buf, strlen(buf) + 1, 0); printf("new connection : connfd is %d, id is %d\n", newfd, id); } else { ret = show_message(i); if(ret == 0) { close(i); FD_CLR(i, ¤t_fds); if(i == maxfd) maxfd--; printf("id : %d client quit!\n", del_peerlist(i)); } } } } } return 0; }
客戶端:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
int main(int argc, char *argv[])
{
int sockfd;
socklen_t len;
struct sockaddr_in dest;
struct sockaddr_in self;
char buffer[MAXBUF + 1];
fd_set rfds;
struct timeval tv;
int retval, maxfd = -1;
if (argc < 2) {
fprintf(stderr, "Usage : %s + server_port\n", argv[0]);
exit(0);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "Socket : %s\n", strerror(errno));
exit(errno);
}
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
if (0 > (dest.sin_port = htons(atoi(argv[1])))) {
fprintf(stderr, "port number is wrong\n");
exit(1);
}
dest.sin_addr.s_addr = inet_addr("192.168.1.50");
if (0 > connect(sockfd, (struct sockaddr *) &dest, sizeof(dest))) {
fprintf(stderr, "Connect : %s\n", strerror(errno));
exit(1);
}
printf("connect success!\n");
len = sizeof(self);
if(0 > getsockname(sockfd, (struct sockaddr *)&self, &len)) {
fprintf(stderr, "getsockname : %s\n", strerror(errno));
exit(1);
}
printf("Myself is %s:%d\n",inet_ntoa(self.sin_addr),ntohs(self.sin_port));
while (1) {
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(sockfd, &rfds);
if (sockfd > maxfd)
maxfd = sockfd;
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
fprintf(stderr, "select : %s\n", strerror(errno));
break;
} else if (retval == 0) {
continue;
} else {
if (FD_ISSET(sockfd, &rfds)) {
bzero(buffer, MAXBUF + 1);
len = recv(sockfd, buffer, MAXBUF, 0);
if (len > 0)
printf("message received from server : %s",buffer);
else{
if (len < 0)
fprintf(stderr, "recv : %s\n", strerror(errno));
else
printf("server close!\n");
break;
}
}
else if(FD_ISSET(0, &rfds)) {
bzero(buffer, MAXBUF + 1);
fgets(buffer, MAXBUF, stdin);
if (!strncasecmp(buffer, "quit", 4)) {
printf("client close!\n");
break;
}
if ( 0 > send(sockfd, buffer, strlen(buffer)+1, 0)) {
fprintf(stderr, "send : %s\n", strerror(errno));
//break;
}
}
}
}
close(sockfd);
return 0;
}