Linux套接字聊天
生活随笔
收集整理的這篇文章主要介紹了
Linux套接字聊天
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Linux套接字
套接字是一種通信機制,憑借這種機制,客戶/服務器系統(tǒng)的開發(fā)工作既可以在本地單機上進行,也可以跨網(wǎng)絡(luò)進行。套接字的特性有三個屬性確定,它們是:域(domain),類型(type),和協(xié)議(protocol)。套接字還用地址作為它的名字。地址的格式隨域(又被稱為協(xié)議族,protocol family)的不同而不同。每個協(xié)議族又可以使用一個或多個地址族定義地址格式。
1.套接字的域
域指定套接字通信中使用的網(wǎng)絡(luò)介質(zhì)。最常見的套接字域是AF_INET,它是指Internet網(wǎng)絡(luò),許多Linux局域網(wǎng)使用的都是該網(wǎng)絡(luò),當然,因特網(wǎng)自身用的也是它。其底層的協(xié)議——網(wǎng)際協(xié)議(IP)只有一個地址族,它使用一種特定的方式來指定網(wǎng)絡(luò)中的計算機,即IP地址。
在計算機系統(tǒng)內(nèi)部,端口通過分配一個唯一的16位的整數(shù)來表示,在系統(tǒng)外部,則需要通過IP地址和端口號的組合來確定。
2.套接字類型
流套接字(在某些方面類似域標準的輸入/輸出流)提供的是一個有序,可靠,雙向字節(jié)流的連接。
流套接字由類型SOCK_STREAM指定,它們是在AF_INET域中通過TCP/IP連接實現(xiàn)的。他們也是AF_UNIX域中常見的套接字類型。
數(shù)據(jù)包套接字
與流套接字相反,由類型SOCK_DGRAM指定的數(shù)據(jù)包套接字不建立和維持一個連接。它對可以發(fā)送的數(shù)據(jù)包的長度有限制。數(shù)據(jù)報作為一個單獨的網(wǎng)絡(luò)消息被傳輸,它可能會丟失,復制或亂序到達。
數(shù)據(jù)報套接字實在AF_INET域中通過UDP/IP連接實現(xiàn),它提供的是一種無需的不可靠服務。
3.套接字協(xié)議
只要底層的傳輸機制允許不止一個協(xié)議來提供要求的套接字類型,我們就可以為套接字選擇一個特定的協(xié)議。
創(chuàng)建套接字
socket系統(tǒng)調(diào)用創(chuàng)建一個套接字并返回一個描述符,該描述符可以用來訪問該套接字。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain , int type , int protocol);
創(chuàng)建的套接字是一條通信線路的一個端點。domain參數(shù)指定協(xié)議族,type參數(shù)指定這個套接字的通信類型,protocol參數(shù)指定使用的協(xié)議。
domain參數(shù)可以指定的協(xié)議族如下
域 說明
AF_UNIX ?UNIX域協(xié)議(文件系統(tǒng)套接字)
AF_INET ?ARPA因特網(wǎng)協(xié)議(UNIX網(wǎng)絡(luò)套接字)
AF_ISO ?ISO標準協(xié)議
AF_NS ?施樂(XEROX)網(wǎng)絡(luò)系統(tǒng)協(xié)議
AF_IPX ?NOVELL IPX協(xié)議
AF_APPLETALK
Appletalk DDS
最常見的套接字域是AF_UNIX和AF_INET,前者用于通過Unix和Linux文件系統(tǒng)實現(xiàn)的本地套接字,后者用于Unix網(wǎng)絡(luò)套接字。AF_INET套接字可以用于通過包括因特網(wǎng)在內(nèi)的TCP/IP網(wǎng)絡(luò)進行通信的程序。微軟Windows系統(tǒng)的winsock接口也提供了對這個套接字域的訪問功能。
socket函數(shù)的參數(shù)type指定用于新套接字的通信特性。它的取值包括SOCK_STREAM和SOCK_DGRAM。
SOCK_STREAM是一個有序、可靠、面向連接的雙字節(jié)流。通過TCP連接來實現(xiàn)。
SOCK_DGRAM是數(shù)據(jù)包服務,我們可以用它來發(fā)送最大長度固定的消息。但消息是否會被正確傳遞或消息是否不會亂序到達沒有保證。
套接字地址結(jié)構(gòu)
結(jié)構(gòu)struct sockaddr_un 定義了一種通用的套接字地址,它的類型是:
struct sockaddr_un
{
? ? sa_family_t sun_family; ? ? ? /*AF_UNIX*/
? ? char ? ? ? ? ? ? ?sun_path; ? ? ? ? /*pathname*/
};
這是一種通用的定義,一般都不用。TCP/IP使用的是自己的結(jié)構(gòu)體struct sockaddr_in,格式如下:
struct sockaddr_in
{
? ? short int sin_family; ? ? ?//地址類型,一般為AF_INET
? ? unsigned short int sin_port; ? ? ? ?//端口號
? ? struct in_addr sin_addr; ? ? ? ?//IP地址
};
這里的struct in_addr的定義如下:
struct in_addr
{
? ? unsigned long int ?s_addr;
};
結(jié)構(gòu)體sockaddr和sockaddr_in的長度都是16字節(jié)。一般在編TCP/IP程序時,一般使用結(jié)構(gòu)體sockaddr_in來設(shè)置地址,然后在需要的時候,通過強制類型轉(zhuǎn)換成sockaddr類型。
建立連接
函數(shù)connect用來在一個指定的套接字上創(chuàng)建一個連接,函數(shù)原型:
[cpp] view plain copy print?
int connect(int socket, const struct sockaddr *address, size_t address_len); ?
參數(shù)sockfd是一個由函數(shù)socket創(chuàng)建的套接字;
參數(shù)address是一個地址結(jié)構(gòu),需要連接的地址;
參數(shù)address_len為參數(shù)addr_addr的長度。
函數(shù)執(zhí)行成功返回0,有錯誤發(fā)生則返回-1。
如果套接字類型是TCP,則該函數(shù)用于向服務器發(fā)出連接請求,服務器的IP地址和端口號由參數(shù)serv_addr指定;如果套接字類型是UDP,則該函數(shù)并不建立真正的連接,它只是告訴內(nèi)核與該套接字進行通信的目的地址(由第二個參數(shù)指定),只有該目的地址發(fā)來的數(shù)據(jù)才會被該socket接收。
通常一個面向連接的套接字只能調(diào)用一次connect函數(shù);而對于無連接的套接字則可以多次調(diào)用connect函數(shù)以改變與目的地址的綁定。
在套接字上監(jiān)聽
函數(shù)listen把套接字轉(zhuǎn)化為被動監(jiān)聽,函數(shù)原型:
int listen(int s, int backlog);
參數(shù)s指定了一個套接字;
參數(shù)backlog指定了該連接隊列的最大長度,如果已達到最大,則之后的連接請求將被服務器拒絕。
函數(shù)執(zhí)行成功趕回0,有錯誤發(fā)生則返回-1。
由函數(shù)socket創(chuàng)建的套接字是主動套接字,這種套接字可以用來主動請求連接到某個服務器上。(通過connect()函數(shù))。
作為服務器端的程序,通常在某個端口上監(jiān)聽等待來自客戶端的連接請求。在服務器端,一般是先調(diào)用函數(shù)socket創(chuàng)建一個主動套接字,然后調(diào)用函數(shù)bind將該套接字綁定到某個端口上,接著再調(diào)用函數(shù)listen將該套接字轉(zhuǎn)化為監(jiān)聽套接字,等待來自于客戶端的連接請求。
函數(shù)listen只是將套接字設(shè)置為傾聽模式以等待連接請求,它并不能接收連接請求,真正的接收客戶端連接請求的是accept()函數(shù)。
接收連接
函數(shù)accept用來接收一個連接請求,函數(shù)原型:
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
參數(shù)s是由socket創(chuàng)建,經(jīng)函數(shù)bind綁定到本地某一端口上,然后通過函數(shù)listen轉(zhuǎn)化而來的監(jiān)聽套接字;
參數(shù)addr用來保存發(fā)起連接請求的主機的地址和端口;
參數(shù)addrlen是addr所指向的結(jié)構(gòu)體的大小。
函數(shù)執(zhí)行成功返回一個新的代表客戶端的套接字,出錯則返回-1。
只能對面向連接的套接字使用accept函數(shù)。accept執(zhí)行成功時,將創(chuàng)建一個新的套接字,并且這個新的套接字分配一個套接字描述符,并返回這個新的套接字描述符。這個新的套接字描述符與打開文件返回的文件描述符類似,進程可以利用這個新的套接字描述符與客戶端交換數(shù)據(jù),參數(shù)s所指定的套接字繼續(xù)等待客戶端的連接請求。
[cpp] view plain copy print?
/* ?Make the necessary includes and set up the variables. ?*/ ?
??
#include <sys/types.h> ?
#include <sys/socket.h> ?
#include <stdio.h> ?
#include <sys/un.h> ?
#include <unistd.h> ?
#include <stdlib.h> ?
??
int main() ?
{ ?
? ? int sockfd; ?
? ? int len; ?
? ? struct sockaddr_un address; ?
? ? int result; ?
? ? char ch = 'A'; ?
??
/* ?Create a socket for the client. ?*/ ?
??
? ? sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ?
??
/* ?Name the socket, as agreed with the server. ?*/ ?
??
? ? address.sun_family = AF_UNIX; ?
? ? strcpy(address.sun_path, "server_socket"); ?
? ? len = sizeof(address); ?
??
/* ?Now connect our socket to the server's socket. ?*/ ?
??
? ? result = connect(sockfd, (struct sockaddr *)&address, len); ?
??
? ? if(result == -1) { ?
? ? ? ? perror("oops: client1"); ?
? ? ? ? exit(1); ?
? ? } ?
??
/* ?We can now read/write via sockfd. ?*/ ?
??
? ? write(sockfd, &ch, 1); ?
? ? read(sockfd, &ch, 1); ?
? ? printf("char from server = %c\n", ch); ?
? ? close(sockfd); ?
? ? exit(0); ?
} ?
[cpp] view plain copy print?
/* ?Make the necessary includes and set up the variables. ?*/ ?
??
#include <sys/types.h> ?
#include <sys/socket.h> ?
#include <stdio.h> ?
#include <sys/un.h> ?
#include <unistd.h> ?
#include <stdlib.h> ?
??
int main() ?
{ ?
? ? int server_sockfd, client_sockfd; ?
? ? int server_len, client_len; ?
? ? struct sockaddr_un server_address; ?
? ? struct sockaddr_un client_address; ?
??
/* ?Remove any old socket and create an unnamed socket for the server. ?*/ ?
??
? ? unlink("server_socket"); ?
? ? server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ?
??
/* ?Name the socket. ?*/ ?
??
? ? server_address.sun_family = AF_UNIX; ?
? ? strcpy(server_address.sun_path, "server_socket"); ?
? ? server_len = sizeof(server_address); ?
? ? bind(server_sockfd, (struct sockaddr *)&server_address, server_len); ?
??
/* ?Create a connection queue and wait for clients. ?*/ ?
??
? ? listen(server_sockfd, 5); ?
? ? while(1) { ?
? ? ? ? char ch; ?
??
? ? ? ? printf("server waiting\n"); ?
??
/* ?Accept a connection. ?*/ ?
??
? ? ? ? client_len = sizeof(client_address); ?
? ? ? ? client_sockfd = accept(server_sockfd, ??
? ? ? ? ? ? (struct sockaddr *)&client_address, &client_len); ?
??
/* ?We can now read/write to client on client_sockfd. ?*/ ?
??
? ? ? ? read(client_sockfd, &ch, 1); ?
? ? ? ? ch++; ?
? ? ? ? write(client_sockfd, &ch, 1); ?
? ? ? ? close(client_sockfd); ?
? ? } ?
}?
========
Linux 下基于socket的簡單網(wǎng)絡(luò)聊天室(服務器與客戶端)
本程序分為服務端與客戶端,服務器建立一個共享內(nèi)存區(qū),用于存貯各個客戶端發(fā)送過來的消息,服務器接收一個客戶端登陸后,即開啟一個子進程,原父進程返回等待新客戶的登陸,子進程用于接收客戶的消息,并把共享內(nèi)存里面的全部的內(nèi)容發(fā)送給客戶端。為了便于處理數(shù)據(jù)的方便,在處理客戶消息的子進程中再創(chuàng)建一個子進程,一個用于接收子進程消息,存于共享內(nèi)存區(qū);另一個子進程用于發(fā)送共享內(nèi)存給客戶端。?
客戶端接收到共享內(nèi)存的內(nèi)容后,根據(jù)實際情況顯示共享內(nèi)存里面的內(nèi)容。后登陸的用戶可以看到前面用戶的聊天內(nèi)容。
本設(shè)計鍛煉了linux中socket的基本應用,服務端建立套接字的大致步驟:
1,建立socket。
2,bind 綁定特定的端口。
3,listen 監(jiān)聽特定的端口。
4,accept,當有客戶端連接服務器端口時,accept接收信息,并返回新的套接字描述符,提供給操作
5,根據(jù)實際需求,write,read,send,recv等操作
6,關(guān)閉套接字。
客戶端大致步驟:
1,創(chuàng)建socket.
2,根據(jù)服務器地址,connect連接到特定服務器。
3,write,read等讀寫操作。
4,關(guān)閉套接字。
?
在程序調(diào)試過程中,要特別注意清空常用的載體。留意sizeof和strlen的區(qū)別。
留意各進程之間的關(guān)系的梳理,各循環(huán)的起始條件,運行情況。
?
?
服務器:server.c
#include <sys/types.h>
#include <sys/socket.h> ? ? ? ? ? ? ? ? ? ? ? ? // 包含套接字函數(shù)庫
#include <stdio.h>
#include <netinet/in.h> ? ? ? ? ? ? ? ? ? ? ? ? // 包含AF_INET相關(guān)結(jié)構(gòu)
#include <arpa/inet.h> ? ? ? ? ? ? ? ? ? ? ?// 包含AF_INET相關(guān)操作的函數(shù)
#include <unistd.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/shm.h>
#define PORT ? ?8888;
#define MYKEY ? 12345
#define SIZE ? ?10240
int main()
{ ?
? ? int shmid;
? ? char *shmaddr; ? ? ? ? ? ? ? ? ? ? ? //定義子進程共用的共享內(nèi)存?
? ? shmid = shmget(MYKEY, SIZE, IPC_CREAT | 0600); ?
? ? shmaddr= (char *) shmat(shmid, 0, 0);
? ?
? ? if(shmid==-1)
? ? {
? ? ? ? printf("shmid error\n");
? ? }
? ? memset(shmaddr,0,SIZE);
? ?
? ? int i=0;
? ? char buf[100];
? ? memset(buf,0,100);
? ?
? ?
? ? int server_sockfd,client_sockfd; ??
? ? int server_len,client_len;
? ?
? ? struct sockaddr_in server_sockaddr,client_sockaddr;
? ?
? ?
? ? server_sockfd = socket(AF_INET,SOCK_STREAM, 0); // 定義套接字類型
? ?
? ? server_sockaddr.sin_family=AF_INET;
? ? server_sockaddr.sin_port=PORT;
? ? server_sockaddr.sin_addr.s_addr=INADDR_ANY;
? ?
? ? server_len=sizeof(server_sockaddr);
? ?
? ? //允許重復使用本地地址和套接字綁定
? ? int j=1;
? ? setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&j,sizeof(j));
? ?
? ? //綁定端口
? ? if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,server_len)==-1)
? ? {
? ? ? ? perror("bind:");
? ? ? ? exit(1); ? ? ??
? ? }
? ?
? ?
? ? if(listen(server_sockfd,5)==-1)
? ? {
? ? ? ? perror("listen:");
? ? ? ? exit(1); ??
? ? }
? ? printf("Listening...\n");
? ?
? ? client_len=sizeof(client_sockaddr);
? ?
? ? pid_t ppid,pid;
??
? ?while(1)
? ?{ ?
? ? if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_sockaddr,&client_len))==-1)
? ? ? ? {
? ? ? ? ? ? ?perror("accept error:");
? ? ? ? ? ? ?exit(1);
? ? ? ? }
? ?
? ? printf("%s登錄服務器\n",inet_ntoa(client_sockaddr.sin_addr));
? ?
? ? ppid=fork();
? ?
? ? if(ppid==-1)
? ? {
? ? ? ? printf("fork 1 failed:");
? ? }
? ?
? ? if(ppid==0) ? ? ? ? ? ? ? ? ?//子進程用于接收客戶端信息并發(fā)送
? ? {
? ? ? ? pid=fork();
? ? ? ? if(pid==-1)
? ? ? ? {
? ? ? ? ? ? printf("fork 2 failed:");
? ? ? ? ? ? exit(1);
? ? ? ? }
? ??
? ? ?int recvbytes; ?
? ? ? ? ?
? ? ? ? if(pid==0) ? ? ? ? ? ? ?//子子進程用于接收消息
? ? ? ? { ?
? ? ? ? ? ? while(1)
? ? ? ? ? ? {
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?if((recvbytes=recv(client_sockfd,buf,100,0))==-1)
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? perror("read client_sockfd failed:");
? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ?// printf("recvbytes=%d\n",recvbytes);
? ? ? ? ? ? ? ? usleep(10000);
? ? ? ? ? ? ? ?printf("client send buf=%s\n",buf);
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? for(i=0;i<1000;i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if(*(shmaddr+100*i)==0)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? strcpy(shmaddr+100*i,buf); ?
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?
? ? ? ? ? ? }
? ? ? ? }
? ? ? ?
? ? ? ? if(pid>0) ? ? ? ? ? ? ? //子進程用于發(fā)送消息?
? ? ? ? {
? ? ? ? ? ?while(1)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if(*(shmaddr+i*100)!=0)
? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ?// strcpy(&buf,shmaddr+100*i);
? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? // ?buf++;
? ? ? ? ? ? ? ? ? ? write(client_sockfd,shmaddr,SIZE);
? ? ? ? ? ? ? ? ? ?// send(client_sockfd,buf,strlen(buf),0);
? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ?// ? printf("the server is send buf=%c",buf);
? ? ? ? ? ? ? ? ? // ?printf("send client :%s\n",(shmaddr+i*100)) ;
? ? ? ? ? ? ? ? ? ? i++;
? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ?
? ? ? ? } ? ?
? ? ??
? ? }
? ?
? ?
? ? if(ppid>0) ? ? ? ? ? ? ?//總父進程返回等待接收消息
? ? {
? ? ? ? close(client_sockfd);
? ? }
? ? ? ?
? ??
? ? }
}
?
客戶端:client.c
#include <sys/types.h>
#include <sys/socket.h> ? ? ? ? ? ? ? ? ? ? ? ? // 包含套接字函數(shù)庫
#include <stdio.h>
#include <netinet/in.h> ? ? ? ? ? ? ? ? ? ? ? ? // 包含AF_INET相關(guān)結(jié)構(gòu)
#include <arpa/inet.h> ? ? ? ? ? ? ? ? ? ? ? ? ?// 包含AF_INET相關(guān)操作的函數(shù)
#include <unistd.h>
#include<string.h>
#include<time.h>
#define ?PORT 8888
#define ?IP_ADDR "192.168.110.185"
#define SIZE 10240
int main()
{ ?
? ? ?
? ? struct tm *timeptr;
? ? time_t timeval;
? ? char tm[50];
? ? //(void)time(&timeval);
? ?
? ? //printf("the date is %s\n",ctime(&timeval));
? ?// printf("The time is %s\n",tm);
? ? int sockfd; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 用于保存客戶套接字標識符
? ? int len; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 用于客戶消息長度
? ? struct sockaddr_in address; ? ? ? ? ? ? ? ? // 定義客戶套接字地址
? ? int result;
? ?
? ? sockfd = socket(AF_INET,SOCK_STREAM, 0); ? ?// 定義套接字類型
? ? address.sin_family = AF_INET; ? ? ? ? ? ? ? // 定義套接字地址中的域
? ?
? ? address.sin_addr.s_addr = inet_addr(IP_ADDR); ? ? ? ? ? ? ? ? ? ? ? ? ? // 定義套接字地址
? ?
? ? address.sin_port = htons(PORT); ? ? ? ? ? ? ? ? // 定義套接字端口
? ? char buf[100]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 定義要傳送的消息
? ? memset(buf,0,100);
? ? char str[90]; ? ? ? ? ? ? ? ? ? ? ? //存貯輸入的語句
? ?
? ? char shmaddr[SIZE]; ? ? ? ? ? ? ? ? ? ?//接受服務器發(fā)送的全部聊天數(shù)據(jù)?
? ? int i=0;
? ?
? ? char myname[100];
? ? char say[10]={"說:"};
? ? printf("歡迎來到聊天室,請輸入你的姓名:\n");
? ? scanf("%s",myname);
? ?
? ?
??
? ?len = sizeof(address);
? ?result = connect(sockfd, (struct sockaddr *) &address, len); // 請求連接
? ?if (result == -1) ?
? ? {
? ? ? perror("Connect failed");
? ? ? return 1;
? ? }
? ? printf("%s成功登錄服務器:\n",myname);
? ?
? ? pid_t pid;
? ?
? ? pid=fork();
? ? if(pid==-1)
? ? {
? ? ? ? printf("fork failed");
? ? }
? ?
? ? int sendbytes=0;
?
? ? if(pid==0) ? ? ? ? ? ? ?//子進程用于發(fā)送數(shù)據(jù)
? ? {
? ? ? ?while(1)
? ? ? ? {
? ? ? ? ? ? ? ? printf("請輸入語句:\n");
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? scanf("%s",str);
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? (void)time(&timeval);
? ? ? ? ? ? ? ? strcpy(tm,ctime(&timeval));
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? strcpy(buf,myname); ? ? ? ? ? ? //姓名傳入buf中
? ? ? ? ? ? ? ? strcat(buf,tm); ? ? ? ? ? ? ? ? //時間傳入buf中
? ? ? ? ? ? ? ? strcat(buf,say); ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? strcat(buf,str); ? ? ? ? ? ? ? ?//語句傳入bufz中
? ? ? ? ? ? ??
? ? ? ? ? ? ? ? //read(0,buf,strlen(buf));
? ? ? ? ? ? ??
? ? ? ? ? ? ? ? // send(sockfd,buf,strlen(buf),0);
? ? ? ? ? ? ? ? // getchar();
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ?if((sendbytes=write(sockfd, buf, 100))==-1)
? ? ? ? ? ? ? ? ?{
? ? ? ? ? ? ? ? ? ? perror("send to server failed:");
? ? ? ? ? ? ? ? ?} ?// 向服務器傳送消息
? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? // printf("sendbytes=%d\n",sendbytes);
? ? ? ? ? ? ? ?// ?printf("buf=%s\n",buf);
? ? ? ? ? ? ? ?// ? printf("input buf=%s\n",buf); ??
? ? ? ? ? ? ? ? ?usleep(1000);
? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? memset(buf,0,100);
? ? ? ? ? ? ? ? memset(tm,0,50);
? ? ? ? } ? ? ?
? ? }
? ?
? ? if(pid>0) ? ? ? ? ? ? ? //父進程用于接受消息并讀取
? ? {
? ? ? ? while(1)
? ? ? ? {
? ? ? ? ? ? read(sockfd,shmaddr,SIZE);
? ? ? ? ? // printf("server send shmaddr=%s\n",shmaddr);
? ? ? ? ? ?
? ? ? ? ? ? if(*(shmaddr+i*100)!=0)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? printf("%s\n",(shmaddr+i*100)) ;
? ? ? ? ? ? ? ? i++;
? ? ? ? ? ??
? ? ? ? ? ? }
? ? ? ? ? ?
? ? ? ? ? ? usleep(1000);
? ? ? ? }
? ?
? ? ? ?
? ? }
? ?
??
? ? close(sockfd);
? ? return 0;
}
========
linux網(wǎng)絡(luò)編程:用C語言實現(xiàn)的聊天程序(異步通信)
本片文章,在上一篇:linux網(wǎng)絡(luò)編程:用C語言實現(xiàn)的聊天程序(同步通信) 的基礎(chǔ)上,增加了IO復用的功能,實現(xiàn)了聊天程序的異步通訊!
1、使用IO復用可以在等待的時候加入了超時的時間,如果等待的時間沒有達到超時時間,那么該情況與阻塞的情況一致。而當超時的時間到達時,仍沒有數(shù)據(jù)接收到,系統(tǒng)回返回0,不再等待。select函數(shù)就實現(xiàn)了這個功能。
2、select函數(shù)原型
int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval * timeout);
a)返回值
>0:就緒的描述符
-1:出錯
0 :超時
struct timeval{
long tv_sec; // seconds
long tv_usec; // microseconds
}
b)具體解釋select的參數(shù)
int maxfdp是一個整數(shù)值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1;
fd_set*readfds 文件描述符集合內(nèi),是否有數(shù)據(jù)可讀;
fd_set*writefds 文件描述符集合內(nèi),是否有數(shù)據(jù)可寫;
fd_set *errorfds 文件描述符集合內(nèi),是否有文件發(fā)生錯誤;
struct timeval *timeout是select的超時時間,它可以使select處于三種狀態(tài),第一,若將NULL以形參傳入,就是將select置于阻塞狀態(tài);第二,若將時間值設(shè)為0秒0毫秒,就變成一個純粹的非阻塞函數(shù),不管文件描述符是否有變化,都立刻返回繼續(xù)執(zhí)行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大于0,這就是等待的超時時間,即select在timeout時間內(nèi)阻塞。
c)4個宏可以操作文件描述符的集合
void FD_ZERO (fd_set *fdset); // 初始化文件描述集合
void FD_SET (int fd, fd_set *fdset); // 將描述符加入到集合中
void FD_CLR (int fd, fd_set *fdset); // 將描述符中集合中刪除
int FD_ISSET (int fd, fd_set *fdset); // 檢查描述符集合中指定的文件描述符是否可讀寫
3、客戶端源代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#define BUFLEN 1024
int main(int argc, char **argv)
{
? ? int sockfd;
? ? struct sockaddr_in s_addr;
? ? socklen_t len;
? ? unsigned int port;
? ? char buf[BUFLEN];
? ? fd_set rfds;
? ? struct timeval tv;
? ? int retval, maxfd; ? ?
? ??
? ? /*建立socket*/
? ? if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
? ? ? ? perror("socket");
? ? ? ? exit(errno);
? ? }else
? ? ? ? printf("socket create success!\n");
? ? /*設(shè)置服務器端口*/ ? ?
? ? if(argv[2])
? ? ? ? port = atoi(argv[2]);
? ? else
? ? ? ? port = 4567;
? ? /*設(shè)置服務器ip*/
? ? bzero(&s_addr, sizeof(s_addr));
? ? s_addr.sin_family = AF_INET;
? ? s_addr.sin_port = htons(port);
? ? if (inet_aton(argv[1], (struct in_addr *)&s_addr.sin_addr.s_addr) == 0) {
? ? ? ? perror(argv[1]);
? ? ? ? exit(errno);
? ? }
? ? /*開始連接服務器*/ ? ?
? ? if(connect(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr)) == -1){
? ? ? ? perror("connect");
? ? ? ? exit(errno);
? ? }else
? ? ? ? printf("conncet success!\n");
? ??
? ? while(1){
? ? ? ? /*把可讀文件描述符的集合清空*/
? ? ? ? FD_ZERO(&rfds);
? ? ? ? /*把標準輸入的文件描述符加入到集合中*/
? ? ? ? FD_SET(0, &rfds);
? ? ? ? maxfd = 0;
? ? ? ? /*把當前連接的文件描述符加入到集合中*/
? ? ? ? FD_SET(sockfd, &rfds);
? ? ? ? /*找出文件描述符集合中最大的文件描述符*/ ? ?
? ? ? ? if(maxfd < sockfd)
? ? ? ? ? ? maxfd = sockfd;
? ? ? ? /*設(shè)置超時時間*/
? ? ? ? tv.tv_sec = 5;
? ? ? ? tv.tv_usec = 0;
? ? ? ? /*等待聊天*/
? ? ? ? retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
? ? ? ? if(retval == -1){
? ? ? ? ? ? printf("select出錯,客戶端程序退出\n");
? ? ? ? ? ? break;
? ? ? ? }else if(retval == 0){
? ? ? ? ? ? printf("客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...\n");
? ? ? ? ? ? continue;
? ? ? ? }else{
? ? ? ? ? ? /*服務器發(fā)來了消息*/
? ? ? ? ? ? if(FD_ISSET(sockfd,&rfds)){
? ? ? ? ? ? ? ? /******接收消息*******/
? ? ? ? ? ? ? ? bzero(buf,BUFLEN);
? ? ? ? ? ? ? ? len = recv(sockfd,buf,BUFLEN,0);
? ? ? ? ? ? ? ? if(len > 0)
? ? ? ? ? ? ? ? ? ? printf("服務器發(fā)來的消息是:%s,共有字節(jié)數(shù)是: %d\n",buf,len);
? ? ? ? ? ? ? ? else{
? ? ? ? ? ? ? ? ? ? if(len < 0 )
? ? ? ? ? ? ? ? ? ? ? ? printf("接受消息失敗!\n");
? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? printf("服務器退出了,聊天終止!\n");
? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? /*用戶輸入信息了,開始處理信息并發(fā)送*/
? ? ? ? ? ? if(FD_ISSET(0, &rfds)){
? ? ? ? ? ? _retry: ? ?
? ? ? ? ? ? ? ? /******發(fā)送消息*******/ ? ?
? ? ? ? ? ? ? ? bzero(buf,BUFLEN);
? ? ? ? ? ? ? ? /*fgets函數(shù):從流中讀取BUFLEN-1個字符*/
? ? ? ? ? ? ? ? fgets(buf,BUFLEN,stdin);
? ? ? ? ? ? ? ? /*打印發(fā)送的消息*/
? ? ? ? ? ? ? ? //fputs(buf,stdout);
? ? ? ? ? ? ? ? if(!strncasecmp(buf,"quit",4)){
? ? ? ? ? ? ? ? ? ? printf("client 請求終止聊天!\n");
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? /*如果輸入的字符串只有"\n",即回車,那么請重新輸入*/
? ? ? ? ? ? ? ? if(!strncmp(buf,"\n",1)){
? ? ? ? ? ? ? ? ? ? printf("輸入的字符只有回車,這個是不正確的!!!\n");
? ? ? ? ? ? ? ? ? ? goto _retry;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? /*如果buf中含有'\n',那么要用strlen(buf)-1,去掉'\n'*/ ? ?
? ? ? ? ? ? ? ? if(strchr(buf,'\n'))
? ? ? ? ? ? ? ? ? ? len = send(sockfd,buf,strlen(buf)-1,0);
? ? ? ? ? ? ? ? /*如果buf中沒有'\n',則用buf的真正長度strlen(buf)*/ ? ?
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? len = send(sockfd,buf,strlen(buf),0);
? ? ? ? ? ? ? ? if(len > 0)
? ? ? ? ? ? ? ? ? ? printf("\t消息發(fā)送成功,本次共發(fā)送的字節(jié)數(shù)是:%d\n",len); ? ? ? ? ? ?
? ? ? ? ? ? ? ? else{
? ? ? ? ? ? ? ? ? ? printf("消息發(fā)送失敗!\n");
? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ?
? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? }
? ? ? ? }
? ??
? ? }
? ? /*關(guān)閉連接*/
? ? close(sockfd);
? ? return 0;
}
4、服務器源代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#define BUFLEN 1024
int main(int argc, char **argv)
{
? ? int sockfd, newfd;
? ? struct sockaddr_in s_addr, c_addr;
? ? char buf[BUFLEN];
? ? socklen_t len;
? ? unsigned int port, listnum;
? ? fd_set rfds;
? ? struct timeval tv; ? ?
? ? int retval,maxfd;
? ??
? ? /*建立socket*/
? ? if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
? ? ? ? perror("socket");
? ? ? ? exit(errno);
? ? }else
? ? ? ? printf("socket create success!\n");
? ? /*設(shè)置服務器端口*/ ? ?
? ? if(argv[2])
? ? ? ? port = atoi(argv[2]);
? ? else
? ? ? ? port = 4567;
? ? /*設(shè)置偵聽隊列長度*/
? ? if(argv[3])
? ? ? ? listnum = atoi(argv[3]);
? ? else
? ? ? ? listnum = 3;
? ? /*設(shè)置服務器ip*/
? ? bzero(&s_addr, sizeof(s_addr));
? ? s_addr.sin_family = AF_INET;
? ? s_addr.sin_port = htons(port);
? ? if(argv[1])
? ? ? ? s_addr.sin_addr.s_addr = inet_addr(argv[1]);
? ? else
? ? ? ? s_addr.sin_addr.s_addr = INADDR_ANY;
? ? /*把地址和端口幫定到套接字上*/
? ? if((bind(sockfd, (struct sockaddr*) &s_addr,sizeof(struct sockaddr))) == -1){
? ? ? ? perror("bind");
? ? ? ? exit(errno);
? ? }else
? ? ? ? printf("bind success!\n");
? ? /*偵聽本地端口*/
? ? if(listen(sockfd,listnum) == -1){
? ? ? ? perror("listen");
? ? ? ? exit(errno); ? ?
? ? }else
? ? ? ? printf("the server is listening!\n");
? ? while(1){
? ? ? ? printf("*****************聊天開始***************\n");
? ? ? ? len = sizeof(struct sockaddr);
? ? ? ? if((newfd = accept(sockfd,(struct sockaddr*) &c_addr, &len)) == -1){
? ? ? ? ? ? perror("accept"); ? ? ? ?
? ? ? ? ? ? exit(errno);
? ? ? ? }else
? ? ? ? ? ? printf("正在與您聊天的客戶端是:%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
? ? ? ? while(1){
? ? ? ? ? ? /*把可讀文件描述符的集合清空*/
? ? ? ? ? ? FD_ZERO(&rfds);
? ? ? ? ? ? /*把標準輸入的文件描述符加入到集合中*/
? ? ? ? ? ? FD_SET(0, &rfds);
? ? ? ? ? ? maxfd = 0;
? ? ? ? ? ? /*把當前連接的文件描述符加入到集合中*/
? ? ? ? ? ? FD_SET(newfd, &rfds);
? ? ? ? ? ? /*找出文件描述符集合中最大的文件描述符*/ ? ?
? ? ? ? ? ? if(maxfd < newfd)
? ? ? ? ? ? ? ? maxfd = newfd;
? ? ? ? ? ? /*設(shè)置超時時間*/
? ? ? ? ? ? tv.tv_sec = 5;
? ? ? ? ? ? tv.tv_usec = 0;
? ? ? ? ? ? /*等待聊天*/
? ? ? ? ? ? retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
? ? ? ? ? ? if(retval == -1){
? ? ? ? ? ? ? ? printf("select出錯,與該客戶端連接的程序?qū)⑼顺鯸n");
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }else if(retval == 0){
? ? ? ? ? ? ? ? printf("服務器沒有任何輸入信息,并且客戶端也沒有信息到來,waiting...\n");
? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? /*用戶輸入信息了,開始處理信息并發(fā)送*/
? ? ? ? ? ? ? ? if(FD_ISSET(0, &rfds)){
? ? ? ? ? ? ? ? _retry:
? ? ? ? ? ? ? ? ? ? /******發(fā)送消息*******/
? ? ? ? ? ? ? ? ? ? bzero(buf,BUFLEN);
? ? ? ? ? ? ? ? ? ? /*fgets函數(shù):從流中讀取BUFLEN-1個字符*/
? ? ? ? ? ? ? ? ? ? fgets(buf,BUFLEN,stdin);
? ? ? ? ? ? ? ? ? ? /*打印發(fā)送的消息*/
? ? ? ? ? ? ? ? ? ? //fputs(buf,stdout);
? ? ? ? ? ? ? ? ? ? if(!strncasecmp(buf,"quit",4)){
? ? ? ? ? ? ? ? ? ? ? ? printf("server 請求終止聊天!\n");
? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? /*如果輸入的字符串只有"\n",即回車,那么請重新輸入*/
? ? ? ? ? ? ? ? ? ? if(!strncmp(buf,"\n",1)){
? ? ? ? ? ? ? ? ? ? ? ? printf("輸入的字符只有回車,這個是不正確的!!!\n");
? ? ? ? ? ? ? ? ? ? ? ? goto _retry;
? ? ? ? ? ? ? ? ? ? } ? ?
? ? ? ? ? ? ? ? ? ? /*如果buf中含有'\n',那么要用strlen(buf)-1,去掉'\n'*/ ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? if(strchr(buf,'\n'))
? ? ? ? ? ? ? ? ? ? ? ? len = send(newfd,buf,strlen(buf)-1,0);
? ? ? ? ? ? ? ? ? ? /*如果buf中沒有'\n',則用buf的真正長度strlen(buf)*/ ? ?
? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? len = send(newfd,buf,strlen(buf),0);
? ? ? ? ? ? ? ? ? ? if(len > 0)
? ? ? ? ? ? ? ? ? ? ? ? printf("\t消息發(fā)送成功,本次共發(fā)送的字節(jié)數(shù)是:%d\n",len); ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? else{
? ? ? ? ? ? ? ? ? ? ? ? printf("消息發(fā)送失敗!\n");
? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? /*客戶端發(fā)來了消息*/
? ? ? ? ? ? ? ? if(FD_ISSET(newfd, &rfds)){
? ? ? ? ? ? ? ? ? ? /******接收消息*******/
? ? ? ? ? ? ? ? ? ? bzero(buf,BUFLEN);
? ? ? ? ? ? ? ? ? ? len = recv(newfd,buf,BUFLEN,0);
? ? ? ? ? ? ? ? ? ? if(len > 0)
? ? ? ? ? ? ? ? ? ? ? ? printf("客戶端發(fā)來的信息是:%s,共有字節(jié)數(shù)是: %d\n",buf,len);
? ? ? ? ? ? ? ? ? ? else{
? ? ? ? ? ? ? ? ? ? ? ? if(len < 0 )
? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("接受消息失敗!\n");
? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("客戶端退出了,聊天終止!\n");
? ? ? ? ? ? ? ? ? ? ? ? break; ? ?
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? /*關(guān)閉聊天的套接字*/
? ? ? ? close(newfd);
? ? ? ? /*是否退出服務器*/
? ? ? ? printf("服務器是否退出程序:y->是;n->否? ");
? ? ? ? bzero(buf, BUFLEN);
? ? ? ? fgets(buf,BUFLEN, stdin);
? ? ? ? if(!strncasecmp(buf,"y",1)){
? ? ? ? ? ? printf("server 退出!\n");
? ? ? ? ? ? break;
? ? ? ? }
? ? }
? ? /*關(guān)閉服務器的套接字*/
? ? close(sockfd);
? ? return 0;
}
5、編譯源代碼:
new@new-desktop:~/linux/c$ gcc -Wall async-server.c -o async-server
new@new-desktop:~/linux/c$ gcc -Wall async-client.c -o async-client
6、服務器運行:
new@new-desktop:~/linux/c$ ./async-server 127.0.0.1
socket create success!
bind success!
the server is listening!
*****************聊天開始***************
正在與您聊天的客戶端是:127.0.0.1: 37648
服務器沒有任何輸入信息,并且客戶端也沒有信息到來,waiting...
hello
消息發(fā)送成功,本次共發(fā)送的字節(jié)數(shù)是:5
服務器沒有任何輸入信息,并且客戶端也沒有信息到來,waiting...
客戶端發(fā)來的信息是:renwen,共有字節(jié)數(shù)是: 6
7、客戶端運行:
new@new-desktop:~/linux/c$ ./async-client 127.0.0.1?
socket create success!
conncet success!
客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...
服務器發(fā)來的消息是:hello,共有字節(jié)數(shù)是: 5
客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...
renwen
消息發(fā)送成功,本次共發(fā)送的字節(jié)數(shù)是:6
客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...
客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...
客戶端沒有任何輸入信息,并且服務器也沒有信息到來,waiting...
========
資源鏈接
http://www.codeceo.com/article/linux-chart.html基于Linux的多功能聊天室
總結(jié)
以上是生活随笔為你收集整理的Linux套接字聊天的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Entity Framework 代码模
- 下一篇: BIOS中断相关资料和应用