发掘考古应用之聊天室。
问题
实现一个命令行的 ChatRoom
分为:Server/Client 两部分
客户端可以通过$chat xxx发送消息,然后所有连接上 Server 的其它客户端,可以收到 name: xxx 这样的消息
网络编程入门
从这里了解了一下C语言socket编程的一些基础函数,理清楚socket消息传递的基本步骤:
服务器端:建立socket->等待消息->接收消息->发送反馈
客户端:连接socket->发送消息->接受反馈
代码如下:
服务端
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SOCKET_PORT 8088
#define MAXRECVLEN 64
int main(int argc,char *argv[]){
char buf[MAXRECVLEN];
int listened_socket,sendsocket;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t addrlen;
if((listened_socket = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() error\n" );
exit(1);
}
server.sin_family = AF_INET;
server.sin_port = htons(SOCKET_PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listened_socket,(struct sockaddr *)&server,sizeof(server))== -1)
{
printf("bind() error\n" );
exit(1);
}
if(listen(listened_socket,2)==-1){
printf("listen() error\n");
exit(1);
}
addrlen = sizeof(client);
while(1){
if(sendsocket = accept(listened_socket,(struct sockaddr *)&client,&addrlen)==-1){
printf("accept() error\n");
exit(1);
}
while(1){
int iret = recv(sendsocket,buf,MAXRECVLEN,0);
if(iret >0){
printf("%s\n",buf );
}else{
close(sendsocket);
break;
}
send(sendsocket,buf,iret,0);
}
}
close(listened_socket);
return 0;
}
客户端
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SOCKET_PORT 8088
#define MAXDATASIZE 1024
int main(int argc ,char *argv[]){
int socket_send;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in server;
if((socket_send = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() error\n");
exit(1);
}
host = gethostbyname("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons(SOCKET_PORT);
server.sin_addr = *((struct in_addr *)host->h_addr);
if(connect(socket_send,(struct sockaddr *)&server,sizeof(server))==-1){
printf("connect() error\n");
exit(1);
}
int bufsize;
char name[]= "hello\n";
if(send(socket_send,name,sizeof(name),0)==-1)
{
printf("sned() error\n");
exit(1);
}
if((bufsize= recv(socket_send,buf,MAXDATASIZE,0))==-1)
{
printf("recv error\n");
exit(1);
}
buf[bufsize -1]='\0';
printf("server message: %s\n",buf);
close(socket_send);
return 0;
}
上述代码实现了服务端建立socket监听端口,客户端发送消息并接受服务器反馈消息的功能。
但是要完成多个用户共同接受服务器消息就意味着有多个客户端同时监听服务器,因此就涉及到了多线程开发。
具体实现思路是:
客户端:每次客户端访问服务端时,建立一个接受服务端消息的线程,并等待输入新的消息
服务端:在每个客户端第一次访问时,新建一个线程去接受其消息。并维护用户列表,在每次收到消息是发送广播给所有用户。具体的分组发送,筛选等这要控制用户列表就可以了。
客户端
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#define SOCKET_PORT 8088
int socket_send;
void* recv_thread(void* p){
while(1){
int bufsize;
char buf[100]={0};
if((bufsize= recv(socket_send,buf,sizeof(buf),0))==-1)
{
printf("recv error\n");
exit(1);
}
buf[bufsize]='\0';
printf("server message: %s\n",buf);
}
}
int main(int argc ,char *argv[]){
if(argc !=2){
printf("error\n");
}
char name[20];
strncpy(name, argv[1], 20);
struct hostent *host;
struct sockaddr_in server;
if((socket_send = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() error\n");
exit(1);
}
host = gethostbyname("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons(SOCKET_PORT);
server.sin_addr = *((struct in_addr *)host->h_addr);
if(connect(socket_send,(struct sockaddr *)&server,sizeof(server))==-1){
printf("connect() error\n");
exit(1);
}
if(send(socket_send,name,sizeof(name),0)==-1)
{
printf("sned() error\n");
exit(1);
}
pthread_t pid;
pthread_create(&pid,0,recv_thread,0);
while(1){
char buf[100] = {0};
scanf("%s",buf);//接受用户输入
printf("%s",buf);
char msg[100] = {0};
sprintf(msg,"%s说:%s",name,buf);
send(socket_send,msg,strlen(msg),0);//发给服务器
}
close(socket_send);
return 0;
}
服务端
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SOCKET_PORT 8088
#define MAXRECVLEN 20
char buff[MAXRECVLEN];
struct Client
{
char name[20];
int socket;
};
struct Client clients[10]={0};
int client_num=0;
// void sendMsgToAll(char *msg){
// for(int i=0;i<client_num;i++){
// printf("send to%d\n", clients[i].socket);
// send(clients[i].socket,msg,strlen(msg),0);
// }
// }
void* service_thread(void* p){
int socket = *(int*)p;
printf("socket %d\n",socket );
clients[client_num].socket=socket;
char name[10]={0};
if(recv(socket,name,sizeof(name),0)>0){
strcpy(clients[client_num].name,name);
}
client_num++;
for(int i=0;i<client_num;i++){
printf("%s jion\n", clients[i].name);
send(clients[i].socket,name,strlen(name),0);
}
char buff[20];
while(1){
if(recv(socket,buff,sizeof(buff),0)<0){
int i;
char name[10]={0};
for(i=0;i<client_num;i++){
if(clients[i].socket==socket){
strcpy(name,clients[i].name);
clients[i].socket=clients[client_num-1].socket;
strcpy(clients[i].name,clients[client_num-1].name);
}
}
client_num--;
printf("%d quit\n",socket );
for(int i=0;i<client_num;i++){
printf("%s quit\n", clients[i].name);
send(clients[i].socket,name,strlen(name),0);
}
close(socket);
return 0;
}
for(int i=0;i<client_num;i++){
printf("send to%s\n", clients[i].name);
send(clients[i].socket,buff,strlen(buff),0);
}
}
}
int main(int argc,char *argv[]){
int listened_socket,sendsocket;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t addrlen;
if((listened_socket = socket(AF_INET,SOCK_STREAM,0))==-1)
{
printf("socket() error\n" );
exit(1);
}
server.sin_family = AF_INET;
server.sin_port = htons(SOCKET_PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listened_socket,(struct sockaddr *)&server,sizeof(server))== -1)
{
printf("bind() error\n" );
exit(1);
}
if(listen(listened_socket,10)==-1){
printf("listen() error\n");
exit(1);
}
while(1){
addrlen = sizeof(client);
if((sendsocket = accept(listened_socket,(struct sockaddr *)&client,&addrlen))==-1){
printf("accept() error\n");
exit(1);
}
pthread_t pid;
pthread_create(&pid,0,service_thread,&sendsocket);
// while(1){
// int iret = recv(sendsocket,buf,MAXRECVLEN,0);
// if(iret >0){
// printf("%s\n",buf );
// }else{
// close(sendsocket);
// break;
// }
// send(sendsocket,buf,iret,0);
// }
}
close(listened_socket);
return 0;
}
现在只剩下最后一步就是用将其命令行话,并将用户名作为参数
原理很简单就是给运行程序client加个别名!
alias chat ='./client'
运行图然后就可以愉快的和自己聊天了。。。
附上java代码
客户端
package socket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
private boolean connected = false;
private DataOutputStream dataOutputStream = null;
private DataInputStream dataInputStream = null;
public Client() {
// TODO Auto-generated constructor stub
}
public static void main(String args[]){
Client client = new Client();
client.Connect();
client.Listen();
}
private void Connect() {
try {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),8088);
connected = true;
dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream.writeUTF("hello");
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void Listen() {
new Thread(new ListenThread()).start();
}
public class ListenThread implements Runnable {
@Override
public void run() {
while(connected){
String backString;
try {
backString = dataInputStream.readUTF();
System.out.println(backString);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
服务端
package socket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
private List<ClientThread> clients = new ArrayList<ClientThread>();
private ServerSocket serverSocket = null;
private boolean started=false;
public Server(){
try {
serverSocket = new ServerSocket(8088);
started = true;
} catch (IOException e) {
System.out.println("newSocket error\n");
e.printStackTrace();
}
}
public void start(){
Socket socket;
try {
socket = serverSocket.accept();
System.out.println("join\n");
if(socket!=null){
ClientThread client = new ClientThread(socket,true);
clients.add(client);
new Thread(client).start();
}
} catch (IOException e) {
System.out.println("accept error\n");
e.printStackTrace();
}
}
public class ClientThread implements Runnable{
private boolean connected = false;
private Socket socket = null;
private DataInputStream dataInputStream = null;
private DataOutputStream dataOutputStream = null;
public ClientThread(Socket socket,boolean connected) {
this.socket = socket;
this.connected = connected;
}
@Override
public void run() {
InetAddress inetAddress = null;
int port = 8088;
try {
if(connected){
dataInputStream = new DataInputStream(socket.getInputStream());
dataOutputStream = new DataOutputStream(socket.getOutputStream());
inetAddress = socket.getInetAddress();
port = socket.getPort();
}
while (connected) {
String bufString = dataInputStream.readUTF();
if(bufString.equals("exit")){
clients.remove(this);
connected = false;
}
System.out.println("From:"+inetAddress.toString()+":"+port+": "+bufString);
for(int i = 0;i<clients.size();i++){
clients.get(i).dataOutputStream.writeUTF("From:"+inetAddress.toString()+":"+port+": "+bufString);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String args[]){
Server server = new Server();
while (server.started) {
server.start();
}
}
}
网友评论